mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
Merge branch 'feat/support_bluedroid_le_coc_and_eatt_v6.0' into 'release/v6.0'
feat(ble/bluedroid): Support bluedroid LE COC and EATT features (6.0) See merge request espressif/esp-idf!50715
This commit is contained in:
@@ -26,6 +26,9 @@
|
||||
#include "btc_gap_ble.h"
|
||||
#include "btc_iso_ble.h"
|
||||
#include "btc_ble_cte.h"
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
#include "btc_ble_l2cap.h"
|
||||
#endif
|
||||
#include "btc/btc_dm.h"
|
||||
#include "bta/bta_gatt_api.h"
|
||||
#if CLASSIC_BT_INCLUDED
|
||||
@@ -279,6 +282,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = {
|
||||
#if (BLE_FEAT_CTE_EN == TRUE)
|
||||
[BTC_PID_BLE_CTE] = {btc_ble_cte_call_handler, btc_ble_cte_cb_handler },
|
||||
#endif // #if (BLE_FEAT_CTE_EN == TRUE)
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
[BTC_PID_BLE_L2CAP] = {btc_ble_l2cap_call_handler, btc_ble_l2cap_cb_handler },
|
||||
#endif // #if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
@@ -120,6 +120,9 @@ typedef enum {
|
||||
#if (BLE_FEAT_CTE_EN == TRUE)
|
||||
BTC_PID_BLE_CTE,
|
||||
#endif // #if (BLE_FEAT_CTE_EN == TRUE)
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
BTC_PID_BLE_L2CAP,
|
||||
#endif // #if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
BTC_PID_NUM,
|
||||
} btc_pid_t; //btc profile id
|
||||
|
||||
|
||||
@@ -313,6 +313,26 @@ if(CONFIG_BT_BLE_FEAT_ISO_EN)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BLE_L2CAP_COC_ENABLED)
|
||||
list(APPEND bluedroid_host_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stack/l2cap/l2c_ble_le_coc.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/ble_l2cap/btc_ble_l2cap.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/api/esp_ble_l2cap_api.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BLE_L2CAP_ENHANCED_COC)
|
||||
list(APPEND bluedroid_host_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stack/l2cap/l2c_ble_ecfc.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BLE_EATT_ENABLE)
|
||||
list(APPEND bluedroid_host_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stack/gatt/gatt_eatt.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BLE_FEAT_CTE_EN)
|
||||
list(APPEND bluedroid_host_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stack/btm/btm_ble_cte.c"
|
||||
@@ -321,6 +341,12 @@ if(CONFIG_BT_BLE_FEAT_CTE_EN)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND)
|
||||
list(APPEND bluedroid_host_srcs
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stack/btm/btm_ble_pseudo.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
# TODO: Added this file in the ble mesh cmake file
|
||||
if(CONFIG_BLE_MESH)
|
||||
list(APPEND bluedroid_host_srcs "${CMAKE_CURRENT_LIST_DIR}/../../esp_ble_mesh/core/bluedroid_host/adapter.c")
|
||||
|
||||
@@ -482,6 +482,24 @@ config BT_BLE_SMP_BOND_NVS_FLASH
|
||||
help
|
||||
This select can save SMP bonding keys to nvs flash
|
||||
|
||||
config BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
bool "Peripheral dual local-identity bond isolation (pseudo address)"
|
||||
depends on BT_BLE_SMP_ENABLE && BT_BLE_50_EXTEND_ADV_EN
|
||||
default n
|
||||
help
|
||||
Enable Host-internal pseudo address derivation so that one peer phone
|
||||
connecting through two distinct local identities (e.g. Public and a
|
||||
fixed Static Random advertising set) is treated as two independent
|
||||
peers. Each connection gets its own device record, LTK and NVS bond
|
||||
section keyed by pseudo = f(local_identity, peer). The over-the-air
|
||||
and SMP cryptography keep using the real peer and the real local
|
||||
identity; the pseudo address never leaves the Host.
|
||||
|
||||
This is intended for BLE 5.0 Extended Advertising peripherals that need
|
||||
simultaneous dual-identity connections with isolated bonds. Requires
|
||||
BT_BLE_50_EXTEND_ADV_EN. When disabled (default) the stack behaves
|
||||
exactly as before.
|
||||
|
||||
config BT_BLE_RPA_SUPPORTED
|
||||
bool "Update RPA to Controller"
|
||||
depends on (BT_BLE_SMP_ENABLE && ((BT_CONTROLLER_ENABLED && !SOC_BLE_DEVICE_PRIVACY_SUPPORTED) || BT_CONTROLLER_DISABLED)) # NOERROR
|
||||
@@ -1707,6 +1725,87 @@ config BT_BLE_HIGH_DUTY_ADV_INTERVAL
|
||||
help
|
||||
This enable BLE high duty advertising interval feature
|
||||
|
||||
menu "Bluedroid L2CAP CoC"
|
||||
# LE CoC client code is compiled only with GATTC and server code only with
|
||||
# GATTS (see BLE_L2CAP_COC_CLIENT/SERVER_INCLUDED in bt_target.h). Without
|
||||
# either, enabling CoC would silently compile out entirely, so require at
|
||||
# least one GATT role to be enabled.
|
||||
depends on BT_BLE_ENABLED && (BT_GATTC_ENABLE || BT_GATTS_ENABLE)
|
||||
|
||||
config BT_BLE_L2CAP_COC_ENABLED
|
||||
bool "Enable BLE L2CAP Connection Oriented Channels"
|
||||
default n
|
||||
help
|
||||
Enable LE Credit Based Flow Control mode L2CAP CoC in Bluedroid stack.
|
||||
Independent of Classic Bluetooth L2CAP; does not affect esp_bt_l2cap_* APIs.
|
||||
|
||||
config BT_BLE_L2CAP_COC_MAX_CHAN
|
||||
int "Maximum LE CoC channels"
|
||||
depends on BT_BLE_L2CAP_COC_ENABLED
|
||||
range 1 15
|
||||
default 5
|
||||
|
||||
config BT_BLE_L2CAP_COC_MPS
|
||||
int "Default MPS (L2CAP fragment size)"
|
||||
depends on BT_BLE_L2CAP_COC_ENABLED
|
||||
range 23 65533 if !BT_BLE_L2CAP_ENHANCED_COC
|
||||
range 64 65533 if BT_BLE_L2CAP_ENHANCED_COC
|
||||
default 247
|
||||
help
|
||||
Default Maximum PDU Payload Size for LE CoC channels. Legacy LE Credit
|
||||
Based Flow Control allows 23–65533 octets (section 4.22). Enhanced
|
||||
Credit Based Flow Control (ECFC/EATT) requires 64–65533 (section 4.25).
|
||||
When ECFC is enabled the minimum is raised to 64 automatically.
|
||||
|
||||
config BT_BLE_L2CAP_COC_INIT_CREDITS
|
||||
int "Initial RX credit window (K-frames per channel)"
|
||||
depends on BT_BLE_L2CAP_COC_ENABLED
|
||||
range 1 64
|
||||
default 24
|
||||
help
|
||||
Number of LE CoC RX credits granted to the peer when a channel opens.
|
||||
One credit allows the peer to send one K-frame. A larger window can
|
||||
raise sustained throughput but increases how many in-flight frames
|
||||
the peer may send before waiting for more credits (higher RX memory
|
||||
pressure). A smaller window reduces that pressure but can lower
|
||||
throughput. Manual credit mode can stall if a single SDU needs more
|
||||
K-frames than this window; prefer automatic credit mode or a larger
|
||||
MPS when using large MTUs.
|
||||
|
||||
config BT_BLE_L2CAP_ENHANCED_COC
|
||||
bool "Enable Enhanced Credit Based Flow Control (ECFC)"
|
||||
depends on BT_BLE_L2CAP_COC_ENABLED
|
||||
default n
|
||||
help
|
||||
Enable LE Enhanced CoC (L2CAP signaling 0x17/0x18) for multi-channel
|
||||
establishment. Required for EATT multi-bearer support.
|
||||
|
||||
config BT_BLE_EATT_ENABLE
|
||||
bool "Enable Enhanced ATT (EATT)"
|
||||
depends on BT_BLE_L2CAP_COC_ENABLED && BT_BLE_L2CAP_ENHANCED_COC
|
||||
default n
|
||||
help
|
||||
Enable EATT bearers over LE Enhanced CoC (PSM 0x0027).
|
||||
GATT operations may use multiple parallel bearers after link encryption.
|
||||
|
||||
config BT_BLE_EATT_CHAN_NUM
|
||||
int "Number of EATT bearers per connection"
|
||||
depends on BT_BLE_EATT_ENABLE
|
||||
range 1 BT_BLE_L2CAP_COC_MAX_CHAN
|
||||
default 3
|
||||
help
|
||||
Number of parallel EATT bearers established per connection. Must not
|
||||
exceed the maximum LE CoC channels, since EATT bearers are LE CoC
|
||||
channels; the range is capped by BT_BLE_L2CAP_COC_MAX_CHAN.
|
||||
|
||||
config BT_BLE_EATT_MTU
|
||||
int "EATT bearer MTU"
|
||||
depends on BT_BLE_EATT_ENABLE
|
||||
range 64 517
|
||||
default 247
|
||||
|
||||
endmenu
|
||||
|
||||
config BT_ABORT_WHEN_ALLOCATION_FAILS
|
||||
bool "Abort when memory allocation fails in BT/BLE stack"
|
||||
depends on BT_BLUEDROID_ENABLED
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_ble_l2cap_api.h"
|
||||
#include "btc/btc_manage.h"
|
||||
#include "btc/btc_task.h"
|
||||
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
#include "common/bt_target.h"
|
||||
#include "btc_ble_l2cap.h"
|
||||
|
||||
/* LE PSM valid range per Core Spec: 0x0001..0x00FF */
|
||||
#define ESP_BLE_L2CAP_IS_VALID_LE_PSM(psm) ((psm) > 0x0000 && (psm) < 0x0100)
|
||||
|
||||
/* Minimum MTU per Core Spec Vol 3 Part A: 23 for LE credit based (4.22),
|
||||
* 64 for enhanced credit based / ECFC (4.25). */
|
||||
#define ESP_BLE_L2CAP_LE_MIN_MTU 23
|
||||
#define ESP_BLE_L2CAP_ECFC_MIN_MTU 64
|
||||
/* Minimum MPS for enhanced credit based / ECFC channels (Core Spec Vol 3
|
||||
* Part A 4.25). */
|
||||
#define ESP_BLE_L2CAP_ECFC_MIN_MPS 64
|
||||
/* Core Spec Vol 3 Part A 4.25/4.27: a single enhanced credit based connection
|
||||
* or reconfiguration request may target at most five channels, regardless of
|
||||
* the (pool-sized) BT_BLE_L2CAP_COC_MAX_CHAN Kconfig value. */
|
||||
#define ESP_BLE_L2CAP_ECFC_MAX_REQ_CHANS 5
|
||||
|
||||
static esp_err_t btc_ble_l2cap_transfer(btc_ble_l2cap_act_t act, btc_ble_l2cap_args_t *arg)
|
||||
{
|
||||
btc_msg_t msg = {0};
|
||||
|
||||
msg.sig = BTC_SIG_API_CALL;
|
||||
msg.pid = BTC_PID_BLE_L2CAP;
|
||||
msg.act = act;
|
||||
|
||||
return (btc_transfer_context(&msg, arg, sizeof(btc_ble_l2cap_args_t),
|
||||
btc_ble_l2cap_arg_deep_copy,
|
||||
btc_ble_l2cap_arg_deep_free) == BT_STATUS_SUCCESS)
|
||||
? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_register_callback(esp_ble_l2cap_cb_t callback)
|
||||
{
|
||||
if (callback == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
return (btc_profile_cb_set(BTC_PID_BLE_L2CAP, callback) == 0) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_init(void)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_INIT, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_deinit(void)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_DEINIT, &arg);
|
||||
}
|
||||
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED == TRUE)
|
||||
esp_err_t esp_ble_l2cap_create_server(uint16_t psm, uint16_t mtu)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (psm == 0 || mtu < ESP_BLE_L2CAP_LE_MIN_MTU || !ESP_BLE_L2CAP_IS_VALID_LE_PSM(psm)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.create_server.psm = psm;
|
||||
arg.create_server.mtu = mtu;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_CREATE_SERVER, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_delete_server(uint16_t psm)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (psm == 0 || !ESP_BLE_L2CAP_IS_VALID_LE_PSM(psm)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.delete_server.psm = psm;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_DELETE_SERVER, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_accept(uint16_t conn_id, uint8_t l2cap_id,
|
||||
uint16_t chan_handle, bool accept, uint16_t mtu)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handle == 0 || (accept && mtu < ESP_BLE_L2CAP_LE_MIN_MTU)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.accept.conn_id = conn_id;
|
||||
arg.accept.l2cap_id = l2cap_id;
|
||||
arg.accept.chan_handle = chan_handle;
|
||||
arg.accept.accept = accept;
|
||||
arg.accept.mtu = mtu;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_ACCEPT, &arg);
|
||||
}
|
||||
#endif /* BLE_L2CAP_COC_SERVER_INCLUDED */
|
||||
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
esp_err_t esp_ble_l2cap_connect(uint16_t conn_id, uint16_t psm, uint16_t mtu)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (psm == 0 || mtu < ESP_BLE_L2CAP_LE_MIN_MTU || !ESP_BLE_L2CAP_IS_VALID_LE_PSM(psm)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.connect.conn_id = conn_id;
|
||||
arg.connect.psm = psm;
|
||||
arg.connect.mtu = mtu;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_CONNECT, &arg);
|
||||
}
|
||||
#endif /* BLE_L2CAP_COC_CLIENT_INCLUDED */
|
||||
|
||||
esp_err_t esp_ble_l2cap_disconnect(uint16_t chan_handle)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handle == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.disconnect.chan_handle = chan_handle;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_DISCONNECT, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_send(uint16_t chan_handle, uint8_t *data, uint16_t len)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handle == 0 || data == NULL || len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.send.chan_handle = chan_handle;
|
||||
arg.send.len = len;
|
||||
arg.send.data = data;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_SEND, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_recv_ready(uint16_t chan_handle)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handle == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.recv_ready.chan_handle = chan_handle;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_RECV_READY, &arg);
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_l2cap_set_auto_credit(uint16_t chan_handle, bool enable)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handle == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.set_auto_credit.chan_handle = chan_handle;
|
||||
arg.set_auto_credit.enable = enable;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_SET_AUTO_CREDIT, &arg);
|
||||
}
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
esp_err_t esp_ble_l2cap_connect_ecoc(uint16_t conn_id, uint16_t psm, uint16_t mtu, uint8_t num_chan)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (psm == 0 || mtu < ESP_BLE_L2CAP_ECFC_MIN_MTU || num_chan == 0 ||
|
||||
num_chan > BLE_MAX_L2CAP_CLIENTS ||
|
||||
num_chan > ESP_BLE_L2CAP_ECFC_MAX_REQ_CHANS || !ESP_BLE_L2CAP_IS_VALID_LE_PSM(psm)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.connect_ecoc.conn_id = conn_id;
|
||||
arg.connect_ecoc.psm = psm;
|
||||
arg.connect_ecoc.mtu = mtu;
|
||||
arg.connect_ecoc.num_chan = num_chan;
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_CONNECT_ECOC, &arg);
|
||||
}
|
||||
#endif /* BLE_L2CAP_COC_CLIENT_INCLUDED */
|
||||
|
||||
esp_err_t esp_ble_l2cap_reconfig(uint16_t *chan_handles, uint8_t num_chan, uint16_t mtu, uint16_t mps)
|
||||
{
|
||||
btc_ble_l2cap_args_t arg = {0};
|
||||
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (chan_handles == NULL || num_chan == 0 || num_chan > BLE_MAX_L2CAP_CLIENTS ||
|
||||
num_chan > ESP_BLE_L2CAP_ECFC_MAX_REQ_CHANS ||
|
||||
mtu < ESP_BLE_L2CAP_ECFC_MIN_MTU || mps < ESP_BLE_L2CAP_ECFC_MIN_MPS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
arg.reconfig.num_chan = num_chan;
|
||||
arg.reconfig.mtu = mtu;
|
||||
arg.reconfig.mps = mps;
|
||||
memcpy(arg.reconfig.chan_handles, chan_handles, num_chan * sizeof(uint16_t));
|
||||
return btc_ble_l2cap_transfer(BTC_BLE_L2CAP_ACT_RECONFIG, &arg);
|
||||
}
|
||||
#endif /* BLE_L2CAP_ENHANCED_COC_INCLUDED */
|
||||
|
||||
#endif /* BLE_L2CAP_COC_INCLUDED == TRUE */
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "btc_gap_ble.h"
|
||||
#include "btc/btc_ble_storage.h"
|
||||
#include "esp_random.h"
|
||||
#include "common/bt_target.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "stack/gatt_api.h"
|
||||
#endif
|
||||
|
||||
/* Hard upper bound to prevent excessive allocations in BTC/BTA layers. */
|
||||
#define ESP_GAP_BLE_EXT_ADV_DATA_MAX_LEN 1650U
|
||||
@@ -512,6 +516,59 @@ esp_err_t esp_ble_gap_get_local_used_addr(esp_bd_addr_t local_used_addr, uint8_t
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if (CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND)
|
||||
esp_err_t esp_ble_gap_get_real_peer_addr(esp_bd_addr_t pseudo, esp_bd_addr_t real_peer)
|
||||
{
|
||||
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
|
||||
LOG_ERROR("%s, bluedroid status error", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (pseudo == NULL || real_peer == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!BTM_BleGetRealPeerByPseudo(pseudo, real_peer)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_gap_get_conn_identity(esp_bd_addr_t pseudo, esp_ble_conn_identity_t *identity)
|
||||
{
|
||||
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
|
||||
LOG_ERROR("%s, bluedroid status error", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (pseudo == NULL || identity == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
UINT8 peer_type = 0, local_type = 0;
|
||||
if (!BTM_BleGetConnIdentityByPseudo(pseudo, identity->peer_addr, identity->local_addr,
|
||||
&peer_type, &local_type)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
identity->peer_addr_type = peer_type;
|
||||
identity->local_addr_type = local_type;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_gap_remove_bond_for_identity(esp_bd_addr_t local_addr,
|
||||
esp_ble_addr_type_t local_addr_type,
|
||||
esp_bd_addr_t peer_addr,
|
||||
esp_ble_addr_type_t peer_addr_type)
|
||||
{
|
||||
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
|
||||
LOG_ERROR("%s, bluedroid status error", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (local_addr == NULL || peer_addr == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_bd_addr_t pseudo;
|
||||
BTM_BleComputePseudoForIdentity(local_addr, local_addr_type, peer_addr, peer_addr_type, pseudo);
|
||||
return esp_ble_remove_bond_device(pseudo);
|
||||
}
|
||||
#endif // CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
#if ((BLE_42_SCAN_EN == TRUE) || (BLE_50_EXTEND_SCAN_EN == TRUE))
|
||||
uint8_t *esp_ble_resolve_adv_data_by_type( uint8_t *adv_data, uint16_t adv_data_len, esp_ble_adv_data_type type, uint8_t *length)
|
||||
{
|
||||
@@ -3226,3 +3283,36 @@ esp_err_t esp_ble_cs_procedure_enable(esp_ble_cs_procedure_enable_params *proced
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Intentionally synchronous: updates the pre-connection EATT bearer count only.
|
||||
* Must be called before the link is encrypted / bearers are established (see API
|
||||
* doc). No btc_transfer_context dispatch — this is a setup-time config write, not
|
||||
* an async stack procedure, and callers need immediate ESP_ERR_INVALID_ARG feedback. */
|
||||
esp_err_t esp_ble_eatt_set_chan_num(uint8_t num_chan)
|
||||
{
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (num_chan == 0 || num_chan > GATT_EATT_MAX_CHAN) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
GATT_EattSetChanNum(num_chan);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Intentionally synchronous: sets the preferred EATT bearer (ec->default_lcid) for
|
||||
* subsequent GATT client TX routing on this connection. No btc_transfer_context
|
||||
* dispatch — by design this is an immediate preference update with synchronous
|
||||
* validation (invalid conn_id/cid returns ESP_ERR_INVALID_ARG at call time).
|
||||
* Client-only: defined solely when the EATT client role is built in, so a build
|
||||
* without it fails at link time rather than exposing a stub. */
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
esp_err_t esp_ble_eatt_set_default_bearer(uint16_t conn_id, uint16_t cid)
|
||||
{
|
||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||
if (!GATT_EattSetDefaultBearer(conn_id, cid)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* BLE_EATT_CLIENT_INCLUDED */
|
||||
#endif /* BLE_EATT_INCLUDED */
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __ESP_BLE_L2CAP_API_H__
|
||||
#define __ESP_BLE_L2CAP_API_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_bt_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LE L2CAP connection-oriented channel (CoC) callback events
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_BLE_L2CAP_COC_CONNECTED_EVT = 0, /*!< When an LE CoC channel is connected or the connection attempt fails, the event comes */
|
||||
ESP_BLE_L2CAP_COC_DISCONNECTED_EVT, /*!< When an LE CoC channel is disconnected, the event comes */
|
||||
ESP_BLE_L2CAP_COC_ACCEPT_EVT, /*!< When a remote device requests a new LE CoC connection to a local server, the event comes */
|
||||
ESP_BLE_L2CAP_COC_DATA_RECEIVED_EVT, /*!< When a complete SDU is received on an LE CoC channel, the event comes */
|
||||
ESP_BLE_L2CAP_COC_TX_UNSTALLED_EVT, /*!< When TX credits are restored and more data may be sent, the event comes */
|
||||
ESP_BLE_L2CAP_COC_RECONFIG_COMPLETED_EVT, /*!< When a local channel reconfiguration request completes, the event comes */
|
||||
ESP_BLE_L2CAP_COC_PEER_RECONFIGURED_EVT, /*!< When the peer completes a channel reconfiguration, the event comes */
|
||||
ESP_BLE_L2CAP_COC_EVT_MAX,
|
||||
} esp_ble_l2cap_evt_t;
|
||||
|
||||
/**
|
||||
* @brief LE CoC channel information
|
||||
*
|
||||
* Delivered in `ESP_BLE_L2CAP_COC_CONNECTED_EVT` and reconfiguration events when the
|
||||
* operation succeeds.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t scid; /*!< Local channel identifier (CID) */
|
||||
uint16_t dcid; /*!< Remote channel identifier (CID) */
|
||||
uint16_t psm; /*!< Protocol/Service Multiplexer */
|
||||
uint16_t our_mtu; /*!< Local maximum SDU size (MTU) */
|
||||
uint16_t peer_mtu; /*!< Peer maximum SDU size (MTU) */
|
||||
uint16_t our_mps; /*!< Local maximum PDU payload size (MPS) */
|
||||
uint16_t peer_mps; /*!< Peer maximum PDU payload size (MPS) */
|
||||
} esp_ble_l2cap_chan_info_t;
|
||||
|
||||
/**
|
||||
* @brief LE L2CAP CoC callback parameters union
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_CONNECTED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t conn_id; /*!< GATT connection id of the underlying ACL link. May be 0 if not yet bound */
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) of the CoC */
|
||||
uint16_t status; /*!< Connection result. 0 (`L2CAP_CONN_OK`) means success; other values are L2CAP connection result codes */
|
||||
esp_ble_l2cap_chan_info_t chan_info; /*!< Channel information. Valid only when `status` is 0 (`L2CAP_CONN_OK`) */
|
||||
} coc_connected; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_CONNECTED_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_DISCONNECTED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t conn_id; /*!< Reserved. Currently not populated by the stack (0) */
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) of the disconnected CoC */
|
||||
} coc_disconnected; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_DISCONNECTED_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_ACCEPT_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t conn_id; /*!< GATT connection id of the underlying ACL link. May be 0 if not yet bound */
|
||||
uint16_t chan_handle; /*!< Proposed local L2CAP channel identifier (CID) */
|
||||
uint8_t l2cap_id; /*!< L2CAP signaling identifier of the connection request */
|
||||
uint16_t psm; /*!< Protocol/Service Multiplexer requested by the peer */
|
||||
} coc_accept; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_ACCEPT_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_DATA_RECEIVED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) that received the SDU */
|
||||
uint16_t len; /*!< SDU length in bytes */
|
||||
uint8_t *data; /*!< Pointer to the received SDU payload. Valid only during the callback */
|
||||
} data_received; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_DATA_RECEIVED_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_TX_UNSTALLED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) whose TX path is no longer congested */
|
||||
} tx_unstalled; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_TX_UNSTALLED_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_RECONFIG_COMPLETED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) that was reconfigured */
|
||||
uint16_t status; /*!< Reconfiguration result. 0 (`L2CAP_LE_RECONFIG_OK`) means success */
|
||||
esp_ble_l2cap_chan_info_t chan_info; /*!< Updated channel information. Valid only when `status` is 0 (`L2CAP_LE_RECONFIG_OK`) */
|
||||
} reconfig_completed; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_RECONFIG_COMPLETED_EVT */
|
||||
|
||||
/**
|
||||
* @brief ESP_BLE_L2CAP_COC_PEER_RECONFIGURED_EVT
|
||||
*/
|
||||
struct {
|
||||
uint16_t chan_handle; /*!< Local L2CAP channel identifier (CID) reconfigured by the peer */
|
||||
uint16_t status; /*!< Reconfiguration result. 0 (`L2CAP_LE_RECONFIG_OK`) means success */
|
||||
esp_ble_l2cap_chan_info_t chan_info; /*!< Updated channel information. Valid only when `status` is 0 (`L2CAP_LE_RECONFIG_OK`) */
|
||||
} peer_reconfigured; /*!< LE L2CAP callback param of ESP_BLE_L2CAP_COC_PEER_RECONFIGURED_EVT */
|
||||
} esp_ble_l2cap_cb_param_t;
|
||||
|
||||
/**
|
||||
* @brief LE L2CAP CoC callback function type
|
||||
*
|
||||
* @param[in] event: Event type
|
||||
* @param[in] param: Pointer to callback parameter, currently is union type
|
||||
*/
|
||||
typedef void (*esp_ble_l2cap_cb_t)(esp_ble_l2cap_evt_t event, esp_ble_l2cap_cb_param_t *param);
|
||||
|
||||
/**
|
||||
* @brief Register the LE L2CAP CoC callback function
|
||||
*
|
||||
* @param[in] callback: Pointer to the callback function
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: callback is NULL
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_register_callback(esp_ble_l2cap_cb_t callback);
|
||||
|
||||
/**
|
||||
* @brief Initialize the LE L2CAP CoC module
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_L2CAP_COC_ENABLED`.
|
||||
* This function should be called after `esp_bluedroid_enable()` completes successfully.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the LE L2CAP CoC module
|
||||
*
|
||||
* Deregisters all local CoC servers created by this module.
|
||||
* This function should be called after `esp_ble_l2cap_init()` completes successfully.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Register a local LE CoC server on the given PSM
|
||||
*
|
||||
* When a remote device requests a connection to this PSM, the callback receives
|
||||
* `ESP_BLE_L2CAP_COC_ACCEPT_EVT`.
|
||||
*
|
||||
* @param[in] psm: LE Protocol/Service Multiplexer. Valid range is 0x0001 to 0x00FF
|
||||
* @param[in] mtu: Local maximum SDU size (MTU) for channels accepted on this PSM
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid `psm` or `mtu`
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_create_server(uint16_t psm, uint16_t mtu);
|
||||
|
||||
/**
|
||||
* @brief Deregister a local LE CoC server
|
||||
*
|
||||
* @param[in] psm: LE Protocol/Service Multiplexer previously registered with `esp_ble_l2cap_create_server()`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `psm` is 0
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_delete_server(uint16_t psm);
|
||||
|
||||
/**
|
||||
* @brief Connect to a remote LE CoC server (client role)
|
||||
*
|
||||
* When the connection attempt completes, the callback receives
|
||||
* `ESP_BLE_L2CAP_COC_CONNECTED_EVT`.
|
||||
*
|
||||
* @param[in] conn_id: GATT connection id of the underlying ACL link
|
||||
* @param[in] psm: Remote LE Protocol/Service Multiplexer. Valid range is 0x0001 to 0x00FF
|
||||
* @param[in] mtu: Local maximum SDU size (MTU) to propose for the channel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid `psm` or `mtu`
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_connect(uint16_t conn_id, uint16_t psm, uint16_t mtu);
|
||||
|
||||
/**
|
||||
* @brief Accept or reject an inbound LE CoC connection request (server role)
|
||||
*
|
||||
* Call this function in response to `ESP_BLE_L2CAP_COC_ACCEPT_EVT`.
|
||||
* When accepted, the callback receives `ESP_BLE_L2CAP_COC_CONNECTED_EVT`.
|
||||
* When rejected, no `ESP_BLE_L2CAP_COC_CONNECTED_EVT` is reported to the local server.
|
||||
*
|
||||
* @param[in] conn_id: GATT connection id from `ESP_BLE_L2CAP_COC_ACCEPT_EVT`
|
||||
* @param[in] l2cap_id: L2CAP signaling identifier from `ESP_BLE_L2CAP_COC_ACCEPT_EVT`
|
||||
* @param[in] chan_handle: Proposed local channel identifier from `ESP_BLE_L2CAP_COC_ACCEPT_EVT`
|
||||
* @param[in] accept: True to accept the connection; false to reject it
|
||||
* @param[in] mtu: Local maximum SDU size (MTU) to use when accepting. Ignored when rejecting
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `chan_handle` is 0
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_accept(uint16_t conn_id, uint8_t l2cap_id,
|
||||
uint16_t chan_handle, bool accept, uint16_t mtu);
|
||||
|
||||
/**
|
||||
* @brief Disconnect an LE CoC channel
|
||||
*
|
||||
* When the channel is closed, the callback receives `ESP_BLE_L2CAP_COC_DISCONNECTED_EVT`.
|
||||
*
|
||||
* @param[in] chan_handle: Local L2CAP channel identifier (CID) of the CoC to disconnect
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `chan_handle` is 0
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_disconnect(uint16_t chan_handle);
|
||||
|
||||
/**
|
||||
* @brief Send an SDU on an LE CoC channel
|
||||
*
|
||||
* Transmission is credit-based. The host accepts at most one SDU per
|
||||
* channel in its TX queue; further calls return `ESP_OK` but the SDU
|
||||
* may be dropped if the channel is busy. Retry on
|
||||
* `ESP_BLE_L2CAP_COC_TX_UNSTALLED_EVT` or after the pipeline drains.
|
||||
*
|
||||
* This function returns `ESP_OK` when the send request is queued to the host stack.
|
||||
* It does not indicate that the SDU has already been transmitted.
|
||||
*
|
||||
* @param[in] chan_handle: Local L2CAP channel identifier (CID)
|
||||
* @param[in] data: Pointer to the SDU payload to send
|
||||
* @param[in] len: SDU length in bytes
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid argument
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_send(uint16_t chan_handle, uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief Return RX credits after processing a received SDU (manual credit mode)
|
||||
*
|
||||
* Call this function once after the application has finished handling the SDU delivered
|
||||
* in `ESP_BLE_L2CAP_COC_DATA_RECEIVED_EVT`. The stack returns the exact number of RX
|
||||
* credits that SDU consumed (a multi-frame SDU consumes more than one), so no credits
|
||||
* are leaked regardless of how the SDU was fragmented.
|
||||
*
|
||||
* This call only has an effect when the channel is in manual credit mode
|
||||
* (`esp_ble_l2cap_set_auto_credit(chan_handle, false)`). In the default automatic mode
|
||||
* the stack returns credits itself and this call is a harmless no-op. See
|
||||
* `esp_ble_l2cap_set_auto_credit()` for the trade-offs between the two modes.
|
||||
*
|
||||
* @param[in] chan_handle: Local L2CAP channel identifier (CID)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `chan_handle` is 0
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_recv_ready(uint16_t chan_handle);
|
||||
|
||||
/**
|
||||
* @brief Connect multiple LE CoC channels in one Enhanced Credit Flow Control request (client role)
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_L2CAP_ENHANCED_COC`. The corresponding API symbols are
|
||||
* available only when this option is enabled at build time.
|
||||
* One `ESP_BLE_L2CAP_COC_CONNECTED_EVT` is reported per channel.
|
||||
*
|
||||
* @param[in] conn_id: GATT connection id of the underlying ACL link
|
||||
* @param[in] psm: Remote LE Protocol/Service Multiplexer. Valid range is 0x0001 to 0x00FF
|
||||
* @param[in] mtu: Local maximum SDU size (MTU) to propose for each channel
|
||||
* @param[in] num_chan: Number of CoC channels to open in a single request
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid argument
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_connect_ecoc(uint16_t conn_id, uint16_t psm, uint16_t mtu, uint8_t num_chan);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure MTU and/or MPS on one or more LE CoC channels
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_L2CAP_ENHANCED_COC`. The corresponding API symbols are
|
||||
* available only when this option is enabled at build time.
|
||||
* When the local request completes, the callback receives
|
||||
* `ESP_BLE_L2CAP_COC_RECONFIG_COMPLETED_EVT` per channel.
|
||||
*
|
||||
* @param[in] chan_handles: Array of local L2CAP channel identifiers (CIDs) to reconfigure
|
||||
* @param[in] num_chan: Number of entries in `chan_handles`
|
||||
* @param[in] mtu: New local maximum SDU size (MTU)
|
||||
* @param[in] mps: New local maximum PDU payload size (MPS)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid argument
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_reconfig(uint16_t *chan_handles, uint8_t num_chan, uint16_t mtu, uint16_t mps);
|
||||
|
||||
/**
|
||||
* @brief Select the RX credit return policy for an LE CoC channel
|
||||
*
|
||||
* LE CoC flow control is credit based: one credit == one K-frame (an L2CAP PDU of
|
||||
* up to MPS bytes). A single application SDU (up to MTU bytes) may be fragmented into
|
||||
* several K-frames, so it consumes several RX credits. This function chooses how those
|
||||
* consumed credits are returned to the peer.
|
||||
*
|
||||
* Automatic mode (enable = true, the default):
|
||||
* - Behaviour: the stack returns credits itself as each K-frame is consumed (returns
|
||||
* are batched for efficiency and flushed as the window drains). In this mode
|
||||
* `esp_ble_l2cap_recv_ready()` is a no-op and does not need to be called.
|
||||
* - Pros: highest sustained RX throughput (credits are replenished on the Bluetooth
|
||||
* task with no application round trip); no per-SDU bookkeeping for the application;
|
||||
* works for any MTU/MPS, including SDUs larger than the credit window (credits are
|
||||
* returned mid-SDU so reassembly can always complete).
|
||||
* - Cons: no application-level backpressure. The peer keeps sending as fast as the
|
||||
* credit window allows, regardless of how quickly the application drains the data.
|
||||
* Recommended for throughput-oriented use and as the general default.
|
||||
*
|
||||
* Manual mode (enable = false):
|
||||
* - Behaviour: the stack withholds the consumed credits; the application returns them
|
||||
* by calling `esp_ble_l2cap_recv_ready()` once after it has finished processing each
|
||||
* SDU delivered in `ESP_BLE_L2CAP_COC_DATA_RECEIVED_EVT`. The stack tracks the exact
|
||||
* number of K-frames each SDU consumed and returns that many credits per call, so a
|
||||
* multi-frame SDU does not leak credits.
|
||||
* - Pros: application-level backpressure. The peer's flow is gated by the application's
|
||||
* processing pace (if `recv_ready()` is not called, the peer stalls once its credits
|
||||
* run out), which is useful when the receiver has limited buffering.
|
||||
* - Cons: lower sustained throughput than automatic mode, because each replenishment
|
||||
* incurs an application-to-stack round trip.
|
||||
*
|
||||
* Possible problem in manual mode (large SDUs):
|
||||
* - Because credits are returned only after a complete SDU is delivered, a single SDU
|
||||
* whose K-frame count exceeds the whole RX credit window (roughly when
|
||||
* ceil((MTU + 2) / MPS) > window) can stall: the peer exhausts its credits before the
|
||||
* SDU is complete, so the application never receives the event and never calls
|
||||
* `recv_ready()`. The stack contains a deadlock breaker that returns the withheld
|
||||
* credits mid-SDU in this situation so the transfer still completes (at the cost of
|
||||
* weaker backpressure for that oversized SDU), and it logs a warning when manual mode
|
||||
* is enabled on a channel where this can happen. For large MTUs prefer automatic mode
|
||||
* or negotiate a larger MPS so a single SDU fits within the credit window.
|
||||
*
|
||||
* @param[in] chan_handle: Local L2CAP channel identifier (CID)
|
||||
* @param[in] enable: True to enable automatic credit return (default); false for manual
|
||||
* return via `esp_ble_l2cap_recv_ready()`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `chan_handle` is 0
|
||||
* - ESP_FAIL: other error
|
||||
*/
|
||||
esp_err_t esp_ble_l2cap_set_auto_credit(uint16_t chan_handle, bool enable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_BLE_L2CAP_API_H__ */
|
||||
@@ -287,6 +287,7 @@ typedef enum {
|
||||
ESP_GAP_BLE_UTP_RECEIVE_EVT, /*!< When UTP data is received, the event comes */
|
||||
ESP_GAP_BLE_CS_SET_SECURITY_REQUIREMENTS_CMPL_EVT, /*!< When CS set security requirements complete, the event comes */
|
||||
ESP_GAP_BLE_CS_SET_DEFAULT_SECURITY_REQUIREMENTS_CMPL_EVT, /*!< When CS set default security requirements complete, the event comes */
|
||||
ESP_GAP_BLE_EATT_EVT, /*!< When an EATT bearer is connected or disconnected, the event comes. Requires `CONFIG_BT_BLE_EATT_ENABLE` */
|
||||
ESP_GAP_BLE_EVT_MAX, /*!< when maximum advertising event complete, the event comes */
|
||||
} esp_gap_ble_cb_event_t;
|
||||
|
||||
@@ -3228,6 +3229,18 @@ typedef union {
|
||||
esp_ble_cs_step_info *step_info; /*!< steps information in the CS subevent */
|
||||
} cs_subevt_result_continue; /*!< Event parameter of ESP_GAP_BLE_CS_SUBEVENT_RESULT_CONTINUE_EVT */
|
||||
#endif // (BT_BLE_FEAT_CHANNEL_SOUNDING == TRUE)
|
||||
|
||||
/**
|
||||
* @brief ESP_GAP_BLE_EATT_EVT
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_EATT_ENABLE`. EATT bearers are established automatically
|
||||
* after the ACL link is encrypted.
|
||||
*/
|
||||
struct ble_eatt_evt {
|
||||
uint16_t conn_id; /*!< GATT connection id of the underlying ACL link. 0xFFFF (GATT_INVALID_CONN_ID) if not yet available. Note: 0 is a valid conn_id (the first BLE connection) */
|
||||
uint8_t status; /*!< EATT bearer status. 0: connected; 1: disconnected */
|
||||
uint16_t cid; /*!< Local L2CAP channel identifier (CID) of the EATT bearer */
|
||||
} eatt_evt; /*!< Event parameter of ESP_GAP_BLE_EATT_EVT */
|
||||
} esp_ble_gap_cb_param_t;
|
||||
|
||||
/**
|
||||
@@ -3597,6 +3610,94 @@ esp_err_t esp_ble_gap_set_key_material(const uint8_t session_key[16], const uint
|
||||
*/
|
||||
esp_err_t esp_ble_gap_get_local_used_addr(esp_bd_addr_t local_used_addr, uint8_t * addr_type);
|
||||
|
||||
#if (CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND)
|
||||
/**
|
||||
* @brief Reverse-map a Host pseudo address to the real peer identity.
|
||||
*
|
||||
* When CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND is enabled, the
|
||||
* remote_bda reported to the application for a dual local
|
||||
* identity link is a Host-internal pseudo address (one peer
|
||||
* phone connected through two local identities shows up as two
|
||||
* different pseudo addresses). This helper returns the actual
|
||||
* over-the-air peer identity for UI / diagnostics.
|
||||
*
|
||||
* **Must be called while the link is connected.** The mapping
|
||||
* lives in a Host-side connection table that is cleared on
|
||||
* disconnect. If there is no active link for `pseudo`, the call
|
||||
* returns `ESP_FAIL` and `real_peer` is not modified.
|
||||
*
|
||||
* For offline bond information, use
|
||||
* `esp_ble_get_bond_device_list()` and read
|
||||
* `bond_key.pid_key.static_addr` for the real peer identity.
|
||||
*
|
||||
* @param[in] pseudo - the pseudo address as seen in remote_bda
|
||||
* @param[out] real_peer - filled with the real peer identity on success
|
||||
*
|
||||
* @return - ESP_OK : success (link connected and pseudo known)
|
||||
* - ESP_FAIL : Bluedroid not enabled, or pseudo not found /
|
||||
* not connected
|
||||
* - ESP_ERR_INVALID_ARG : NULL pointer argument
|
||||
*/
|
||||
esp_err_t esp_ble_gap_get_real_peer_addr(esp_bd_addr_t pseudo, esp_bd_addr_t real_peer);
|
||||
|
||||
/**
|
||||
* @brief Full identity of a dual local-identity connection.
|
||||
*/
|
||||
typedef struct {
|
||||
esp_bd_addr_t peer_addr; /*!< real over-the-air peer identity */
|
||||
esp_bd_addr_t local_addr; /*!< local identity used for this link */
|
||||
esp_ble_addr_type_t peer_addr_type; /*!< peer identity address type */
|
||||
esp_ble_addr_type_t local_addr_type; /*!< local identity address type */
|
||||
} esp_ble_conn_identity_t;
|
||||
|
||||
/**
|
||||
* @brief Get the full (peer, local) identity of a dual local-identity
|
||||
* link, keyed by the pseudo address the application sees as
|
||||
* remote_bda.
|
||||
*
|
||||
* **Must be called while the link is connected.** The mapping
|
||||
* is kept in a Host-side connection table that is registered at
|
||||
* connection complete and cleared on disconnect. If there is no
|
||||
* active link for `pseudo`, or the local identity is not yet
|
||||
* finalized (`local_ready`), the call returns `ESP_FAIL` and
|
||||
* `identity` is not modified.
|
||||
*
|
||||
* For offline bond information (no connection), use
|
||||
* `esp_ble_get_bond_device_list()` and read
|
||||
* `bond_key.pid_key.static_addr` for the real peer identity.
|
||||
* The bond list key is the stored pseudo address; local identity
|
||||
* is not exposed by this API offline.
|
||||
*
|
||||
* @param[in] pseudo - the pseudo address as seen in remote_bda
|
||||
* @param[out] identity - filled with the peer/local identity on success
|
||||
*
|
||||
* @return - ESP_OK : success (link connected and pseudo known)
|
||||
* - ESP_FAIL : Bluedroid not enabled, or pseudo not found /
|
||||
* not connected / local identity not yet ready
|
||||
* - ESP_ERR_INVALID_ARG : NULL pointer argument
|
||||
*/
|
||||
esp_err_t esp_ble_gap_get_conn_identity(esp_bd_addr_t pseudo, esp_ble_conn_identity_t *identity);
|
||||
|
||||
/**
|
||||
* @brief Remove the stored bond for one specific (local, peer)
|
||||
* identity pair. The pseudo bond section is recomputed from the
|
||||
* identity, so this only deletes that one local identity's bond
|
||||
* and never affects the same phone's other local identity.
|
||||
*
|
||||
* @param[in] local_addr - local identity used when bonding
|
||||
* @param[in] local_addr_type - local identity address type
|
||||
* @param[in] peer_addr - real peer identity
|
||||
* @param[in] peer_addr_type - peer identity address type
|
||||
*
|
||||
* @return - ESP_OK : request accepted
|
||||
* - other : invalid arguments / not enabled
|
||||
*/
|
||||
esp_err_t esp_ble_gap_remove_bond_for_identity(esp_bd_addr_t local_addr,
|
||||
esp_ble_addr_type_t local_addr_type,
|
||||
esp_bd_addr_t peer_addr,
|
||||
esp_ble_addr_type_t peer_addr_type);
|
||||
#endif // CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
|
||||
/**
|
||||
* @brief This function is called to get ADV data for a specific type.
|
||||
*
|
||||
@@ -5167,6 +5268,53 @@ esp_err_t esp_ble_cs_set_procedure_params(esp_ble_cs_set_proc_params *procedure_
|
||||
*/
|
||||
esp_err_t esp_ble_cs_procedure_enable(esp_ble_cs_procedure_enable_params *procedure_enable_params);
|
||||
|
||||
/**
|
||||
* @brief Set the number of EATT bearers to establish per connection
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_EATT_ENABLE`.
|
||||
* EATT bearers are created automatically after the link is encrypted.
|
||||
* Call this function before the bearers are established. The value must
|
||||
* not exceed `CONFIG_BT_BLE_EATT_CHAN_NUM` (compile-time maximum).
|
||||
*
|
||||
* This API is intentionally synchronous (does not dispatch through the
|
||||
* BTC task): it only stores the requested bearer count for future
|
||||
* connections and returns validation errors immediately.
|
||||
*
|
||||
* @param[in] num_chan: Number of EATT bearers to establish per connection
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: `num_chan` is 0 or greater than
|
||||
* `CONFIG_BT_BLE_EATT_CHAN_NUM`
|
||||
*
|
||||
* @note Defined only when `CONFIG_BT_BLE_EATT_ENABLE` is set; calling it
|
||||
* in a build with EATT disabled fails at link time (no definition).
|
||||
*/
|
||||
esp_err_t esp_ble_eatt_set_chan_num(uint8_t num_chan);
|
||||
|
||||
/**
|
||||
* @brief Set the preferred EATT bearer for GATT client operations on a connection
|
||||
*
|
||||
* Requires `CONFIG_BT_BLE_EATT_ENABLE`.
|
||||
* By default the stack selects an available bearer automatically.
|
||||
* Pass `cid` as 0 to restore automatic selection.
|
||||
*
|
||||
* This API is intentionally synchronous (does not dispatch through the
|
||||
* BTC task): it updates the preferred bearer for GATT client TX routing
|
||||
* and returns validation errors immediately.
|
||||
*
|
||||
* @param[in] conn_id: GATT connection id
|
||||
* @param[in] cid: Local L2CAP channel identifier (CID) of the preferred EATT bearer
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: success
|
||||
* - ESP_ERR_INVALID_ARG: invalid `conn_id` or `cid`
|
||||
*
|
||||
* @note Defined only when `CONFIG_BT_BLE_EATT_ENABLE` is set; calling it
|
||||
* in a build with EATT disabled fails at link time (no definition).
|
||||
*/
|
||||
esp_err_t esp_ble_eatt_set_default_bearer(uint16_t conn_id, uint16_t cid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3825,17 +3825,55 @@ void bta_dm_acl_change(tBTA_DM_MSG *p_data)
|
||||
bta_dm_cb.p_sec_cback(BTA_DM_LINK_UP_EVT, (tBTA_DM_SEC *)&conn);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < bta_dm_cb.device_list.count; i++) {
|
||||
if (bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda)
|
||||
#if BLE_INCLUDED == TRUE
|
||||
|| bta_dm_cb.device_list.peer_device[i].transport != p_data->acl_change.transport
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BOOLEAN handle_only_match = FALSE;
|
||||
BD_ADDR op_bda;
|
||||
|
||||
bdcpy(op_bda, p_bda);
|
||||
#endif
|
||||
) {
|
||||
for (i = 0; i < bta_dm_cb.device_list.count; i++) {
|
||||
BOOLEAN entry_match = (bdcmp(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda) == 0)
|
||||
#if BLE_INCLUDED == TRUE
|
||||
&& (bta_dm_cb.device_list.peer_device[i].transport == p_data->acl_change.transport)
|
||||
#endif
|
||||
;
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* The peripheral pseudo-address bond feature may re-key an LE link's
|
||||
* address (RPA -> pseudo) AFTER link-up was recorded, so the stored
|
||||
* peer_bdaddr no longer matches the address reported at link-down.
|
||||
* Match by the stable connection handle for LE to avoid leaking
|
||||
* device_list entries (which would eventually exhaust the list). */
|
||||
if (!entry_match &&
|
||||
p_data->acl_change.transport == BT_TRANSPORT_LE &&
|
||||
bta_dm_cb.device_list.peer_device[i].transport == BT_TRANSPORT_LE &&
|
||||
bta_dm_cb.device_list.peer_device[i].conn_handle == p_data->acl_change.handle) {
|
||||
entry_match = TRUE;
|
||||
handle_only_match = TRUE;
|
||||
}
|
||||
#endif
|
||||
if (!entry_match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if (handle_only_match) {
|
||||
tBTM_SEC_DEV_REC *p_rec = btm_find_dev_by_handle(p_data->acl_change.handle);
|
||||
if (p_rec) {
|
||||
bdcpy(op_bda, p_rec->bd_addr);
|
||||
} else {
|
||||
APPL_TRACE_WARNING("%s: handle-matched entry but no BTM record (handle=0x%x),"
|
||||
" falling back to event addr",
|
||||
__func__, p_data->acl_change.handle);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_UNPAIRING ) {
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if (BTM_SecDeleteDevice(op_bda, bta_dm_cb.device_list.peer_device[i].transport)) {
|
||||
#else
|
||||
if (BTM_SecDeleteDevice(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, bta_dm_cb.device_list.peer_device[i].transport)) {
|
||||
#endif
|
||||
issue_unpair_cb = TRUE;
|
||||
}
|
||||
|
||||
@@ -3890,10 +3928,18 @@ void bta_dm_acl_change(tBTA_DM_MSG *p_data)
|
||||
}
|
||||
}
|
||||
if (conn.link_down.is_removed) {
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BTM_SecDeleteDevice(op_bda, p_data->acl_change.transport);
|
||||
#if (GATTC_INCLUDED == TRUE)
|
||||
/* need to remove all pending background connection */
|
||||
BTA_GATTC_CancelOpen(0, op_bda, FALSE);
|
||||
#endif
|
||||
#else
|
||||
BTM_SecDeleteDevice(p_bda, p_data->acl_change.transport);
|
||||
#if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE)
|
||||
/* need to remove all pending background connection */
|
||||
BTA_GATTC_CancelOpen(0, p_bda, FALSE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3902,6 +3948,11 @@ void bta_dm_acl_change(tBTA_DM_MSG *p_data)
|
||||
if ( bta_dm_cb.p_sec_cback ) {
|
||||
bta_dm_cb.p_sec_cback(BTA_DM_LINK_DOWN_EVT, &conn);
|
||||
if ( issue_unpair_cb ) {
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if (handle_only_match) {
|
||||
bdcpy(conn.link_down.bd_addr, op_bda);
|
||||
}
|
||||
#endif
|
||||
if (p_data->acl_change.transport == BT_TRANSPORT_LE) {
|
||||
bta_dm_cb.p_sec_cback(BTA_DM_BLE_DEV_UNPAIRED_EVT, &conn);
|
||||
} else {
|
||||
@@ -5137,7 +5188,19 @@ void bta_dm_add_ble_device (tBTA_DM_MSG *p_data)
|
||||
(p_data->add_ble_device.bd_addr[0] << 24) + (p_data->add_ble_device.bd_addr[1] << 16) + \
|
||||
(p_data->add_ble_device.bd_addr[2] << 8) + p_data->add_ble_device.bd_addr[3],
|
||||
(p_data->add_ble_device.bd_addr[4] << 8) + p_data->add_ble_device.bd_addr[5]);
|
||||
return;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if (p_data->add_ble_device.is_pseudo_bond) {
|
||||
if (!BTM_BleMarkPseudoBond(p_data->add_ble_device.bd_addr)) {
|
||||
APPL_TRACE_WARNING("BTA_DM: failed to mark pseudo bond for device %08x%04x",
|
||||
(p_data->add_ble_device.bd_addr[0] << 24) + (p_data->add_ble_device.bd_addr[1] << 16) + \
|
||||
(p_data->add_ble_device.bd_addr[2] << 8) + p_data->add_ble_device.bd_addr[3],
|
||||
(p_data->add_ble_device.bd_addr[4] << 8) + p_data->add_ble_device.bd_addr[5]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -1261,10 +1261,32 @@ void BTA_DmAddBleKey (BD_ADDR bd_addr, tBTA_LE_KEY_VALUE *p_le_key, tBTA_LE_KEY_
|
||||
** dev_type - Remote device's device type.
|
||||
** auth_mode - auth mode
|
||||
** addr_type - LE device address type.
|
||||
** is_pseudo_bond - (pseudo bond only) TRUE when NVS section is
|
||||
** keyed by a Host pseudo; tagged on BTU thread.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode,
|
||||
tBT_DEVICE_TYPE dev_type, BOOLEAN is_pseudo_bond)
|
||||
{
|
||||
tBTA_DM_API_ADD_BLE_DEVICE *p_msg;
|
||||
|
||||
if ((p_msg = (tBTA_DM_API_ADD_BLE_DEVICE *) osi_malloc(sizeof(tBTA_DM_API_ADD_BLE_DEVICE))) != NULL) {
|
||||
memset (p_msg, 0, sizeof(tBTA_DM_API_ADD_BLE_DEVICE));
|
||||
|
||||
p_msg->hdr.event = BTA_DM_API_ADD_BLEDEVICE_EVT;
|
||||
bdcpy(p_msg->bd_addr, bd_addr);
|
||||
p_msg->addr_type = addr_type;
|
||||
p_msg->auth_mode = auth_mode;
|
||||
p_msg->dev_type = dev_type;
|
||||
p_msg->is_pseudo_bond = is_pseudo_bond;
|
||||
|
||||
bta_sys_sendmsg(p_msg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode, tBT_DEVICE_TYPE dev_type)
|
||||
{
|
||||
tBTA_DM_API_ADD_BLE_DEVICE *p_msg;
|
||||
@@ -1281,6 +1303,7 @@ void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode
|
||||
bta_sys_sendmsg(p_msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function BTA_DmBlePasskeyReply
|
||||
|
||||
@@ -807,6 +807,9 @@ typedef struct {
|
||||
tBT_DEVICE_TYPE dev_type ;
|
||||
UINT32 auth_mode;
|
||||
tBLE_ADDR_TYPE addr_type;
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BOOLEAN is_pseudo_bond;
|
||||
#endif
|
||||
|
||||
} tBTA_DM_API_ADD_BLE_DEVICE;
|
||||
|
||||
|
||||
@@ -2533,12 +2533,19 @@ extern void BTA_DmBleConfirmReply(BD_ADDR bd_addr, BOOLEAN accept);
|
||||
** dev_type - Remote device's device type.
|
||||
** auth_mode - auth mode
|
||||
** addr_type - LE device address type.
|
||||
** is_pseudo_bond - (pseudo bond only) TRUE when NVS section is
|
||||
** keyed by a Host pseudo; tagged on BTU thread.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
extern void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode,
|
||||
tBT_DEVICE_TYPE dev_type, BOOLEAN is_pseudo_bond);
|
||||
#else
|
||||
extern void BTA_DmAddBleDevice(BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, int auth_mode,
|
||||
tBT_DEVICE_TYPE dev_type);
|
||||
#endif
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "btc/btc_ble_storage.h"
|
||||
#include "bta/bta_gatts_co.h"
|
||||
#include "btc/btc_util.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "stack/btm_ble_api.h"
|
||||
#endif
|
||||
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
|
||||
@@ -134,6 +137,21 @@ static bt_status_t _btc_storage_add_ble_bonding_key(bt_bdaddr_t *remote_bd_addr,
|
||||
}
|
||||
|
||||
int ret = btc_config_set_bin(bdstr, name, (const uint8_t *)key, key_length);
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* If this bond's section is keyed by a Host pseudo address (dual local
|
||||
* identity link, still connected at save time), flag the section so the
|
||||
* identity-based NVS de-dup never deletes it as a "duplicate" of the other
|
||||
* local identity's bond (which shares the same peer Identity). Normal /
|
||||
* RPA-keyed bonds are NOT flagged and keep the native cleanup behavior. */
|
||||
{
|
||||
BD_ADDR real_peer;
|
||||
if (BTM_BleGetRealPeerByPseudo(remote_bd_addr->address, real_peer)) {
|
||||
btc_config_set_int(bdstr, BTC_BLE_STORAGE_PSEUDO_BOND_STR, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_btc_storage_save();
|
||||
return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
|
||||
}
|
||||
@@ -256,6 +274,14 @@ static bt_status_t _btc_storage_remove_all_ble_keys(const char *name)
|
||||
if (btc_config_exist(name, BTC_BLE_STORAGE_LE_KEY_LID_STR)) {
|
||||
ret |= btc_config_remove(name, BTC_BLE_STORAGE_LE_KEY_LID_STR);
|
||||
}
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Clear the dual-identity pseudo-bond marker together with the LE keys so
|
||||
* a removed bond does not leave a stale flag that would shield an empty
|
||||
* section from cleanup. */
|
||||
if (btc_config_exist(name, BTC_BLE_STORAGE_PSEUDO_BOND_STR)) {
|
||||
ret |= btc_config_remove(name, BTC_BLE_STORAGE_PSEUDO_BOND_STR);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -273,6 +299,22 @@ void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del
|
||||
return;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Never use a pseudo-keyed bond as the de-dup baseline: it legitimately
|
||||
* shares the peer Identity with a normal bond on another local identity.
|
||||
* Symmetric with btc_storage_delete_duplicate_ble_devices() skipping pseudo
|
||||
* baselines. The flag is only set when keys are saved, so use the live
|
||||
* pseudo mapping rather than BTC_BLE_STORAGE_PSEUDO_BOND_STR on cur_addr.
|
||||
* Orphan cleanup below still runs; only identity de-dup is skipped. */
|
||||
BOOLEAN skip_identity_dedup = FALSE;
|
||||
{
|
||||
BD_ADDR dummy;
|
||||
if (BTM_BleGetRealPeerByPseudo(cur_addr, dummy)) {
|
||||
skip_identity_dedup = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
btc_config_lock();
|
||||
|
||||
const btc_config_section_iter_t *iter = btc_config_section_begin();
|
||||
@@ -303,6 +345,13 @@ void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del
|
||||
continue;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if (skip_identity_dedup) {
|
||||
iter = btc_config_section_next(iter);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
string_to_bdaddr(section, &bd_addr);
|
||||
|
||||
char buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0};
|
||||
@@ -319,7 +368,14 @@ void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del
|
||||
if (del_pid_key->addr_type == pid_key->addr_type &&
|
||||
!btc_storage_is_all_zeros(pid_key->static_addr, sizeof(pid_key->static_addr)) &&
|
||||
memcmp(del_pid_key->static_addr, pid_key->static_addr, sizeof(pid_key->static_addr)) == 0 &&
|
||||
memcmp(cur_addr, bd_addr.address, sizeof(bd_addr.address)) != 0) {
|
||||
memcmp(cur_addr, bd_addr.address, sizeof(bd_addr.address)) != 0
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Dual local-identity bond isolation: a section keyed by a Host
|
||||
* pseudo legitimately shares the peer Identity with another
|
||||
* local identity's bond; never delete it as a "duplicate". */
|
||||
&& !btc_config_exist(section, BTC_BLE_STORAGE_PSEUDO_BOND_STR)
|
||||
#endif
|
||||
) {
|
||||
if (device_type == BT_DEVICE_TYPE_DUMO) {
|
||||
btc_config_set_int(section, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR);
|
||||
_btc_storage_remove_all_ble_keys(section);
|
||||
@@ -360,6 +416,19 @@ void btc_storage_delete_duplicate_ble_devices(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Dual local-identity bond isolation: never use a pseudo-keyed section
|
||||
* as the de-dup baseline. The inner check below only protects pseudo
|
||||
* candidates, so without this an order-dependent case remains: if a
|
||||
* pseudo bond is visited first and becomes the baseline, a normal bond
|
||||
* that legitimately shares the same peer Identity (no PseudoBond flag)
|
||||
* would match and be deleted. Skipping pseudo baselines makes the
|
||||
* protection symmetric. */
|
||||
if (btc_config_exist(name, BTC_BLE_STORAGE_PSEUDO_BOND_STR)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
string_to_bdaddr(name, &bd_addr);
|
||||
size_t pid_len = sizeof(tBTM_LE_PID_KEYS);
|
||||
bool pid_ok = btc_config_get_bin(name, BTC_BLE_STORAGE_LE_KEY_PID_STR, (uint8_t *)buffer, &pid_len);
|
||||
@@ -388,7 +457,13 @@ void btc_storage_delete_duplicate_ble_devices(void)
|
||||
temp_pid_key = (tBTM_LE_PID_KEYS *) temp_buffer;
|
||||
if (pid_key->addr_type == temp_pid_key->addr_type &&
|
||||
!btc_storage_is_all_zeros(temp_pid_key->static_addr, sizeof(temp_pid_key->static_addr)) &&
|
||||
memcmp(pid_key->static_addr, temp_pid_key->static_addr, sizeof(pid_key->static_addr)) == 0) {
|
||||
memcmp(pid_key->static_addr, temp_pid_key->static_addr, sizeof(pid_key->static_addr)) == 0
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Skip pseudo-keyed sections: a dual local-identity bond
|
||||
* shares the peer Identity with another bond on purpose. */
|
||||
&& !btc_config_exist(temp_name, BTC_BLE_STORAGE_PSEUDO_BOND_STR)
|
||||
#endif
|
||||
) {
|
||||
temp_iter = btc_config_section_next(temp_iter);
|
||||
if (temp_device_type == BT_DEVICE_TYPE_DUMO) {
|
||||
btc_config_set_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR);
|
||||
@@ -915,8 +990,17 @@ bt_status_t btc_storage_get_remote_addr_type(bt_bdaddr_t *remote_bd_addr,
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
#if (SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#define BTC_BLE_FETCH_PSEUDO_BOND_PARAM , bool is_pseudo_bond
|
||||
#define BTC_BLE_FETCH_PSEUDO_BOND_ARG , is_pseudo_bond
|
||||
#else
|
||||
#define BTC_BLE_FETCH_PSEUDO_BOND_PARAM
|
||||
#define BTC_BLE_FETCH_PSEUDO_BOND_ARG
|
||||
#endif
|
||||
|
||||
static void _btc_read_le_key(const uint8_t key_type, const size_t key_len, bt_bdaddr_t bd_addr,
|
||||
const uint8_t addr_type, const bool add_key, bool *device_added, bool *key_found)
|
||||
const uint8_t addr_type, const bool add_key BTC_BLE_FETCH_PSEUDO_BOND_PARAM,
|
||||
bool *device_added, bool *key_found)
|
||||
{
|
||||
assert(device_added);
|
||||
assert(key_found);
|
||||
@@ -936,7 +1020,12 @@ static void _btc_read_le_key(const uint8_t key_type, const size_t key_len, bt_bd
|
||||
if(_btc_storage_get_ble_dev_auth_mode(&bd_addr, &auth_mode) != BT_STATUS_SUCCESS) {
|
||||
BTC_TRACE_WARNING("%s Failed to get auth mode from flash, please erase flash and download the firmware again", __func__);
|
||||
}
|
||||
#if (SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BTA_DmAddBleDevice(bta_bd_addr, addr_type, auth_mode, BT_DEVICE_TYPE_BLE,
|
||||
is_pseudo_bond ? TRUE : FALSE);
|
||||
#else
|
||||
BTA_DmAddBleDevice(bta_bd_addr, addr_type, auth_mode, BT_DEVICE_TYPE_BLE);
|
||||
#endif
|
||||
*device_added = true;
|
||||
}
|
||||
|
||||
@@ -956,9 +1045,11 @@ bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr,
|
||||
uint32_t device_type = 0;
|
||||
int addr_type = BLE_ADDR_PUBLIC;
|
||||
bt_bdaddr_t bd_addr;
|
||||
BD_ADDR bta_bd_addr;
|
||||
bool device_added = false;
|
||||
bool key_found = false;
|
||||
#if (SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
const bool is_pseudo_bond = add && btc_config_exist(remote_bd_addr, BTC_BLE_STORAGE_PSEUDO_BOND_STR);
|
||||
#endif
|
||||
|
||||
if (!btc_config_get_int(remote_bd_addr, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type)) {
|
||||
BTC_TRACE_ERROR("%s, device_type = %x", __func__, device_type);
|
||||
@@ -966,7 +1057,6 @@ bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr,
|
||||
}
|
||||
|
||||
string_to_bdaddr(remote_bd_addr, &bd_addr);
|
||||
bdcpy(bta_bd_addr, bd_addr.address);
|
||||
|
||||
if (_btc_storage_get_remote_addr_type(&bd_addr, &addr_type) != BT_STATUS_SUCCESS) {
|
||||
addr_type = BLE_ADDR_PUBLIC;
|
||||
@@ -974,22 +1064,22 @@ bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr,
|
||||
}
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_PENC, sizeof(tBTM_LE_PENC_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_PID, sizeof(tBTM_LE_PID_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_LID, sizeof(tBTM_LE_PID_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_PCSRK, sizeof(tBTM_LE_PCSRK_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_LENC, sizeof(tBTM_LE_LENC_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
_btc_read_le_key(BTM_LE_KEY_LCSRK, sizeof(tBTM_LE_LCSRK_KEYS),
|
||||
bd_addr, addr_type, add, &device_added, &key_found);
|
||||
bd_addr, addr_type, add BTC_BLE_FETCH_PSEUDO_BOND_ARG, &device_added, &key_found);
|
||||
|
||||
if (key_found) {
|
||||
return BT_STATUS_SUCCESS;
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include "bta_gattc_int.h"
|
||||
#include "bta_gatts_int.h"
|
||||
#include "bta_dm_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
|
||||
static future_t *main_future[BTC_MAIN_FUTURE_NUM];
|
||||
static SemaphoreHandle_t s_init_done_sem = NULL;
|
||||
@@ -48,6 +51,15 @@ static void btc_disable_bluetooth(void)
|
||||
|
||||
void btc_init_callback(bt_status_t status)
|
||||
{
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Only arm the EATT callback once BTE startup actually succeeded. On failure
|
||||
* the partial-init cleanup path tears the stack down (and NULLs this cback
|
||||
* in gatt_eatt_deinit), so registering it here would only briefly reference a
|
||||
* non-running stack. Matches the deliberate NULL-on-teardown in deinit. */
|
||||
if (status == BT_STATUS_SUCCESS) {
|
||||
gatt_eatt_register_evt_cback(btc_ble_gap_eatt_evt_cback);
|
||||
}
|
||||
#endif
|
||||
s_init_clean = (status == BT_STATUS_SUCCESS) ? false : true;
|
||||
future_ready(*btc_main_get_future_p(BTC_MAIN_INIT_FUTURE),
|
||||
(status == BT_STATUS_SUCCESS) ? FUTURE_SUCCESS : FUTURE_FAIL);
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#define BTC_BLE_STORAGE_LE_KEY_LID_STR "LE_KEY_LID"
|
||||
#define BTC_BLE_STORAGE_LE_KEY_LCSRK_STR "LE_KEY_LCSRK"
|
||||
#define BTC_BLE_STORAGE_LE_AUTH_MODE_STR "AuthMode"
|
||||
/* Marks a bond whose section is keyed by a Host pseudo address (dual local
|
||||
* identity feature). Such sections legitimately share the peer Identity with
|
||||
* another (local,peer) bond and must be exempt from identity-based de-dup. */
|
||||
#define BTC_BLE_STORAGE_PSEUDO_BOND_STR "PseudoBond"
|
||||
|
||||
#define BTC_BLE_STORAGE_LOCAL_ADAPTER_STR "Adapter"
|
||||
#define BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR "LE_LOCAL_KEY_IR"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,9 @@
|
||||
#include "btc/btc_util.h"
|
||||
#include "osi/mutex.h"
|
||||
#include "osi/thread.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#include "osi/pkt_queue.h"
|
||||
#if (BT_CONTROLLER_INCLUDED == TRUE)
|
||||
#include "esp_bt.h"
|
||||
@@ -2286,6 +2289,31 @@ static void btc_ble_set_privacy_mode(uint8_t addr_type,
|
||||
BTA_DmBleSetPrivacyMode(addr_type, addr, privacy_mode);
|
||||
}
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
void btc_ble_gap_eatt_evt_cback(UINT16 conn_id, UINT8 status, UINT16 cid)
|
||||
{
|
||||
btc_msg_t msg = {0};
|
||||
esp_ble_gap_cb_param_t param = {0};
|
||||
bt_status_t ret;
|
||||
|
||||
param.eatt_evt.conn_id = conn_id;
|
||||
param.eatt_evt.status = status;
|
||||
param.eatt_evt.cid = cid;
|
||||
|
||||
msg.sig = BTC_SIG_API_CB;
|
||||
msg.pid = BTC_PID_GAP_BLE;
|
||||
msg.act = ESP_GAP_BLE_EATT_EVT;
|
||||
|
||||
/* eatt_evt holds only scalars, so no deep copy/free is needed (matches the
|
||||
* convention used by the other scalar-only GAP cb events in this file). */
|
||||
ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t),
|
||||
NULL, NULL);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
BTC_TRACE_ERROR("EATT evt transfer failed");
|
||||
}
|
||||
}
|
||||
#endif /* BLE_EATT_INCLUDED == TRUE */
|
||||
|
||||
void btc_gap_ble_cb_handler(btc_msg_t *msg)
|
||||
{
|
||||
esp_ble_gap_cb_param_t *param = (esp_ble_gap_cb_param_t *)msg->arg;
|
||||
@@ -3096,6 +3124,13 @@ void btc_gap_ble_cb_deep_free(btc_msg_t *msg)
|
||||
}
|
||||
break;
|
||||
#endif // (BT_BLE_FEAT_CHANNEL_SOUNDING == TRUE)
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
case ESP_GAP_BLE_EATT_EVT:
|
||||
/* Scalar-only event: nothing to free. Handled explicitly so the
|
||||
* unconditional cb_deep_free call in btc_gap_ble_cb_handler does not
|
||||
* emit a spurious "Unhandled deep free" debug log. */
|
||||
break;
|
||||
#endif // (BLE_EATT_INCLUDED == TRUE)
|
||||
default:
|
||||
BTC_TRACE_DEBUG("Unhandled deep free %d", msg->act);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __BTC_BLE_L2CAP_H__
|
||||
#define __BTC_BLE_L2CAP_H__
|
||||
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
|
||||
#include "btc/btc_manage.h"
|
||||
#include "common/bt_target.h"
|
||||
#include "esp_ble_l2cap_api.h"
|
||||
|
||||
typedef enum {
|
||||
BTC_BLE_L2CAP_ACT_INIT = 0,
|
||||
BTC_BLE_L2CAP_ACT_DEINIT,
|
||||
BTC_BLE_L2CAP_ACT_CREATE_SERVER,
|
||||
BTC_BLE_L2CAP_ACT_DELETE_SERVER,
|
||||
BTC_BLE_L2CAP_ACT_CONNECT,
|
||||
BTC_BLE_L2CAP_ACT_ACCEPT,
|
||||
BTC_BLE_L2CAP_ACT_DISCONNECT,
|
||||
BTC_BLE_L2CAP_ACT_SEND,
|
||||
BTC_BLE_L2CAP_ACT_RECV_READY,
|
||||
BTC_BLE_L2CAP_ACT_CONNECT_ECOC,
|
||||
BTC_BLE_L2CAP_ACT_RECONFIG,
|
||||
BTC_BLE_L2CAP_ACT_SET_AUTO_CREDIT,
|
||||
} btc_ble_l2cap_act_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t psm;
|
||||
uint16_t mtu;
|
||||
} create_server;
|
||||
struct {
|
||||
uint16_t psm;
|
||||
} delete_server;
|
||||
struct {
|
||||
uint16_t conn_id;
|
||||
uint16_t psm;
|
||||
uint16_t mtu;
|
||||
} connect;
|
||||
struct {
|
||||
uint16_t conn_id;
|
||||
uint8_t l2cap_id;
|
||||
uint16_t chan_handle;
|
||||
bool accept;
|
||||
uint16_t mtu;
|
||||
} accept;
|
||||
struct {
|
||||
uint16_t chan_handle;
|
||||
} disconnect;
|
||||
struct {
|
||||
uint16_t chan_handle;
|
||||
uint16_t len;
|
||||
uint8_t *data;
|
||||
} send;
|
||||
struct {
|
||||
uint16_t chan_handle;
|
||||
} recv_ready;
|
||||
struct {
|
||||
uint16_t conn_id;
|
||||
uint16_t psm;
|
||||
uint16_t mtu;
|
||||
uint8_t num_chan;
|
||||
} connect_ecoc;
|
||||
struct {
|
||||
uint16_t num_chan;
|
||||
uint16_t mtu;
|
||||
uint16_t mps;
|
||||
uint16_t chan_handles[BLE_MAX_L2CAP_CLIENTS];
|
||||
} reconfig;
|
||||
struct {
|
||||
uint16_t chan_handle;
|
||||
bool enable;
|
||||
} set_auto_credit;
|
||||
} btc_ble_l2cap_args_t;
|
||||
|
||||
void btc_ble_l2cap_call_handler(btc_msg_t *msg);
|
||||
void btc_ble_l2cap_cb_handler(btc_msg_t *msg);
|
||||
void btc_ble_l2cap_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
|
||||
void btc_ble_l2cap_arg_deep_free(btc_msg_t *msg);
|
||||
void btc_ble_l2cap_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
|
||||
void btc_ble_l2cap_cb_deep_free(btc_msg_t *msg);
|
||||
|
||||
#endif /* BLE_L2CAP_COC_INCLUDED == TRUE */
|
||||
|
||||
#endif /* __BTC_BLE_L2CAP_H__ */
|
||||
@@ -831,4 +831,8 @@ void btc_gap_ble_deinit(void);
|
||||
void btc_adv_list_init(void);
|
||||
void btc_adv_list_deinit(void);
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
void btc_ble_gap_eatt_evt_cback(UINT16 conn_id, UINT8 status, UINT16 cid);
|
||||
#endif
|
||||
|
||||
#endif /* __BTC_GAP_BLE_H__ */
|
||||
|
||||
@@ -219,6 +219,12 @@
|
||||
#define UC_BT_BLE_50_FEATURES_SUPPORTED FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
#define UC_BT_BLE_PERIPH_PSEUDO_ADDR_BOND CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
#else
|
||||
#define UC_BT_BLE_PERIPH_PSEUDO_ADDR_BOND FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_42_FEATURES_SUPPORTED
|
||||
#define UC_BT_BLE_42_FEATURES_SUPPORTED CONFIG_BT_BLE_42_FEATURES_SUPPORTED
|
||||
#else
|
||||
@@ -701,6 +707,54 @@
|
||||
#define UC_BT_BLE_RPA_TIMEOUT 900
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_L2CAP_COC_ENABLED
|
||||
#define UC_BT_BLE_L2CAP_COC_ENABLED CONFIG_BT_BLE_L2CAP_COC_ENABLED
|
||||
#else
|
||||
#define UC_BT_BLE_L2CAP_COC_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_L2CAP_COC_MAX_CHAN
|
||||
#define UC_BT_BLE_L2CAP_COC_MAX_CHAN CONFIG_BT_BLE_L2CAP_COC_MAX_CHAN
|
||||
#else
|
||||
#define UC_BT_BLE_L2CAP_COC_MAX_CHAN 5
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_L2CAP_COC_MPS
|
||||
#define UC_BT_BLE_L2CAP_COC_MPS CONFIG_BT_BLE_L2CAP_COC_MPS
|
||||
#else
|
||||
#define UC_BT_BLE_L2CAP_COC_MPS 247
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_L2CAP_COC_INIT_CREDITS
|
||||
#define UC_BT_BLE_L2CAP_COC_INIT_CREDITS CONFIG_BT_BLE_L2CAP_COC_INIT_CREDITS
|
||||
#else
|
||||
#define UC_BT_BLE_L2CAP_COC_INIT_CREDITS 24
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_L2CAP_ENHANCED_COC
|
||||
#define UC_BT_BLE_L2CAP_ENHANCED_COC CONFIG_BT_BLE_L2CAP_ENHANCED_COC
|
||||
#else
|
||||
#define UC_BT_BLE_L2CAP_ENHANCED_COC FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_EATT_ENABLE
|
||||
#define UC_BT_BLE_EATT_ENABLE CONFIG_BT_BLE_EATT_ENABLE
|
||||
#else
|
||||
#define UC_BT_BLE_EATT_ENABLE FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_EATT_CHAN_NUM
|
||||
#define UC_BT_BLE_EATT_CHAN_NUM CONFIG_BT_BLE_EATT_CHAN_NUM
|
||||
#else
|
||||
#define UC_BT_BLE_EATT_CHAN_NUM 3
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_EATT_MTU
|
||||
#define UC_BT_BLE_EATT_MTU CONFIG_BT_BLE_EATT_MTU
|
||||
#else
|
||||
#define UC_BT_BLE_EATT_MTU 247
|
||||
#endif
|
||||
|
||||
//SCO VOICE OVER HCI
|
||||
#ifdef CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
|
||||
#define UC_BT_HFP_AUDIO_DATA_PATH_HCI CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
|
||||
|
||||
@@ -246,6 +246,14 @@
|
||||
#define BLE_50_FEATURE_SUPPORT FALSE
|
||||
#endif
|
||||
|
||||
/* Peripheral dual local-identity bond isolation via Host-internal pseudo
|
||||
* address. Guarded so default builds keep the legacy single-bond behavior. */
|
||||
#if (UC_BT_BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#define BLE_PERIPH_PSEUDO_ADDR_BOND TRUE
|
||||
#else
|
||||
#define BLE_PERIPH_PSEUDO_ADDR_BOND FALSE
|
||||
#endif
|
||||
|
||||
#if (UC_BT_BLE_ENABLED ==TRUE)
|
||||
#if (UC_BT_BLE_42_FEATURES_SUPPORTED == TRUE || BLE_50_FEATURE_SUPPORT == FALSE)
|
||||
#define BLE_42_FEATURE_SUPPORT TRUE
|
||||
@@ -1496,7 +1504,85 @@
|
||||
|
||||
/* Support status of L2CAP connection-oriented dynamic channels over LE transport with dynamic CID */
|
||||
#ifndef BLE_L2CAP_COC_INCLUDED
|
||||
#define BLE_L2CAP_COC_INCLUDED FALSE // LE COC not use by default
|
||||
#if (UC_BT_BLE_L2CAP_COC_ENABLED == TRUE)
|
||||
#define BLE_L2CAP_COC_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_L2CAP_COC_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
#undef BLE_MAX_L2CAP_CLIENTS
|
||||
#define BLE_MAX_L2CAP_CLIENTS UC_BT_BLE_L2CAP_COC_MAX_CHAN
|
||||
#endif
|
||||
|
||||
/* Initial LE CoC/ECFC RX credit window (K-frames) from
|
||||
* CONFIG_BT_BLE_L2CAP_COC_INIT_CREDITS. Defined even when CoC is disabled so
|
||||
* that internal headers that reference L2CAP_LE_INIT_CREDITS remain valid. */
|
||||
#ifndef L2CAP_LE_INIT_CREDITS
|
||||
#define L2CAP_LE_INIT_CREDITS UC_BT_BLE_L2CAP_COC_INIT_CREDITS
|
||||
#endif
|
||||
|
||||
/* Default LE CoC/ECFC MPS from CONFIG_BT_BLE_L2CAP_COC_MPS. */
|
||||
#ifndef L2CAP_LE_COC_MPS
|
||||
#define L2CAP_LE_COC_MPS UC_BT_BLE_L2CAP_COC_MPS
|
||||
#endif
|
||||
|
||||
#ifndef BLE_L2CAP_COC_CLIENT_INCLUDED
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE)
|
||||
#define BLE_L2CAP_COC_CLIENT_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_L2CAP_COC_CLIENT_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BLE_L2CAP_COC_SERVER_INCLUDED
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE) && (GATTS_INCLUDED == TRUE)
|
||||
#define BLE_L2CAP_COC_SERVER_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_L2CAP_COC_SERVER_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BLE_L2CAP_ENHANCED_COC_INCLUDED
|
||||
#if (UC_BT_BLE_L2CAP_ENHANCED_COC == TRUE) && (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
#define BLE_L2CAP_ENHANCED_COC_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_L2CAP_ENHANCED_COC_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BLE_EATT_INCLUDED
|
||||
#if (UC_BT_BLE_EATT_ENABLE == TRUE) && (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
#define BLE_EATT_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_EATT_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BLE_EATT_CLIENT_INCLUDED
|
||||
#if (BLE_EATT_INCLUDED == TRUE) && (GATTC_INCLUDED == TRUE)
|
||||
#define BLE_EATT_CLIENT_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_EATT_CLIENT_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef BLE_EATT_SERVER_INCLUDED
|
||||
#if (BLE_EATT_INCLUDED == TRUE) && (GATTS_INCLUDED == TRUE)
|
||||
#define BLE_EATT_SERVER_INCLUDED TRUE
|
||||
#else
|
||||
#define BLE_EATT_SERVER_INCLUDED FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* EATT bearer count and MTU from CONFIG_BT_BLE_EATT_CHAN_NUM / CONFIG_BT_BLE_EATT_MTU. */
|
||||
#ifndef GATT_EATT_MAX_CHAN
|
||||
#define GATT_EATT_MAX_CHAN UC_BT_BLE_EATT_CHAN_NUM
|
||||
#endif
|
||||
|
||||
#ifndef GATT_EATT_MTU
|
||||
#define GATT_EATT_MTU UC_BT_BLE_EATT_MTU
|
||||
#endif
|
||||
|
||||
/* Support status of L2CAP connection-oriented dynamic channels over LE or BR/EDR transport with dynamic CID */
|
||||
|
||||
@@ -182,6 +182,10 @@ static void reassemble_and_dispatch(BT_HDR *packet)
|
||||
}
|
||||
|
||||
STREAM_TO_UINT16(l2cap_length, stream);
|
||||
/* A zero-length L2CAP information payload is valid per Core Spec v6.2
|
||||
* Vol 3 Part A 3.1 (B-frame payload is 0..65535 octets); do not drop
|
||||
* it. The downstream length math handles l2cap_length == 0 correctly
|
||||
* (full_length == header-only == 8). */
|
||||
/* Check for integer overflow in length calculation */
|
||||
if (l2cap_length > (UINT16_MAX - L2CAP_HEADER_SIZE - HCI_ACL_PREAMBLE_SIZE)) {
|
||||
HCI_TRACE_ERROR("L2CAP length too large: %u", l2cap_length);
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
#include "stack/btu.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "btm_int.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "btm_ble_int.h"
|
||||
#include "btm_ble_pseudo.h"
|
||||
#endif
|
||||
#include "stack/acl_hci_link_interface.h"
|
||||
#include "l2c_int.h"
|
||||
#include "stack/l2cap_hci_link_interface.h"
|
||||
@@ -571,12 +575,6 @@ void btm_acl_removed (BD_ADDR bda, tBT_TRANSPORT transport)
|
||||
btm_cb.ble_ctr_cb.inq_var.connectable_mode,
|
||||
p->link_role);
|
||||
|
||||
if (p->transport == BT_TRANSPORT_LE) {
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
btm_ble_clear_ext_adv_ter_con_handle(p->hci_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
p_dev_rec = btm_find_dev(bda);
|
||||
if ( p_dev_rec) {
|
||||
BTM_TRACE_DEBUG("before update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags);
|
||||
@@ -603,6 +601,11 @@ void btm_acl_removed (BD_ADDR bda, tBT_TRANSPORT transport)
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
list_remove(btm_cb.p_pm_mode_db_list, p->p_pm_mode_db);
|
||||
#endif // #if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
if (p->transport == BT_TRANSPORT_LE) {
|
||||
btm_ble_clear_ext_adv_ter_con_handle(p->hci_handle);
|
||||
}
|
||||
#endif
|
||||
/* Remove and free the ACL connection data */
|
||||
list_remove(btm_cb.p_acl_db_list, p);
|
||||
p = NULL;
|
||||
@@ -2920,5 +2923,16 @@ BOOLEAN btm_acl_disconnected(UINT16 handle, UINT8 reason)
|
||||
|
||||
#endif /* SMP_INCLUDED == TRUE */
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Drop the per-connection pseudo identity mapping for this handle. */
|
||||
BLE_PSEUDO_DBG("disconnect: handle=0x%x reason=0x%x -> cleanup", handle, reason);
|
||||
btm_ble_conn_identity_unregister(handle);
|
||||
#endif
|
||||
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
/* Unbind ext-adv sets so a reused handle cannot leak into another inst. */
|
||||
btm_ble_clear_ext_adv_ter_con_handle(handle);
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -36,12 +36,17 @@
|
||||
#include "stack/gap_api.h"
|
||||
//#include "bt_utils.h"
|
||||
#include "device/controller.h"
|
||||
#include "btm_ble_pseudo.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "gatt_int.h"
|
||||
#endif
|
||||
|
||||
//#define LOG_TAG "bt_btm_ble"
|
||||
//#include "osi/include/log.h"
|
||||
#if BLE_INCLUDED == TRUE
|
||||
extern void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda);
|
||||
#if SMP_INCLUDED == TRUE
|
||||
#include "smp_int.h"
|
||||
// The temp variable to pass parameter between functions when in the connected event callback.
|
||||
static BOOLEAN temp_enhanced = FALSE;
|
||||
extern BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length,
|
||||
@@ -50,6 +55,21 @@ extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable);
|
||||
extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda);
|
||||
#endif
|
||||
extern void gatt_notify_enc_cmpl(BD_ADDR bd_addr);
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
static BOOLEAN btm_ble_make_conn_pseudo(UINT16 handle, BD_ADDR real_peer,
|
||||
tBLE_ADDR_TYPE peer_type, BD_ADDR pseudo_out);
|
||||
static void btm_ble_pseudo_bringup_conn(UINT16 handle, UINT8 role,
|
||||
const BD_ADDR hash_peer, UINT8 hash_peer_type,
|
||||
const BD_ADDR fallback_bda, UINT8 bda_type,
|
||||
UINT16 conn_interval, UINT16 conn_latency,
|
||||
UINT16 conn_timeout, BOOLEAN match,
|
||||
const UINT8 *air_peer, UINT8 air_peer_type,
|
||||
const char *tag, BD_ADDR conn_index_bda_out);
|
||||
static void btm_ble_pseudo_pick_peer_identity(tBTM_SEC_DEV_REC *p_rec, const BD_ADDR on_air,
|
||||
UINT8 on_air_type,
|
||||
BD_ADDR peer_out, UINT8 *p_peer_type);
|
||||
extern tBTM_SEC_DEV_REC *btm_find_dev_by_identity_addr(BD_ADDR bd_addr, UINT8 addr_type);
|
||||
#endif
|
||||
/*******************************************************************************/
|
||||
/* External Function to be called by other modules */
|
||||
/*******************************************************************************/
|
||||
@@ -280,6 +300,90 @@ void BTM_GetDeviceDHK (BT_OCTET16 dhk)
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleGetRealPeerByPseudo
|
||||
**
|
||||
** Description Reverse map a Host pseudo address (as seen by the app in
|
||||
** remote_bda for a dual-identity link) to the real peer
|
||||
** identity. Returns TRUE if the pseudo is currently known.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleGetRealPeerByPseudo(BD_ADDR pseudo, BD_ADDR real_peer)
|
||||
{
|
||||
return btm_ble_pseudo_to_real_peer(pseudo, real_peer);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleGetConnIdentityByPseudo
|
||||
**
|
||||
** Description Return the full identity (real peer + local identity and
|
||||
** their address types) for a connected dual-identity link,
|
||||
** keyed by the pseudo address the application sees.
|
||||
**
|
||||
** Returns TRUE if the pseudo belongs to a finalized link.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleGetConnIdentityByPseudo(BD_ADDR pseudo, BD_ADDR peer, BD_ADDR local,
|
||||
UINT8 *peer_type, UINT8 *local_type)
|
||||
{
|
||||
tBTM_BLE_CONN_IDENTITY ent;
|
||||
|
||||
if (!btm_ble_conn_identity_get_by_pseudo(pseudo, &ent) || !ent.local_ready) {
|
||||
return FALSE;
|
||||
}
|
||||
if (peer) {
|
||||
memcpy(peer, ent.id.peer, BD_ADDR_LEN);
|
||||
}
|
||||
if (local) {
|
||||
memcpy(local, ent.id.local, BD_ADDR_LEN);
|
||||
}
|
||||
if (peer_type) {
|
||||
*peer_type = ent.id.peer_type;
|
||||
}
|
||||
if (local_type) {
|
||||
*local_type = ent.id.local_type;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleComputePseudoForIdentity
|
||||
**
|
||||
** Description Recompute the deterministic Host pseudo for a (local, peer)
|
||||
** identity pair. Lets the app target a bond section by its
|
||||
** identity even when the link is no longer connected.
|
||||
*******************************************************************************/
|
||||
void BTM_BleComputePseudoForIdentity(BD_ADDR local, UINT8 local_type,
|
||||
BD_ADDR peer, UINT8 peer_type, BD_ADDR pseudo)
|
||||
{
|
||||
tBLE_CONN_IDENTITY id;
|
||||
memset(&id, 0, sizeof(id));
|
||||
memcpy(id.local, local, BD_ADDR_LEN);
|
||||
memcpy(id.peer, peer, BD_ADDR_LEN);
|
||||
id.local_type = local_type;
|
||||
id.peer_type = peer_type;
|
||||
btm_ble_identity_to_pseudo(&id, pseudo);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleMarkPseudoBond
|
||||
**
|
||||
** Description Tag the device record for bd_addr as a pseudo-address bond
|
||||
** so the BTM_LE_KEY_PID handler keeps its pseudo bd_addr and
|
||||
** skips consolidation while loading bonds from NVS.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleMarkPseudoBond(BD_ADDR bd_addr)
|
||||
{
|
||||
tBTM_SEC_DEV_REC *p_rec = btm_find_dev(bd_addr);
|
||||
if (p_rec == NULL) {
|
||||
BLE_PSEUDO_DBG("mark pseudo bond: no rec for " BLE_PSEUDO_BDA_FMT, BLE_PSEUDO_BDA(bd_addr));
|
||||
return FALSE;
|
||||
}
|
||||
p_rec->ble.is_pseudo_bond = TRUE;
|
||||
BLE_PSEUDO_DBG("mark pseudo bond: " BLE_PSEUDO_BDA_FMT, BLE_PSEUDO_BDA(bd_addr));
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void BTM_ReadConnectionAddr (BD_ADDR remote_bda, BD_ADDR local_conn_addr, tBLE_ADDR_TYPE *p_addr_type)
|
||||
{
|
||||
tACL_CONN *p_acl = btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE);
|
||||
@@ -458,7 +562,11 @@ void BTM_BleConfirmReply (BD_ADDR bd_addr, UINT8 res)
|
||||
return;
|
||||
}
|
||||
|
||||
p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED;
|
||||
/* Only mark the link as authenticated when the user accepts the comparison;
|
||||
* a rejected/failed confirm must not raise the security level. */
|
||||
if (res_smp == SMP_SUCCESS) {
|
||||
p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED;
|
||||
}
|
||||
BTM_TRACE_DEBUG ("%s\n", __func__);
|
||||
SMP_ConfirmReply(bd_addr, res_smp);
|
||||
}
|
||||
@@ -488,6 +596,13 @@ void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data)
|
||||
BTM_TRACE_ERROR("BTM_BleOobDataReply() to Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore OOB data supplied for a device other than the one currently pairing. */
|
||||
if (memcmp(bd_addr, smp_cb.pairing_bda, BD_ADDR_LEN) != 0) {
|
||||
BTM_TRACE_ERROR("BTM_BleOobDataReply() - Wrong BD Addr");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res_smp == SMP_SUCCESS) {
|
||||
p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED;
|
||||
}
|
||||
@@ -971,10 +1086,6 @@ tBTM_SEC_ACTION btm_ble_determine_security_act(BOOLEAN is_originator, BD_ADDR bd
|
||||
return BTM_SEC_ENC_PENDING;
|
||||
}
|
||||
|
||||
if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_NONE) {
|
||||
return BTM_SEC_OK;
|
||||
}
|
||||
|
||||
UINT8 sec_flag = 0;
|
||||
BTM_GetSecurityFlagsByTransport(bdaddr, &sec_flag, BT_TRANSPORT_LE);
|
||||
|
||||
@@ -1044,6 +1155,12 @@ BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originat
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (btm_find_dev(bd_addr) == NULL) {
|
||||
BTM_TRACE_ERROR ("%s no device record for bd_addr=" MACSTR, __func__, MAC2STR(bd_addr));
|
||||
(*p_callback) (bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_UNKNOWN_ADDR);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tBTM_SEC_ACTION sec_act = btm_ble_determine_security_act(is_originator,
|
||||
bd_addr, p_serv_rec->security_flags);
|
||||
|
||||
@@ -1281,10 +1398,36 @@ void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY
|
||||
p_rec->ble.static_addr_type = p_keys->pid_key.addr_type;
|
||||
p_rec->ble.key_type |= BTM_LE_KEY_PID;
|
||||
BTM_TRACE_DEBUG("BTM_LE_KEY_PID key_type=0x%x save peer IRK", p_rec->ble.key_type);
|
||||
/* update device record address as static address */
|
||||
memcpy(p_rec->bd_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN);
|
||||
/* combine DUMO device security record if needed */
|
||||
btm_consolidate_dev(p_rec);
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* A pseudo bond record must NEVER be consolidated onto the peer
|
||||
* Identity, otherwise two local identities of the same phone (which
|
||||
* share the peer IRK / static_addr) collapse into a single device
|
||||
* record and overwrite each other's LTK. Detect it two ways:
|
||||
* 1) an active dual-identity link: the side table has this handle;
|
||||
* 2) an NVS-loaded bond marked as pseudo: BTA_DmAddBleDevice queued
|
||||
* is_pseudo_bond=TRUE and bta_dm_add_ble_device called
|
||||
* BTM_BleMarkPseudoBond() before this PID was added. This is the
|
||||
* authoritative signal (no live connection exists at boot). */
|
||||
if (!btm_ble_conn_identity_exists_by_handle(p_rec->ble_hci_handle) &&
|
||||
!p_rec->ble.is_pseudo_bond)
|
||||
#endif
|
||||
{
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BLE_PSEUDO_DBG("PID: handle=0x%x NOT a pseudo bond -> overwrite bd_addr + consolidate (default)",
|
||||
p_rec->ble_hci_handle);
|
||||
#endif
|
||||
/* update device record address as static address */
|
||||
memcpy(p_rec->bd_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN);
|
||||
/* combine DUMO device security record if needed */
|
||||
btm_consolidate_dev(p_rec);
|
||||
}
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
else {
|
||||
BLE_PSEUDO_DBG("PID: handle=0x%x IS a pseudo bond -> keep bd_addr=" BLE_PSEUDO_BDA_FMT
|
||||
", skip consolidate (LTK isolated)",
|
||||
p_rec->ble_hci_handle, BLE_PSEUDO_BDA(p_rec->bd_addr));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case BTM_LE_KEY_PCSRK:
|
||||
@@ -1862,12 +2005,13 @@ UINT8 btm_ble_br_keys_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data)
|
||||
** air for THIS connection and causes SMP c1 / f5 / f6
|
||||
** to compute the wrong local address (pair fail 0x04).
|
||||
**
|
||||
** RPA paths (own_addr_type 0x02, or 0x03 with a valid
|
||||
** RPA paths (own_addr_type 0x02 or 0x03 with a valid
|
||||
** local RPA in the LE Enhanced Connection Complete event)
|
||||
** are left untouched. For 0x03 when the controller falls
|
||||
** back to per-set identity (zero local_rpa), replace the
|
||||
** global private_addr written by
|
||||
** btm_ble_refresh_local_resolvable_private_addr().
|
||||
** are left untouched. When the controller falls back to
|
||||
** identity (zero local_rpa) the global private_addr written
|
||||
** by btm_ble_refresh_local_resolvable_private_addr() is
|
||||
** replaced: for 0x03 with the per-set static random, and for
|
||||
** 0x02 with the public identity address.
|
||||
**
|
||||
** No-op when no ext-adv instance matches the handle
|
||||
** (initiator role or legacy adv).
|
||||
@@ -1912,6 +2056,15 @@ void btm_ble_adjust_conn_addr_for_ext_adv(UINT16 handle)
|
||||
memcpy(p_acl->conn_addr,
|
||||
extend_adv_cb.inst[inst].rand_addr,
|
||||
BD_ADDR_LEN);
|
||||
} else if (on_air_type == BLE_ADDR_PUBLIC_ID &&
|
||||
!BTM_BLE_IS_RESOLVE_BDA(p_acl->conn_addr)) {
|
||||
/* Identity fallback: controller used the public identity, not an RPA.
|
||||
* The RPA path (conn_addr already holds a valid local RPA) is left
|
||||
* untouched by the IS_RESOLVE_BDA guard, mirroring the 0x03 case. */
|
||||
p_acl->conn_addr_type = BLE_ADDR_PUBLIC;
|
||||
memcpy(p_acl->conn_addr,
|
||||
controller_get_interface()->get_address()->address,
|
||||
BD_ADDR_LEN);
|
||||
}
|
||||
|
||||
BTM_TRACE_DEBUG("%s: handle=0x%04x inst=%u type=%u addr=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
@@ -1921,6 +2074,69 @@ void btm_ble_adjust_conn_addr_for_ext_adv(UINT16 handle)
|
||||
}
|
||||
#endif /* (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE) && (CONTROLLER_RPA_LIST_ENABLE == TRUE) */
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_bringup_conn
|
||||
**
|
||||
** Description Shared peripheral connection-completion path for the
|
||||
** pseudo-address bond feature, used by both the synchronous
|
||||
** btm_ble_conn_complete() branch and the asynchronous RPA
|
||||
** resolution callback. It derives the Host pseudo from the
|
||||
** (local, peer-identity) pair and brings the link up on that
|
||||
** pseudo, or keeps the real peer (fallback_bda) when the local
|
||||
** identity is not yet resolvable (deferred to adv-terminate).
|
||||
**
|
||||
** When air_peer is non-NULL the ACL active_remote_addr is
|
||||
** restored to the real on-air RPA so SC pairing f5/f6 stays
|
||||
** valid after host RPA resolution rewrote bda to the pseudo.
|
||||
**
|
||||
** The address actually used to index the ACL / device record
|
||||
** is written to conn_index_bda_out. tag only labels the trace.
|
||||
*******************************************************************************/
|
||||
static void btm_ble_pseudo_bringup_conn(UINT16 handle, UINT8 role,
|
||||
const BD_ADDR hash_peer, UINT8 hash_peer_type,
|
||||
const BD_ADDR fallback_bda, UINT8 bda_type,
|
||||
UINT16 conn_interval, UINT16 conn_latency,
|
||||
UINT16 conn_timeout, BOOLEAN match,
|
||||
const UINT8 *air_peer, UINT8 air_peer_type,
|
||||
const char *tag, BD_ADDR conn_index_bda_out)
|
||||
{
|
||||
BD_ADDR pseudo;
|
||||
|
||||
if (role == HCI_ROLE_SLAVE &&
|
||||
btm_ble_make_conn_pseudo(handle, (UINT8 *)hash_peer, hash_peer_type, pseudo)) {
|
||||
memcpy(conn_index_bda_out, pseudo, BD_ADDR_LEN);
|
||||
BLE_PSEUDO_DBG("conn_complete[%s]: keyed handle=0x%x peer=" BLE_PSEUDO_BDA_FMT
|
||||
" -> pseudo=" BLE_PSEUDO_BDA_FMT,
|
||||
tag, handle, BLE_PSEUDO_BDA(hash_peer), BLE_PSEUDO_BDA(pseudo));
|
||||
} else {
|
||||
memcpy(conn_index_bda_out, fallback_bda, BD_ADDR_LEN);
|
||||
if (role == HCI_ROLE_SLAVE) {
|
||||
BLE_PSEUDO_DBG("conn_complete[%s]: local NOT ready, defer to adv-terminate; handle=0x%x peer="
|
||||
BLE_PSEUDO_BDA_FMT, tag, handle, BLE_PSEUDO_BDA(fallback_bda));
|
||||
}
|
||||
}
|
||||
|
||||
btm_ble_connected(conn_index_bda_out, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match);
|
||||
l2cble_conn_comp(handle, role, conn_index_bda_out, bda_type, conn_interval,
|
||||
conn_latency, conn_timeout);
|
||||
|
||||
/* Host RPA resolution replaced the on-air RPA with a stored pseudo on the
|
||||
* ACL. Restore the real on-air peer address so SC pairing f5/f6 uses what
|
||||
* the peer actually put on air (otherwise the DHKey check fails on a
|
||||
* resolved reconnect). The pseudo stays the dev_rec index. */
|
||||
if (air_peer != NULL && role == HCI_ROLE_SLAVE) {
|
||||
tACL_CONN *p_air = btm_handle_to_acl(handle);
|
||||
if (p_air != NULL && BTM_BLE_IS_RESOLVE_BDA(air_peer)) {
|
||||
memcpy(p_air->active_remote_addr, air_peer, BD_ADDR_LEN);
|
||||
p_air->active_remote_addr_type = air_peer_type;
|
||||
BLE_PSEUDO_DBG("force air addr: handle=0x%x active_remote=" BLE_PSEUDO_BDA_FMT " type %u",
|
||||
handle, BLE_PSEUDO_BDA(air_peer), air_peer_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* BLE_PERIPH_PSEUDO_ADDR_BOND */
|
||||
|
||||
#if (BLE_PRIVACY_SPT == TRUE )
|
||||
/*******************************************************************************
|
||||
**
|
||||
@@ -1940,6 +2156,10 @@ static void btm_ble_resolve_random_addr_on_conn_cmpl(void *p_rec, void *p_data)
|
||||
BD_ADDR bda, local_rpa, peer_rpa;
|
||||
UINT16 conn_interval, conn_latency, conn_timeout;
|
||||
BOOLEAN match = FALSE;
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BD_ADDR air_peer; /* on-air peer address (RPA) before resolution rewrite */
|
||||
UINT8 air_peer_type;
|
||||
#endif
|
||||
|
||||
++p;
|
||||
STREAM_TO_UINT16 (handle, p);
|
||||
@@ -1959,6 +2179,14 @@ static void btm_ble_resolve_random_addr_on_conn_cmpl(void *p_rec, void *p_data)
|
||||
handle = HCID_GET_HANDLE (handle);
|
||||
BTM_TRACE_EVENT ("%s\n", __func__);
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Snapshot the real on-air peer address (the RPA the controller reported)
|
||||
* BEFORE host RPA resolution rewrites bda to a stored pseudo_addr. SC
|
||||
* pairing f5/f6 must use this real on-air address, not the pseudo. */
|
||||
memcpy(air_peer, bda, BD_ADDR_LEN);
|
||||
air_peer_type = bda_type;
|
||||
#endif
|
||||
|
||||
if (match_rec) {
|
||||
BTM_TRACE_DEBUG("%s matched and resolved random address", __func__);
|
||||
match = TRUE;
|
||||
@@ -1974,10 +2202,38 @@ static void btm_ble_resolve_random_addr_on_conn_cmpl(void *p_rec, void *p_data)
|
||||
BTM_TRACE_DEBUG("%s unable to match and resolve random address", __func__);
|
||||
}
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
{
|
||||
BD_ADDR conn_bda;
|
||||
BD_ADDR hash_peer;
|
||||
UINT8 hash_peer_type = air_peer_type;
|
||||
|
||||
/* Derive the pseudo from the PEER IDENTITY, never from a transient RPA
|
||||
* or the stored pseudo_addr. bda may have been rewritten to the old
|
||||
* pseudo above; use air_peer as the on-air fallback. */
|
||||
btm_ble_pseudo_pick_peer_identity(match_rec, air_peer, air_peer_type, hash_peer, &hash_peer_type);
|
||||
|
||||
/* Bring the link up on the real on-air address (air_peer), NOT the
|
||||
* possibly-rewritten bda. When the peer is already bonded under another
|
||||
* local identity, btm_ble_init_pseudo_addr() above rewrites bda to that
|
||||
* other identity's stored pseudo; using it as the deferred fallback
|
||||
* would make this second link collide with the first link's LCB / GATT
|
||||
* TCB (same remote_bd_addr) instead of getting its own, so the app would
|
||||
* never receive a CONNECT event for the second identity. air_peer is the
|
||||
* unique on-air address; finalize re-keys it to f(local, peer) at
|
||||
* adv-terminate. air_peer is also passed (last two real args) so the ACL
|
||||
* active_remote_addr is restored to the real on-air RPA for SC f5/f6. */
|
||||
btm_ble_pseudo_bringup_conn(handle, role, hash_peer, hash_peer_type,
|
||||
air_peer, air_peer_type, conn_interval, conn_latency,
|
||||
conn_timeout, match, air_peer, air_peer_type,
|
||||
"rpa", conn_bda);
|
||||
}
|
||||
#else
|
||||
btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match);
|
||||
|
||||
l2cble_conn_comp (handle, role, bda, bda_type, conn_interval,
|
||||
conn_latency, conn_timeout);
|
||||
#endif
|
||||
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE) && (CONTROLLER_RPA_LIST_ENABLE == TRUE)
|
||||
/* Multi-ADV: fix up p_acl->conn_addr / conn_addr_type from per-set state. */
|
||||
@@ -2024,11 +2280,44 @@ void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Dual local-identity: a phone connecting through a SECOND local identity
|
||||
* exposes the SAME peer IRK, so btm_find_dev(bda) resolves the on-air RPA to
|
||||
* the FIRST identity's record, which belongs to a different, still-connected
|
||||
* handle. Reusing it here would steal that live link's record (overwrite its
|
||||
* ble_hci_handle and pseudo_addr) and break its encrypted session. Allocate a
|
||||
* fresh record instead; this deferred link is re-keyed to its own
|
||||
* f(local, peer) pseudo at adv-terminate finalize. */
|
||||
tBTM_SEC_DEV_REC *no_hijack_exclude = NULL;
|
||||
if (role == HCI_ROLE_SLAVE && p_dev_rec &&
|
||||
p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE &&
|
||||
p_dev_rec->ble_hci_handle != handle &&
|
||||
btm_handle_to_acl(p_dev_rec->ble_hci_handle) != NULL) {
|
||||
BLE_PSEUDO_DBG("connected: " BLE_PSEUDO_BDA_FMT " resolves to live handle 0x%x (rec %p);"
|
||||
" alloc fresh rec for handle 0x%x (no hijack)",
|
||||
BLE_PSEUDO_BDA(bda), p_dev_rec->ble_hci_handle, p_dev_rec, handle);
|
||||
/* Keep the live record we just refused to hijack out of the recycle
|
||||
* pool: when the device table is full btm_sec_alloc_dev() would call
|
||||
* btm_find_oldest_dev_ex(NULL) and could pick this very record (it does
|
||||
* not check for an active ACL), memset it and destroy the first
|
||||
* identity's keys/handle. Exclude it explicitly, mirroring
|
||||
* btm_ble_pseudo_finalize_local(). */
|
||||
no_hijack_exclude = p_dev_rec;
|
||||
p_dev_rec = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!p_dev_rec) {
|
||||
/* There is no device record for new connection. Allocate one */
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
if ((p_dev_rec = btm_sec_alloc_dev_ex (bda, no_hijack_exclude)) == NULL) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if ((p_dev_rec = btm_sec_alloc_dev (bda)) == NULL) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else { /* Update the timestamp for this device */
|
||||
p_dev_rec->timestamp = btm_cb.dev_rec_count++;
|
||||
}
|
||||
@@ -2060,6 +2349,356 @@ void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role,
|
||||
return;
|
||||
}
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_resolve_conn_local
|
||||
**
|
||||
** Description Resolve the local identity (Public or fixed Static Random)
|
||||
** that produced this peripheral connection from its ext-adv
|
||||
** instance. Returns TRUE and fills id->local / local_type
|
||||
** when the ext-adv instance is resolvable for the handle.
|
||||
*******************************************************************************/
|
||||
static BOOLEAN btm_ble_resolve_conn_local(UINT16 handle, tBLE_CONN_IDENTITY *id)
|
||||
{
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE) && (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
UINT8 inst = BTM_BleGetExtAdvInstByConHandle(handle);
|
||||
if (inst < MAX_BLE_ADV_INSTANCE) {
|
||||
tBLE_ADDR_TYPE own = extend_adv_cb.inst[inst].own_addr_type;
|
||||
if (own == BLE_ADDR_PUBLIC || own == BLE_ADDR_PUBLIC_ID) {
|
||||
memcpy(id->local, controller_get_interface()->get_address()->address, BD_ADDR_LEN);
|
||||
id->local_type = BLE_ADDR_PUBLIC;
|
||||
return TRUE;
|
||||
} else if ((own == BLE_ADDR_RANDOM || own == BLE_ADDR_RANDOM_ID) &&
|
||||
extend_adv_cb.inst[inst].rand_addr_set) {
|
||||
memcpy(id->local, extend_adv_cb.inst[inst].rand_addr, BD_ADDR_LEN);
|
||||
id->local_type = BLE_ADDR_RANDOM;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED(handle);
|
||||
UNUSED(id);
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_make_conn_pseudo
|
||||
**
|
||||
** Description Resolve the local identity, derive the Host pseudo and
|
||||
** register the (handle -> pseudo, identity) side table.
|
||||
** Returns TRUE and fills pseudo_out when a usable local
|
||||
** identity is known; FALSE if the ext-adv instance is not
|
||||
** yet resolvable (the connection then keeps using the real
|
||||
** peer until btm_ble_pseudo_finalize_local() at adv-terminate).
|
||||
*******************************************************************************/
|
||||
static BOOLEAN btm_ble_make_conn_pseudo(UINT16 handle, BD_ADDR real_peer,
|
||||
tBLE_ADDR_TYPE peer_type, BD_ADDR pseudo_out)
|
||||
{
|
||||
tBLE_CONN_IDENTITY id;
|
||||
|
||||
memset(&id, 0, sizeof(id));
|
||||
memcpy(id.peer, real_peer, BD_ADDR_LEN);
|
||||
id.peer_type = peer_type;
|
||||
|
||||
if (!btm_ble_resolve_conn_local(handle, &id)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
btm_ble_identity_to_pseudo(&id, pseudo_out);
|
||||
/* The pseudo is already traced by btm_ble_identity_to_pseudo() and
|
||||
* btm_ble_conn_identity_register() via BLE_PSEUDO_DBG. */
|
||||
return btm_ble_conn_identity_register(handle, &id, pseudo_out, TRUE);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_rekey_link
|
||||
**
|
||||
** Description Re-key the whole per-link chain (GATT TCB, L2CAP LCB, ACL,
|
||||
** device record) from its current index address to the given
|
||||
** pseudo, consistently. ACL active_remote_addr (real on-air
|
||||
** address) is left untouched so SMP cryptography stays valid.
|
||||
*******************************************************************************/
|
||||
static void btm_ble_pseudo_rekey_link(UINT16 handle, tACL_CONN *p_acl, const BD_ADDR pseudo)
|
||||
{
|
||||
if (p_acl == NULL || memcmp(p_acl->remote_addr, pseudo, BD_ADDR_LEN) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLE_PSEUDO_DBG("re-key: handle=0x%x " BLE_PSEUDO_BDA_FMT " -> " BLE_PSEUDO_BDA_FMT,
|
||||
handle, BLE_PSEUDO_BDA(p_acl->remote_addr), BLE_PSEUDO_BDA(pseudo));
|
||||
|
||||
tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(p_acl->remote_addr, BT_TRANSPORT_LE);
|
||||
if (p_tcb) {
|
||||
memcpy(p_tcb->peer_bda, pseudo, BD_ADDR_LEN);
|
||||
}
|
||||
|
||||
tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle);
|
||||
if (p_lcb) {
|
||||
memcpy(p_lcb->remote_bd_addr, pseudo, BD_ADDR_LEN);
|
||||
}
|
||||
|
||||
tBTM_SEC_DEV_REC *p_rec = btm_find_dev_by_handle(handle);
|
||||
if (p_rec) {
|
||||
memcpy(p_rec->bd_addr, pseudo, BD_ADDR_LEN);
|
||||
memcpy(p_rec->ble.pseudo_addr, pseudo, BD_ADDR_LEN);
|
||||
|
||||
/* Remove any OTHER device record that still carries this same pseudo
|
||||
* (a stale duplicate left by an earlier pairing of the same
|
||||
* (local,peer)). Keeping it would let btm_find_dev() return the stale
|
||||
* record with an old LTK on a later encrypted reconnect -> MIC failure.
|
||||
* Only exact-pseudo duplicates are removed, so other local identities
|
||||
* (different pseudo) are never touched. */
|
||||
list_node_t *p_node = list_begin(btm_cb.p_sec_dev_rec_list);
|
||||
while (p_node) {
|
||||
tBTM_SEC_DEV_REC *p_dup = list_node(p_node);
|
||||
p_node = list_next(p_node);
|
||||
if (p_dup != p_rec && (p_dup->sec_flags & BTM_SEC_IN_USE) &&
|
||||
memcmp(p_dup->bd_addr, pseudo, BD_ADDR_LEN) == 0) {
|
||||
BLE_PSEUDO_DBG("re-key: drop stale dup rec %p for pseudo " BLE_PSEUDO_BDA_FMT,
|
||||
p_dup, BLE_PSEUDO_BDA(pseudo));
|
||||
/* Use the canonical free path so the stale LTK / BLE keys are
|
||||
* zeroed before the record memory is released; it also removes
|
||||
* the record from the list once BTM_SEC_IN_USE is cleared. */
|
||||
btm_sec_free_dev(p_dup, BT_TRANSPORT_LE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(p_acl->remote_addr, pseudo, BD_ADDR_LEN);
|
||||
BLE_PSEUDO_DBG("re-key: done handle=0x%x tcb=%p lcb=%p rec=%p", handle, p_tcb, p_lcb, p_rec);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_pick_peer_identity
|
||||
**
|
||||
** Description Choose the STABLE peer identity to feed into the pseudo
|
||||
** hash. A phone that connects with an RPA exposes a different
|
||||
** address on every connection, so hashing the on-air address
|
||||
** would make the pseudo (and therefore the bond key) drift on
|
||||
** every reconnect. Prefer the resolved IRK Identity Address
|
||||
** (ble.static_addr, learned from SMP Identity / PID) and only
|
||||
** fall back to the on-air address before pairing.
|
||||
*******************************************************************************/
|
||||
static void btm_ble_pseudo_pick_peer_identity(tBTM_SEC_DEV_REC *p_rec, const BD_ADDR on_air,
|
||||
UINT8 on_air_type,
|
||||
BD_ADDR peer_out, UINT8 *p_peer_type)
|
||||
{
|
||||
const BD_ADDR zero = {0};
|
||||
if (p_rec && (p_rec->ble.key_type & BTM_LE_KEY_PID) &&
|
||||
memcmp(p_rec->ble.static_addr, zero, BD_ADDR_LEN) != 0) {
|
||||
memcpy(peer_out, p_rec->ble.static_addr, BD_ADDR_LEN);
|
||||
*p_peer_type = p_rec->ble.static_addr_type;
|
||||
BLE_PSEUDO_DBG("pick_peer: use IDENTITY " BLE_PSEUDO_BDA_FMT " (type %u, key_type=0x%x)",
|
||||
BLE_PSEUDO_BDA(peer_out), *p_peer_type, p_rec->ble.key_type);
|
||||
} else {
|
||||
memcpy(peer_out, on_air, BD_ADDR_LEN);
|
||||
*p_peer_type = on_air_type;
|
||||
BLE_PSEUDO_DBG("pick_peer: use ON-AIR " BLE_PSEUDO_BDA_FMT " (no PID yet; rec=%p key_type=0x%x)",
|
||||
BLE_PSEUDO_BDA(peer_out), p_rec, p_rec ? p_rec->ble.key_type : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_finalize_local
|
||||
**
|
||||
** Description Second-phase finalize, called from the LE Advertising Set
|
||||
** Terminated handler. When the ext-adv instance was not yet
|
||||
** resolvable at LE Connection Complete, the link was kept on
|
||||
** the real peer address. Now that ter_con_handle is set the
|
||||
** instance is known: derive the pseudo and re-key the link.
|
||||
*******************************************************************************/
|
||||
void btm_ble_pseudo_finalize_local(UINT16 handle)
|
||||
{
|
||||
tBTM_BLE_CONN_IDENTITY ent;
|
||||
|
||||
if (btm_ble_conn_identity_get_by_handle(handle, &ent) && ent.local_ready) {
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x already keyed, skip", handle);
|
||||
return; /* already keyed at connection complete */
|
||||
}
|
||||
|
||||
tACL_CONN *p_acl = btm_handle_to_acl(handle);
|
||||
if (p_acl == NULL || p_acl->transport != BT_TRANSPORT_LE ||
|
||||
p_acl->link_role != HCI_ROLE_SLAVE) {
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x no LE slave ACL, skip (p_acl=%p)", handle, p_acl);
|
||||
return;
|
||||
}
|
||||
|
||||
tBTM_SEC_DEV_REC *p_cur = btm_find_dev_by_handle(handle);
|
||||
|
||||
/* Pick the record that carries the peer Identity. Normally that is p_cur,
|
||||
* but when btm_ble_connected() took the no-hijack path it allocated a FRESH,
|
||||
* key-less record for this handle (because the on-air RPA IRK-resolved to
|
||||
* ANOTHER live identity's bonded record). That fresh record has no PID, so
|
||||
* feeding it to pick_peer would fall back to the transient on-air RPA and
|
||||
* derive the WRONG pseudo - on an already-bonded reconnect there is no SMP
|
||||
* pairing to re-key it, so the stored LTK is never found and the link fails.
|
||||
* Recover the stable Identity by IRK-resolving the on-air RPA against the
|
||||
* bonded records (all of this phone's local-identity bonds share one IRK /
|
||||
* Identity). The local identity still comes from the adv set below, so any
|
||||
* matching bond yields the correct (local, Identity) pseudo. */
|
||||
tBTM_SEC_DEV_REC *p_id_rec = p_cur;
|
||||
if (p_id_rec == NULL || !(p_id_rec->ble.key_type & BTM_LE_KEY_PID)) {
|
||||
list_node_t *p_node = list_begin(btm_cb.p_sec_dev_rec_list);
|
||||
while (p_node) {
|
||||
tBTM_SEC_DEV_REC *p_r = list_node(p_node);
|
||||
p_node = list_next(p_node);
|
||||
if (p_r != p_cur && (p_r->sec_flags & BTM_SEC_IN_USE) &&
|
||||
(p_r->ble.key_type & BTM_LE_KEY_PID) &&
|
||||
btm_ble_addr_resolvable(p_acl->active_remote_addr, p_r)) {
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x recover identity from bonded rec %p (cur %p has no PID)",
|
||||
handle, p_r, p_cur);
|
||||
p_id_rec = p_r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tBLE_CONN_IDENTITY id;
|
||||
memset(&id, 0, sizeof(id));
|
||||
/* Hash on the stable peer identity (static_addr) once known; this keeps the
|
||||
* pseudo constant across the peer's RPA rotation. Before pairing (first
|
||||
* ever connection) only the on-air RPA is known; PID will re-key later. */
|
||||
btm_ble_pseudo_pick_peer_identity(p_id_rec, p_acl->active_remote_addr, p_acl->active_remote_addr_type,
|
||||
id.peer, &id.peer_type);
|
||||
|
||||
if (!btm_ble_resolve_conn_local(handle, &id)) {
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x local STILL unknown, give up", handle);
|
||||
return; /* instance still unknown; nothing we can do */
|
||||
}
|
||||
|
||||
BD_ADDR pseudo;
|
||||
btm_ble_identity_to_pseudo(&id, pseudo);
|
||||
|
||||
if (!btm_ble_conn_identity_register(handle, &id, pseudo, TRUE)) {
|
||||
BTM_TRACE_ERROR("%s: handle=0x%x side-table full, disconnect to avoid re-key without tracking",
|
||||
__func__, handle);
|
||||
btm_sec_disconnect(handle, HCI_ERR_HOST_REJECT_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Bind this link to the device record that belongs to `pseudo` WITHOUT
|
||||
* hijacking another local identity's bonded record (a phone connecting to
|
||||
* a second local identity resolves to the first identity's record via its
|
||||
* shared IRK, so btm_ble_connected() may have reused the wrong record). At
|
||||
* this point pairing has not started, so no BLE keys can be lost. If the
|
||||
* current record only holds a classic link key, allocate a separate entry
|
||||
* so in-place re-key does not overwrite bd_addr and orphan the BR/EDR bond.
|
||||
*/
|
||||
tBTM_SEC_DEV_REC *p_tgt = btm_find_dev(pseudo);
|
||||
if (p_tgt && p_tgt != p_cur) {
|
||||
if (p_cur) {
|
||||
p_cur->ble_hci_handle = BTM_SEC_INVALID_HANDLE;
|
||||
p_tgt->ble.ble_addr_type = p_cur->ble.ble_addr_type;
|
||||
/* p_cur was a fresh, key-less placeholder allocated by
|
||||
* btm_ble_connected()'s no-hijack path. Now that the link is bound
|
||||
* to the bonded target record, release the placeholder so it does
|
||||
* not linger as an orphan (BTM_SEC_IN_USE with an invalid handle and
|
||||
* a stale on-air RPA) consuming a device-record slot. Its
|
||||
* ble_addr_type was copied to p_tgt above. Guard on "no bond" so a
|
||||
* record that still holds BLE keys or a BR/EDR link key is never
|
||||
* destroyed (that case is handled by the separate-alloc branch). */
|
||||
if (!p_cur->ble.key_type &&
|
||||
!(p_cur->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
|
||||
btm_sec_free_dev(p_cur, BT_TRANSPORT_LE);
|
||||
p_cur = NULL;
|
||||
}
|
||||
} else {
|
||||
p_tgt->ble.ble_addr_type = p_acl->active_remote_addr_type;
|
||||
}
|
||||
p_tgt->ble_hci_handle = handle;
|
||||
p_tgt->device_type |= BT_DEVICE_TYPE_BLE;
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x bind to existing rec for pseudo", handle);
|
||||
} else if (p_tgt == NULL && p_cur &&
|
||||
(p_cur->ble.key_type || (p_cur->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) &&
|
||||
memcmp(p_cur->bd_addr, pseudo, BD_ADDR_LEN) != 0) {
|
||||
UINT8 saved_addr_type = p_cur->ble.ble_addr_type;
|
||||
tBTM_SEC_DEV_REC *p_new = btm_sec_alloc_dev_ex(pseudo, p_cur);
|
||||
if (p_new) {
|
||||
p_new->ble_hci_handle = handle;
|
||||
p_new->device_type |= BT_DEVICE_TYPE_BLE;
|
||||
p_new->ble.ble_addr_type = saved_addr_type;
|
||||
memcpy(p_new->ble.pseudo_addr, pseudo, BD_ADDR_LEN);
|
||||
if (p_new != p_cur) {
|
||||
p_cur->ble_hci_handle = BTM_SEC_INVALID_HANDLE;
|
||||
}
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x alloc separate rec for pseudo (no hijack)", handle);
|
||||
} else {
|
||||
BTM_TRACE_ERROR("%s: handle=0x%x alloc failed, disconnect to avoid cross-identity key corruption",
|
||||
__func__, handle);
|
||||
btm_sec_disconnect(handle, HCI_ERR_HOST_REJECT_RESOURCES);
|
||||
return;
|
||||
}
|
||||
} else if (p_tgt == NULL && p_cur == NULL) {
|
||||
/* A concurrent connection IRK-resolved to the same bonded record and
|
||||
* overwrote ble_hci_handle, orphaning this link. Allocate a fresh entry. */
|
||||
tBTM_SEC_DEV_REC *p_new = btm_sec_alloc_dev(pseudo);
|
||||
if (p_new) {
|
||||
p_new->ble_hci_handle = handle;
|
||||
p_new->device_type |= BT_DEVICE_TYPE_BLE;
|
||||
p_new->ble.ble_addr_type = p_acl->active_remote_addr_type;
|
||||
memcpy(p_new->ble.pseudo_addr, pseudo, BD_ADDR_LEN);
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x alloc rec for orphan link", handle);
|
||||
} else {
|
||||
BTM_TRACE_ERROR("%s: handle=0x%x alloc failed, disconnect to avoid missing sec record",
|
||||
__func__, handle);
|
||||
btm_sec_disconnect(handle, HCI_ERR_HOST_REJECT_RESOURCES);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
BLE_PSEUDO_DBG("finalize: handle=0x%x in-place key cur rec (tgt=%p cur=%p)", handle, p_tgt, p_cur);
|
||||
}
|
||||
|
||||
/* Re-key the address chain (GATT/LCB/ACL + the now-correct dev record). */
|
||||
btm_ble_pseudo_rekey_link(handle, p_acl, pseudo);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_apply_identity
|
||||
**
|
||||
** Description Called from SMP when the peer's Identity Address (PID) is
|
||||
** received during pairing. If the link was keyed earlier from
|
||||
** a transient RPA, re-derive the pseudo from the now known
|
||||
** stable identity and re-key the link in place (the in-flight
|
||||
** pairing keys stay in the same record). Returns TRUE and
|
||||
** fills new_pseudo when the pseudo changed, so the SMP caller
|
||||
** can update smp_cb.pairing_bda to keep pairing consistent.
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_pseudo_apply_identity(UINT16 handle, const BD_ADDR identity,
|
||||
UINT8 id_type, BD_ADDR new_pseudo)
|
||||
{
|
||||
tBTM_BLE_CONN_IDENTITY ent;
|
||||
const BD_ADDR zero = {0};
|
||||
|
||||
if (!btm_ble_conn_identity_get_by_handle(handle, &ent) ||
|
||||
identity == NULL || memcmp(identity, zero, BD_ADDR_LEN) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tBLE_CONN_IDENTITY id = ent.id;
|
||||
memcpy(id.peer, identity, BD_ADDR_LEN);
|
||||
id.peer_type = id_type;
|
||||
|
||||
btm_ble_identity_to_pseudo(&id, new_pseudo);
|
||||
if (memcmp(new_pseudo, ent.pseudo, BD_ADDR_LEN) == 0) {
|
||||
BLE_PSEUDO_DBG("apply_identity: handle=0x%x pseudo unchanged (already on identity)", handle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BLE_PSEUDO_DBG("apply_identity: handle=0x%x identity=" BLE_PSEUDO_BDA_FMT
|
||||
" re-key " BLE_PSEUDO_BDA_FMT " -> " BLE_PSEUDO_BDA_FMT,
|
||||
handle, BLE_PSEUDO_BDA(identity),
|
||||
BLE_PSEUDO_BDA(ent.pseudo), BLE_PSEUDO_BDA(new_pseudo));
|
||||
|
||||
if (!btm_ble_conn_identity_register(handle, &id, new_pseudo, ent.local_ready)) {
|
||||
BTM_TRACE_ERROR("%s: handle=0x%x side-table update failed, skip re-key", __func__, handle);
|
||||
return FALSE;
|
||||
}
|
||||
btm_ble_pseudo_rekey_link(handle, btm_handle_to_acl(handle), new_pseudo);
|
||||
return TRUE;
|
||||
}
|
||||
#endif /* BLE_PERIPH_PSEUDO_ADDR_BOND */
|
||||
|
||||
/*****************************************************************************
|
||||
** Function btm_ble_conn_complete
|
||||
**
|
||||
@@ -2077,6 +2716,12 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced)
|
||||
BD_ADDR local_rpa, peer_rpa;
|
||||
UINT16 conn_interval, conn_latency, conn_timeout;
|
||||
BOOLEAN match = FALSE;
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BD_ADDR pseudo_real_peer; /* controller-reported peer, before any pseudo_addr merge */
|
||||
UINT8 pseudo_peer_type; /* controller-reported peer type, before any rewrite */
|
||||
BOOLEAN pseudo_peer_valid = FALSE;
|
||||
BD_ADDR conn_index_bda; /* address actually used to index ACL/dev_rec (pseudo or real) */
|
||||
#endif
|
||||
UNUSED(evt_len);
|
||||
STREAM_TO_UINT8 (status, p);
|
||||
STREAM_TO_UINT16 (handle, p);
|
||||
@@ -2089,6 +2734,12 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced)
|
||||
if (enhanced) {
|
||||
STREAM_TO_BDADDR (local_rpa, p);
|
||||
STREAM_TO_BDADDR (peer_rpa, p);
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BLE_PSEUDO_DBG("conn_complete[enh]: handle=0x%x reported_peer=" BLE_PSEUDO_BDA_FMT
|
||||
" local_rpa=" BLE_PSEUDO_BDA_FMT " peer_rpa(on-air)=" BLE_PSEUDO_BDA_FMT,
|
||||
HCID_GET_HANDLE(handle), BLE_PSEUDO_BDA(bda),
|
||||
BLE_PSEUDO_BDA(local_rpa), BLE_PSEUDO_BDA(peer_rpa));
|
||||
#endif
|
||||
#if (CONTROLLER_RPA_LIST_ENABLE == TRUE)
|
||||
BD_ADDR dummy_bda = {0};
|
||||
/* For controller generates RPA, if resolving list contains no matching entry, it use identity address.
|
||||
@@ -2101,6 +2752,14 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Capture the controller-reported peer now: btm_identity_addr_to_random_pseudo()
|
||||
* may rewrite bda/bda_type for a bonded peer, which would make the
|
||||
* (local, peer) hash drift on reconnect. */
|
||||
pseudo_peer_type = bda_type;
|
||||
memcpy(pseudo_real_peer, bda, BD_ADDR_LEN);
|
||||
pseudo_peer_valid = TRUE;
|
||||
#endif
|
||||
#if (BLE_PRIVACY_SPT == TRUE )
|
||||
peer_addr_type = bda_type;
|
||||
match = btm_identity_addr_to_random_pseudo (bda, &bda_type, FALSE);
|
||||
@@ -2134,17 +2793,63 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced)
|
||||
STREAM_TO_UINT16 (conn_timeout, p);
|
||||
handle = HCID_GET_HANDLE (handle);
|
||||
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
{
|
||||
BD_ADDR hash_peer;
|
||||
UINT8 hash_peer_type;
|
||||
tBTM_SEC_DEV_REC *p_rec = NULL;
|
||||
|
||||
#if (BLE_PRIVACY_SPT == TRUE)
|
||||
if (match) {
|
||||
p_rec = btm_find_dev_by_identity_addr(pseudo_real_peer, pseudo_peer_type);
|
||||
}
|
||||
#endif
|
||||
/* Same stable-identity pick as the RPA async path and finalize. */
|
||||
btm_ble_pseudo_pick_peer_identity(p_rec,
|
||||
pseudo_peer_valid ? pseudo_real_peer : bda,
|
||||
pseudo_peer_type,
|
||||
hash_peer, &hash_peer_type);
|
||||
/* Bring the link up on the controller-reported peer captured
|
||||
* BEFORE btm_identity_addr_to_random_pseudo() rewrote bda. For a
|
||||
* peer already bonded under another local identity that rewrite
|
||||
* turns bda into the other identity's stored pseudo; using it as
|
||||
* the deferred fallback would collide this link's LCB / GATT TCB
|
||||
* with the already-connected identity (no CONNECT event, no
|
||||
* encryption). pseudo_real_peer is unique on-air; finalize re-keys
|
||||
* it to f(local, peer). No air_peer restore here: this branch did
|
||||
* not run host RPA resolution, so the index address already is the
|
||||
* real on-air address. */
|
||||
btm_ble_pseudo_bringup_conn(handle, role, hash_peer, hash_peer_type,
|
||||
pseudo_peer_valid ? pseudo_real_peer : bda,
|
||||
pseudo_peer_valid ? pseudo_peer_type : bda_type,
|
||||
conn_interval, conn_latency,
|
||||
conn_timeout, match, NULL, 0,
|
||||
"sync", conn_index_bda);
|
||||
}
|
||||
#else
|
||||
btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match);
|
||||
l2cble_conn_comp (handle, role, bda, bda_type, conn_interval,
|
||||
conn_latency, conn_timeout);
|
||||
#endif
|
||||
|
||||
#if (BLE_PRIVACY_SPT == TRUE)
|
||||
if (enhanced) {
|
||||
#if (BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Use the connection index address (pseudo when keyed) so the
|
||||
* ACL / device record lookups inside the refresh helpers hit
|
||||
* the right entry. */
|
||||
btm_ble_refresh_local_resolvable_private_addr(conn_index_bda, local_rpa);
|
||||
|
||||
if (peer_addr_type & BLE_ADDR_TYPE_ID_BIT) {
|
||||
btm_ble_refresh_peer_resolvable_private_addr(conn_index_bda, peer_rpa, BLE_ADDR_RANDOM);
|
||||
}
|
||||
#else
|
||||
btm_ble_refresh_local_resolvable_private_addr(bda, local_rpa);
|
||||
|
||||
if (peer_addr_type & BLE_ADDR_TYPE_ID_BIT) {
|
||||
btm_ble_refresh_peer_resolvable_private_addr(bda, peer_rpa, BLE_ADDR_RANDOM);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "btm_int.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "btm_ble_pseudo.h"
|
||||
#endif
|
||||
#include "stack/hcimsgs.h"
|
||||
#include "stack/hcidefs.h"
|
||||
#include "osi/allocator.h"
|
||||
@@ -625,7 +628,14 @@ tBTM_STATUS BTM_BleExtAdvSetRemove(UINT8 instance)
|
||||
extend_adv_cb.inst[instance].own_addr_type = BLE_ADDR_PUBLIC;
|
||||
extend_adv_cb.inst[instance].rand_addr_set = FALSE;
|
||||
memset(extend_adv_cb.inst[instance].rand_addr, 0, BD_ADDR_LEN);
|
||||
/* Fully reset the per-set record, consistent with BTM_BleExtAdvSetClear(). */
|
||||
adv_record[instance].ter_con_handle = INVALID_VALUE_16BIT;
|
||||
adv_record[instance].invalid = false;
|
||||
adv_record[instance].enabled = false;
|
||||
adv_record[instance].instance = INVALID_VALUE_8BIT;
|
||||
adv_record[instance].duration = INVALID_VALUE_32BIT;
|
||||
adv_record[instance].max_events = INVALID_VALUE_32BIT;
|
||||
adv_record[instance].retry_count = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -658,7 +668,18 @@ tBTM_STATUS BTM_BleExtAdvSetClear(void)
|
||||
extend_adv_cb.inst[i].own_addr_type = BLE_ADDR_PUBLIC;
|
||||
extend_adv_cb.inst[i].rand_addr_set = FALSE;
|
||||
memset(extend_adv_cb.inst[i].rand_addr, 0, BD_ADDR_LEN);
|
||||
/* Fully reset the per-set record, consistent with
|
||||
* btm_ble_advrecod_init() and the disable-all path. Resetting only
|
||||
* ter_con_handle would leave 'enabled' (and the rest) stale, making
|
||||
* btm_ble_ext_adv_active_count() report sets that the controller
|
||||
* has already removed. */
|
||||
adv_record[i].ter_con_handle = INVALID_VALUE_16BIT;
|
||||
adv_record[i].invalid = false;
|
||||
adv_record[i].enabled = false;
|
||||
adv_record[i].instance = INVALID_VALUE_8BIT;
|
||||
adv_record[i].duration = INVALID_VALUE_32BIT;
|
||||
adv_record[i].max_events = INVALID_VALUE_32BIT;
|
||||
adv_record[i].retry_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,6 +1293,14 @@ void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params)
|
||||
* after LE (Enhanced) Connection Complete. */
|
||||
#if (CONTROLLER_RPA_LIST_ENABLE == TRUE)
|
||||
btm_ble_adjust_conn_addr_for_ext_adv(adv_record[params->adv_handle].ter_con_handle);
|
||||
#endif
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* The ext-adv instance is now resolvable for this handle. If the link
|
||||
* could not be pseudo-keyed at connection complete (instance not yet
|
||||
* known), finalize it now so bond / LTK storage is isolated. */
|
||||
BLE_PSEUDO_DBG("adv_terminated: adv_handle=%u con_handle=0x%x -> finalize",
|
||||
params->adv_handle, adv_record[params->adv_handle].ter_con_handle);
|
||||
btm_ble_pseudo_finalize_local(adv_record[params->adv_handle].ter_con_handle);
|
||||
#endif
|
||||
} else {
|
||||
adv_record[params->adv_handle].ter_con_handle = INVALID_VALUE_16BIT;
|
||||
|
||||
@@ -859,6 +859,35 @@ void btm_ble_enqueue_direct_conn_req(void *p_param)
|
||||
}
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btm_ble_remove_direct_conn_req
|
||||
**
|
||||
** Description Remove a pending direct connection request for the given LCB.
|
||||
**
|
||||
** Returns None.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void btm_ble_remove_direct_conn_req(void *p_param)
|
||||
{
|
||||
fixed_queue_t *q = btm_cb.ble_ctr_cb.conn_pending_q;
|
||||
|
||||
if (q == NULL || p_param == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
list_t *list = fixed_queue_get_list(q);
|
||||
for (const list_node_t *node = list_begin(list); node != NULL; node = list_next(node)) {
|
||||
tBTM_BLE_CONN_REQ *p = (tBTM_BLE_CONN_REQ *)list_node(node);
|
||||
|
||||
if (p->p_param == p_param) {
|
||||
if (fixed_queue_try_remove_from_queue(q, p) != NULL) {
|
||||
osi_free(p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btm_send_pending_direct_conn
|
||||
**
|
||||
** Description This function send the pending direct connection request in queue
|
||||
@@ -873,7 +902,17 @@ BOOLEAN btm_send_pending_direct_conn(void)
|
||||
|
||||
p_req = (tBTM_BLE_CONN_REQ*)fixed_queue_dequeue(btm_cb.ble_ctr_cb.conn_pending_q, 0);
|
||||
if (p_req != NULL) {
|
||||
rt = l2cble_init_direct_conn((tL2C_LCB *)(p_req->p_param));
|
||||
tL2C_LCB *p_lcb = (tL2C_LCB *)(p_req->p_param);
|
||||
|
||||
if (p_lcb == NULL || !p_lcb->in_use) {
|
||||
osi_free((void *)p_req);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rt = l2cble_init_direct_conn(p_lcb);
|
||||
if (!rt) {
|
||||
l2cu_release_lcb(p_lcb);
|
||||
}
|
||||
|
||||
osi_free((void *)p_req);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
//#include "bt_utils.h"
|
||||
#include "btm_int.h"
|
||||
#include "stack/btm_ble_api.h"
|
||||
#include "btm_ble_pseudo.h"
|
||||
#include "stack/btu.h"
|
||||
#include "device/controller.h"
|
||||
#include "stack/hcimsgs.h"
|
||||
@@ -1841,7 +1842,7 @@ UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst,
|
||||
if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_DATA &&
|
||||
p_data && p_data->p_service_data && p_data->p_service_data->len != 0 && p_data->p_service_data->p_val) {
|
||||
if (len > (p_data->p_service_data->service_uuid.len + MIN_ADV_LENGTH)) {
|
||||
if (p_data->p_service_data->len > (len - MIN_ADV_LENGTH)) {
|
||||
if (p_data->p_service_data->len > (len - MIN_ADV_LENGTH - p_data->p_service_data->service_uuid.len)) {
|
||||
cp_len = len - MIN_ADV_LENGTH - p_data->p_service_data->service_uuid.len;
|
||||
} else {
|
||||
cp_len = p_data->p_service_data->len;
|
||||
@@ -2895,6 +2896,9 @@ void btm_send_sel_conn_callback(BD_ADDR remote_bda, UINT8 evt_type, UINT8 *p_dat
|
||||
}
|
||||
|
||||
if (p_dev_name) {
|
||||
if (len > sizeof(remname) - 1) {
|
||||
len = sizeof(remname) - 1;
|
||||
}
|
||||
memcpy(remname, p_dev_name, len);
|
||||
}
|
||||
}
|
||||
@@ -3018,6 +3022,10 @@ void btm_ble_process_adv_pkt (UINT8 *p_data, UINT8 evt_len)
|
||||
#endif
|
||||
/* Validate data_len before any path (callee reads 1 + data_len + 1 = data_len+2 bytes from p) */
|
||||
data_len = *p; /* read without advancing; p points to data_len byte */
|
||||
if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) {
|
||||
BTM_TRACE_ERROR("btm_ble_process_adv_pkt: legacy adv data_len %u exceeds max %u", data_len, BTM_BLE_ADV_DATA_LEN_MAX);
|
||||
break;
|
||||
}
|
||||
if (data_len + 2 > remaining - 8) {
|
||||
BTM_TRACE_ERROR("btm_ble_process_adv_pkt: data_len %u + data + rssi exceeds remaining %u", data_len, (UINT16)(remaining - 8));
|
||||
break;
|
||||
@@ -3944,6 +3952,10 @@ void btm_ble_init (void)
|
||||
#if (BLE_VENDOR_HCI_EN == TRUE)
|
||||
BTM_RegisterForVSEvents(btm_ble_vs_evt_callback, TRUE);
|
||||
#endif // #if (BLE_VENDOR_HCI_EN == TRUE)
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
btm_ble_pseudo_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -3969,6 +3981,10 @@ void btm_ble_free (void)
|
||||
osi_event_delete(p_cb->adv_rpt_ready);
|
||||
p_cb->adv_rpt_ready = NULL;
|
||||
#endif // #if (BLE_42_SCAN_EN == TRUE)
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
btm_ble_pseudo_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool enable_topology_check_flag = true;
|
||||
|
||||
@@ -330,6 +330,8 @@ tBTM_STATUS BTM_BleBigCreate(uint8_t big_handle, uint8_t adv_handle, uint8_t num
|
||||
return BTM_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
/* btsnd_hcic_ble_big_create() returns FALSE only when HCI_GET_CMD_BUF()
|
||||
* fails (out of memory). That path is not surfaced to the caller by design. */
|
||||
btsnd_hcic_ble_big_create(big_handle, adv_handle, num_bis, sdu_interval, max_sdu, max_transport_latency,
|
||||
rtn, phy, packing, framing, encryption, broadcast_code);
|
||||
|
||||
@@ -348,6 +350,7 @@ tBTM_STATUS BTM_BleBigCreateTest(uint8_t big_handle, uint8_t adv_handle, uint8_t
|
||||
return BTM_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
/* See BTM_BleBigCreate: HCI cmd buffer alloc failure is not checked. */
|
||||
btsnd_hcic_ble_big_create_test(big_handle, adv_handle, num_bis, sdu_interval, iso_interval, nse,
|
||||
max_sdu, max_pdu, phy, packing, framing, bn, irc, pto, encryption,
|
||||
broadcast_code);
|
||||
@@ -357,6 +360,7 @@ tBTM_STATUS BTM_BleBigCreateTest(uint8_t big_handle, uint8_t adv_handle, uint8_t
|
||||
tBTM_STATUS BTM_BleBigTerminate(UINT8 big_handle, UINT8 reason)
|
||||
{
|
||||
// event will be triggered in command status and complete event
|
||||
/* See BTM_BleBigCreate: HCI cmd buffer alloc failure is not checked. */
|
||||
btsnd_hcic_ble_big_terminate(big_handle, reason);
|
||||
return BTM_SUCCESS;
|
||||
}
|
||||
@@ -373,6 +377,7 @@ tBTM_STATUS BTM_BleBigSyncCreate(uint8_t big_handle, uint16_t sync_handle,
|
||||
return BTM_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
/* See BTM_BleBigCreate: HCI cmd buffer alloc failure is not checked. */
|
||||
btsnd_hcic_ble_big_sync_create(big_handle, sync_handle, encryption, bc_code,
|
||||
mse, big_sync_timeout, num_bis, bis);
|
||||
return BTM_SUCCESS;
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "common/bt_target.h"
|
||||
#include "common/bt_trace.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "btm_int.h"
|
||||
#include "btm_ble_pseudo.h"
|
||||
#include "osi/mutex.h"
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
|
||||
/* The pseudo is derived with AES-CMAC, the same SMP crypto primitive used by
|
||||
* c1/f5/f6. aes_cipher_msg_auth_code() is provided by stack/smp/smp_cmac.c for
|
||||
* ALL three configurable crypto backends (mbedtls/PSA, tinycrypt, stack-native,
|
||||
* selected by SMP_CRYPTO_MBEDTLS / SMP_CRYPTO_TINYCRYPT / SMP_CRYPTO_STACK_NATIVE),
|
||||
* so the derivation automatically follows the configured crypto library and
|
||||
* this module carries no library-specific code. */
|
||||
extern BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length,
|
||||
UINT16 tlen, UINT8 *p_signature);
|
||||
|
||||
/* Fixed 16-byte domain-separation key for the pseudo CMAC (not secret; the
|
||||
* pseudo is a Host-internal index, never sent on air). Do not change after
|
||||
* deployment without bumping BLE_PSEUDO_SCHEME_VER and migrating bonds. */
|
||||
static const UINT8 btm_ble_pseudo_cmac_key[16] = {
|
||||
'E', 'S', 'P', '_', 'B', 'L', 'E', '_', 'P', 'S', 'E', 'U', 'D', 'O', 'v', BLE_PSEUDO_SCHEME_VER
|
||||
};
|
||||
|
||||
/* The side table holds one entry per concurrent LE link. BTU updates it from
|
||||
* HCI/SMP paths; BTC and the public esp_ble_gap_* API read it. All accessors
|
||||
* take btm_ble_pseudo_mutex and copy data out before returning. */
|
||||
#define BTM_BLE_PSEUDO_MAX_CONN MAX_ACL_CONNECTIONS
|
||||
|
||||
static osi_mutex_t btm_ble_pseudo_mutex;
|
||||
static tBTM_BLE_CONN_IDENTITY btm_ble_conn_id_tab[BTM_BLE_PSEUDO_MAX_CONN];
|
||||
|
||||
static tBTM_BLE_CONN_IDENTITY *conn_identity_by_handle_locked(UINT16 handle)
|
||||
{
|
||||
for (int i = 0; i < BTM_BLE_PSEUDO_MAX_CONN; i++) {
|
||||
if (btm_ble_conn_id_tab[i].in_use && btm_ble_conn_id_tab[i].handle == handle) {
|
||||
return &btm_ble_conn_id_tab[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static tBTM_BLE_CONN_IDENTITY *conn_identity_by_pseudo_locked(const BD_ADDR pseudo)
|
||||
{
|
||||
for (int i = 0; i < BTM_BLE_PSEUDO_MAX_CONN; i++) {
|
||||
if (btm_ble_conn_id_tab[i].in_use &&
|
||||
memcmp(btm_ble_conn_id_tab[i].pseudo, pseudo, BD_ADDR_LEN) == 0) {
|
||||
return &btm_ble_conn_id_tab[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_identity_to_pseudo
|
||||
*******************************************************************************/
|
||||
void btm_ble_identity_to_pseudo(const tBLE_CONN_IDENTITY *p_id, BD_ADDR pseudo)
|
||||
{
|
||||
uint8_t in[1 + BD_ADDR_LEN + BD_ADDR_LEN];
|
||||
uint8_t cmac[16];
|
||||
BT_OCTET16 key;
|
||||
BOOLEAN hashed;
|
||||
|
||||
if (p_id == NULL || pseudo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
in[0] = BLE_PSEUDO_SCHEME_VER;
|
||||
memcpy(&in[1], p_id->local, BD_ADDR_LEN);
|
||||
memcpy(&in[1 + BD_ADDR_LEN], p_id->peer, BD_ADDR_LEN);
|
||||
|
||||
/* AES-CMAC(key, version || local || peer); follows the configured SMP
|
||||
* crypto backend (mbedtls/tinycrypt/native). */
|
||||
memcpy(key, btm_ble_pseudo_cmac_key, sizeof(key));
|
||||
hashed = aes_cipher_msg_auth_code(key, in, sizeof(in), sizeof(cmac), cmac);
|
||||
|
||||
if (!hashed) {
|
||||
/* Fall back to a deterministic non-crypto mix so we never emit a
|
||||
* zero / unstable pseudo; bring-up only, should not happen. */
|
||||
for (int i = 0; i < BD_ADDR_LEN; i++) {
|
||||
cmac[i] = in[1 + i] ^ in[1 + BD_ADDR_LEN + i] ^ BLE_PSEUDO_SCHEME_VER;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(pseudo, cmac, BD_ADDR_LEN);
|
||||
|
||||
/* Force Static-Random format (top two bits = 11). This is functionally
|
||||
* required: it keeps the pseudo out of the resolvable-RPA space so that
|
||||
* btm_find_dev()/btm_ble_addr_resolvable() can never misresolve it. */
|
||||
pseudo[0] |= 0xC0;
|
||||
|
||||
/* Avoid the all-ones broadcast pattern. */
|
||||
if (pseudo[0] == 0xFF && pseudo[1] == 0xFF && pseudo[2] == 0xFF &&
|
||||
pseudo[3] == 0xFF && pseudo[4] == 0xFF && pseudo[5] == 0xFF) {
|
||||
pseudo[0] = 0xC1;
|
||||
}
|
||||
|
||||
BLE_PSEUDO_DBG("derive: local(t%u) " BLE_PSEUDO_BDA_FMT " + peer(t%u) " BLE_PSEUDO_BDA_FMT
|
||||
" -> pseudo " BLE_PSEUDO_BDA_FMT " (hashed=%d)",
|
||||
p_id->local_type, BLE_PSEUDO_BDA(p_id->local),
|
||||
p_id->peer_type, BLE_PSEUDO_BDA(p_id->peer),
|
||||
BLE_PSEUDO_BDA(pseudo), hashed);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_init / deinit
|
||||
*******************************************************************************/
|
||||
void btm_ble_pseudo_init(void)
|
||||
{
|
||||
/* Bluedroid host init runs during stack bring-up where heap exhaustion is
|
||||
* not a tolerated / recoverable condition: if this single fixed-size mutex
|
||||
* cannot be created the whole host cannot come up, so there is nothing to
|
||||
* gracefully fall back to. The return value is intentionally not checked
|
||||
* here, matching the rest of the host init path (e.g. btm_ble_init()), and
|
||||
* this is not a bug. */
|
||||
osi_mutex_new(&btm_ble_pseudo_mutex);
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
memset(btm_ble_conn_id_tab, 0, sizeof(btm_ble_conn_id_tab));
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
}
|
||||
|
||||
void btm_ble_pseudo_deinit(void)
|
||||
{
|
||||
if (btm_ble_pseudo_mutex == NULL) {
|
||||
return;
|
||||
}
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
memset(btm_ble_conn_id_tab, 0, sizeof(btm_ble_conn_id_tab));
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
osi_mutex_free(&btm_ble_pseudo_mutex);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_get_by_handle
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_get_by_handle(UINT16 handle, tBTM_BLE_CONN_IDENTITY *p_out)
|
||||
{
|
||||
BOOLEAN found = FALSE;
|
||||
|
||||
if (p_out == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
tBTM_BLE_CONN_IDENTITY *p_ent = conn_identity_by_handle_locked(handle);
|
||||
if (p_ent) {
|
||||
memcpy(p_out, p_ent, sizeof(tBTM_BLE_CONN_IDENTITY));
|
||||
found = TRUE;
|
||||
}
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_get_by_pseudo
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_get_by_pseudo(const BD_ADDR pseudo, tBTM_BLE_CONN_IDENTITY *p_out)
|
||||
{
|
||||
BOOLEAN found = FALSE;
|
||||
|
||||
if (pseudo == NULL || p_out == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
tBTM_BLE_CONN_IDENTITY *p_ent = conn_identity_by_pseudo_locked(pseudo);
|
||||
if (p_ent) {
|
||||
memcpy(p_out, p_ent, sizeof(tBTM_BLE_CONN_IDENTITY));
|
||||
found = TRUE;
|
||||
}
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_exists_by_handle
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_exists_by_handle(UINT16 handle)
|
||||
{
|
||||
BOOLEAN found = FALSE;
|
||||
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
found = (conn_identity_by_handle_locked(handle) != NULL);
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_register
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_register(UINT16 handle,
|
||||
const tBLE_CONN_IDENTITY *p_id,
|
||||
const BD_ADDR pseudo,
|
||||
BOOLEAN local_ready)
|
||||
{
|
||||
BOOLEAN ok = FALSE;
|
||||
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
|
||||
tBTM_BLE_CONN_IDENTITY *p_ent = conn_identity_by_handle_locked(handle);
|
||||
if (p_ent == NULL) {
|
||||
for (int i = 0; i < BTM_BLE_PSEUDO_MAX_CONN; i++) {
|
||||
if (!btm_ble_conn_id_tab[i].in_use) {
|
||||
p_ent = &btm_ble_conn_id_tab[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_ent == NULL) {
|
||||
BTM_TRACE_ERROR("%s no free slot for handle 0x%x", __func__, handle);
|
||||
BLE_PSEUDO_DBG("register FAIL: no free slot, handle=0x%x", handle);
|
||||
} else {
|
||||
p_ent->handle = handle;
|
||||
p_ent->in_use = TRUE;
|
||||
p_ent->local_ready = local_ready;
|
||||
if (p_id) {
|
||||
memcpy(&p_ent->id, p_id, sizeof(tBLE_CONN_IDENTITY));
|
||||
}
|
||||
if (pseudo) {
|
||||
memcpy(p_ent->pseudo, pseudo, BD_ADDR_LEN);
|
||||
}
|
||||
BLE_PSEUDO_DBG("register: handle=0x%x slot=%d pseudo=" BLE_PSEUDO_BDA_FMT " local_ready=%d",
|
||||
handle, (int)(p_ent - btm_ble_conn_id_tab),
|
||||
BLE_PSEUDO_BDA(p_ent->pseudo), local_ready);
|
||||
ok = TRUE;
|
||||
}
|
||||
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_unregister
|
||||
*******************************************************************************/
|
||||
void btm_ble_conn_identity_unregister(UINT16 handle)
|
||||
{
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
tBTM_BLE_CONN_IDENTITY *p_ent = conn_identity_by_handle_locked(handle);
|
||||
if (p_ent) {
|
||||
BLE_PSEUDO_DBG("unregister: handle=0x%x pseudo=" BLE_PSEUDO_BDA_FMT,
|
||||
handle, BLE_PSEUDO_BDA(p_ent->pseudo));
|
||||
memset(p_ent, 0, sizeof(tBTM_BLE_CONN_IDENTITY));
|
||||
}
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_to_real_peer
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_pseudo_to_real_peer(const BD_ADDR pseudo, BD_ADDR real_peer)
|
||||
{
|
||||
BOOLEAN found = FALSE;
|
||||
|
||||
if (pseudo == NULL || real_peer == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&btm_ble_pseudo_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||
tBTM_BLE_CONN_IDENTITY *p_ent = conn_identity_by_pseudo_locked(pseudo);
|
||||
if (p_ent) {
|
||||
memcpy(real_peer, p_ent->id.peer, BD_ADDR_LEN);
|
||||
found = TRUE;
|
||||
}
|
||||
osi_mutex_unlock(&btm_ble_pseudo_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
#endif /* BLE_INCLUDED && SMP_INCLUDED && BLE_PERIPH_PSEUDO_ADDR_BOND */
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "stack/hcidefs.h"
|
||||
#include "stack/l2c_api.h"
|
||||
|
||||
static tBTM_SEC_DEV_REC *btm_find_oldest_dev (void);
|
||||
static tBTM_SEC_DEV_REC *btm_find_oldest_dev_ex (tBTM_SEC_DEV_REC *exclude_rec);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@@ -330,6 +330,22 @@ BOOLEAN btm_find_sec_dev_in_list (void *p_node_data, void *context)
|
||||
**
|
||||
*******************************************************************************/
|
||||
tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
|
||||
{
|
||||
return btm_sec_alloc_dev_ex(bd_addr, NULL);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btm_sec_alloc_dev_ex
|
||||
**
|
||||
** Description Same as btm_sec_alloc_dev(), but exclude_rec will never be
|
||||
** recycled when the device table is full and an existing entry
|
||||
** must be reused.
|
||||
**
|
||||
** Returns Pointer to the record or NULL
|
||||
**
|
||||
*******************************************************************************/
|
||||
tBTM_SEC_DEV_REC *btm_sec_alloc_dev_ex (BD_ADDR bd_addr, tBTM_SEC_DEV_REC *exclude_rec)
|
||||
{
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = NULL;
|
||||
tBTM_SEC_DEV_REC *p_dev_new_rec = NULL;
|
||||
@@ -339,7 +355,7 @@ tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
|
||||
BOOLEAN new_entry_found = FALSE;
|
||||
BOOLEAN old_entry_found = FALSE;
|
||||
BOOLEAN malloc_new_entry = FALSE;
|
||||
BTM_TRACE_EVENT ("btm_sec_alloc_dev - start alloc for device %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
BTM_TRACE_EVENT ("btm_sec_alloc_dev_ex - start alloc for device %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
|
||||
for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) {
|
||||
p_dev_old_rec = list_node(p_node);
|
||||
@@ -374,13 +390,16 @@ tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
|
||||
}
|
||||
}
|
||||
if (!new_entry_found) {
|
||||
p_dev_rec = btm_find_oldest_dev();
|
||||
p_dev_rec = btm_find_oldest_dev_ex(exclude_rec);
|
||||
#if (BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)
|
||||
// If device record exists and contains identity key, remove it from resolving list
|
||||
if (p_dev_rec && (p_dev_rec->ble.key_type & SMP_SEC_KEY_TYPE_ID)) {
|
||||
btm_ble_resolving_list_remove_dev(p_dev_rec);
|
||||
}
|
||||
#endif // (BLE_INCLUDED == TRUE) && (SMP_INCLUDED == TRUE)
|
||||
if (p_dev_rec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
/* if the old device entry not present go with new entry */
|
||||
if (old_entry_found) {
|
||||
@@ -654,16 +673,17 @@ tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr)
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function btm_find_oldest_dev
|
||||
** Function btm_find_oldest_dev_ex
|
||||
**
|
||||
** Description Locates the oldest device in use. It first looks for
|
||||
** the oldest non-paired device. If all devices are paired it
|
||||
** deletes the oldest paired device.
|
||||
** deletes the oldest paired device. exclude_rec is never
|
||||
** returned when non-NULL.
|
||||
**
|
||||
** Returns Pointer to the record or NULL
|
||||
**
|
||||
*******************************************************************************/
|
||||
tBTM_SEC_DEV_REC *btm_find_oldest_dev (void)
|
||||
static tBTM_SEC_DEV_REC *btm_find_oldest_dev_ex (tBTM_SEC_DEV_REC *exclude_rec)
|
||||
{
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = NULL;
|
||||
tBTM_SEC_DEV_REC *p_oldest = NULL;
|
||||
@@ -673,6 +693,9 @@ tBTM_SEC_DEV_REC *btm_find_oldest_dev (void)
|
||||
/* First look for the non-paired devices for the oldest entry */
|
||||
for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) {
|
||||
p_dev_rec = list_node(p_node);
|
||||
if (p_dev_rec == exclude_rec) {
|
||||
continue;
|
||||
}
|
||||
if (((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0)
|
||||
|| ((p_dev_rec->sec_flags & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LE_LINK_KEY_KNOWN)) != 0)) {
|
||||
continue; /* Device is paired so skip it */
|
||||
@@ -689,8 +712,12 @@ tBTM_SEC_DEV_REC *btm_find_oldest_dev (void)
|
||||
}
|
||||
|
||||
/* All devices are paired; find the oldest */
|
||||
old_ts = 0xFFFFFFFF;
|
||||
for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) {
|
||||
p_dev_rec = list_node(p_node);
|
||||
if (p_dev_rec == exclude_rec) {
|
||||
continue;
|
||||
}
|
||||
if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -971,4 +971,14 @@ static const char *mode_to_string(tBTM_PM_MODE mode)
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* CLASSIC_BT_INCLUDED != TRUE */
|
||||
|
||||
tBTM_STATUS BTM_SetPowerMode(UINT8 pm_id, BD_ADDR remote_bda, tBTM_PM_PWR_MD *p_mode)
|
||||
{
|
||||
UNUSED(pm_id);
|
||||
UNUSED(remote_bda);
|
||||
UNUSED(p_mode);
|
||||
return BTM_SUCCESS;
|
||||
}
|
||||
|
||||
#endif // #if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
|
||||
@@ -480,6 +480,7 @@ void btm_ble_update_link_topology_mask(UINT8 role, BOOLEAN increase);
|
||||
/* direct connection utility */
|
||||
BOOLEAN btm_send_pending_direct_conn(void);
|
||||
void btm_ble_enqueue_direct_conn_req(void *p_param);
|
||||
void btm_ble_remove_direct_conn_req(void *p_param);
|
||||
|
||||
/* BLE address management */
|
||||
void btm_gen_resolvable_private_addr (void *p_cmd_cplt_cback);
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Peripheral dual local-identity bond isolation: Host-internal pseudo
|
||||
* address derivation and the per-connection identity side table.
|
||||
*
|
||||
* A pseudo address is a 6-byte Host-only key computed from
|
||||
* (local_identity, peer). It lets one peer phone that connects through two
|
||||
* distinct local identities (e.g. Public and a fixed Static Random adv set)
|
||||
* appear as two independent peers inside the Host (separate device record,
|
||||
* LTK and NVS bond section). The over-the-air and SMP cryptography keep
|
||||
* using the real peer and the real local identity; the pseudo never leaves
|
||||
* the Host.
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef BTM_BLE_PSEUDO_H
|
||||
#define BTM_BLE_PSEUDO_H
|
||||
|
||||
#include "common/bt_target.h"
|
||||
#include "stack/bt_types.h"
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
|
||||
#include "common/bt_trace.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Debug logging for the pseudo-address bond feature. Routed through the Host
|
||||
* BTM trace macro so the "[PSEUDO]" lines follow the standard Bluetooth log
|
||||
* level (BT_LOG_LEVEL_BTM) like the rest of the stack. */
|
||||
#define BLE_PSEUDO_DBG(fmt, ...) BTM_TRACE_DEBUG("[PSEUDO] " fmt, ##__VA_ARGS__)
|
||||
|
||||
/* Helper to print a BD_ADDR without a MACSTR dependency. */
|
||||
#define BLE_PSEUDO_BDA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
|
||||
#define BLE_PSEUDO_BDA_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
|
||||
/* Bump when the pseudo derivation input layout or algorithm changes (e.g. v1
|
||||
* was SHA-256 truncated; v2 is AES-CMAC via the configured SMP crypto backend).
|
||||
* Persisted in the input/key so a mismatch yields a different pseudo. */
|
||||
#define BLE_PSEUDO_SCHEME_VER 2
|
||||
|
||||
/* Identity that produced one connection: the resolved real peer plus the
|
||||
* local identity (Public or fixed Static Random) of the adv set / link. */
|
||||
typedef struct {
|
||||
BD_ADDR local; /* local identity (NOT a transient RPA) */
|
||||
BD_ADDR peer; /* resolved real peer identity */
|
||||
tBLE_ADDR_TYPE local_type;
|
||||
tBLE_ADDR_TYPE peer_type;
|
||||
} tBLE_CONN_IDENTITY;
|
||||
|
||||
/* Per-connection side table entry, keyed by HCI handle. */
|
||||
typedef struct {
|
||||
UINT16 handle;
|
||||
BD_ADDR pseudo;
|
||||
tBLE_CONN_IDENTITY id;
|
||||
BOOLEAN local_ready; /* TRUE once local identity finalized */
|
||||
BOOLEAN in_use;
|
||||
} tBTM_BLE_CONN_IDENTITY;
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_identity_to_pseudo
|
||||
**
|
||||
** Description Deterministically derive a 6-byte Host pseudo address from
|
||||
** (local identity || peer). Same input always yields the same
|
||||
** output. The result is forced to Static-Random format (top
|
||||
** two bits = 11) so it can never be mistaken for a resolvable
|
||||
** RPA by btm_find_dev()/btm_ble_addr_resolvable().
|
||||
*******************************************************************************/
|
||||
void btm_ble_identity_to_pseudo(const tBLE_CONN_IDENTITY *p_id, BD_ADDR pseudo);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_init / btm_ble_pseudo_deinit
|
||||
*******************************************************************************/
|
||||
void btm_ble_pseudo_init(void);
|
||||
void btm_ble_pseudo_deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_register
|
||||
**
|
||||
** Description Record (handle -> pseudo, identity). If an entry for the
|
||||
** handle already exists it is updated. Returns FALSE when the
|
||||
** table is full.
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_register(UINT16 handle,
|
||||
const tBLE_CONN_IDENTITY *p_id,
|
||||
const BD_ADDR pseudo,
|
||||
BOOLEAN local_ready);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_unregister
|
||||
*******************************************************************************/
|
||||
void btm_ble_conn_identity_unregister(UINT16 handle);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_get_by_handle / get_by_pseudo
|
||||
**
|
||||
** Description Copy-out snapshot of a side-table entry. Safe from any task.
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_get_by_handle(UINT16 handle, tBTM_BLE_CONN_IDENTITY *p_out);
|
||||
BOOLEAN btm_ble_conn_identity_get_by_pseudo(const BD_ADDR pseudo, tBTM_BLE_CONN_IDENTITY *p_out);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_conn_identity_exists_by_handle
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_conn_identity_exists_by_handle(UINT16 handle);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_to_real_peer
|
||||
**
|
||||
** Description Reverse map a pseudo back to the real peer. Returns TRUE if
|
||||
** the pseudo is known.
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_pseudo_to_real_peer(const BD_ADDR pseudo, BD_ADDR real_peer);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_finalize_local
|
||||
**
|
||||
** Description Second-phase finalize, called from the LE Advertising Set
|
||||
** Terminated handler. Re-keys a link to its pseudo when the
|
||||
** ext-adv instance was not resolvable at connection complete.
|
||||
** Defined in btm_ble.c.
|
||||
*******************************************************************************/
|
||||
void btm_ble_pseudo_finalize_local(UINT16 handle);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function btm_ble_pseudo_apply_identity
|
||||
**
|
||||
** Description Re-key a link from a transient-RPA-derived pseudo to the
|
||||
** stable f(local, peer Identity) pseudo once the peer's
|
||||
** Identity Address (PID) is learned during pairing. Returns
|
||||
** TRUE and fills new_pseudo when the pseudo changed so the
|
||||
** SMP caller can keep smp_cb.pairing_bda consistent.
|
||||
*******************************************************************************/
|
||||
BOOLEAN btm_ble_pseudo_apply_identity(UINT16 handle, const BD_ADDR identity,
|
||||
UINT8 id_type, BD_ADDR new_pseudo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BLE_INCLUDED && SMP_INCLUDED && BLE_PERIPH_PSEUDO_ADDR_BOND */
|
||||
#endif /* BTM_BLE_PSEUDO_H */
|
||||
@@ -624,6 +624,12 @@ typedef struct {
|
||||
BD_ADDR current_addr; /* current adv addr*/
|
||||
bool current_addr_valid; /* current addr info is valid or not*/
|
||||
#endif
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BOOLEAN is_pseudo_bond; /* record is keyed by a Host pseudo
|
||||
* (dual local-identity bond); never
|
||||
* consolidate it onto the peer
|
||||
* Identity or its LTK is lost */
|
||||
#endif
|
||||
} tBTM_SEC_BLE;
|
||||
|
||||
|
||||
@@ -1282,6 +1288,7 @@ void btm_page_to_setup_timeout (void *p_tle);
|
||||
BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr);
|
||||
|
||||
tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr);
|
||||
tBTM_SEC_DEV_REC *btm_sec_alloc_dev_ex (BD_ADDR bd_addr, tBTM_SEC_DEV_REC *exclude_rec);
|
||||
void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport);
|
||||
tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr);
|
||||
tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr);
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
#include "stack/gatt_api.h"
|
||||
#include "gatt_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#if SMP_INCLUDED == TRUE
|
||||
#include "smp_int.h"
|
||||
#endif
|
||||
@@ -120,6 +123,14 @@ void btu_init_core(void)
|
||||
******************************************************************************/
|
||||
void btu_free_core(void)
|
||||
{
|
||||
#if (BLE_INCLUDED == TRUE && defined(GATT_INCLUDED) && GATT_INCLUDED == true && BLE_EATT_INCLUDED == TRUE)
|
||||
/* Tear down EATT before l2c_free(): gatt_eatt_deinit() deregisters the EATT
|
||||
* LE CoC PSM (L2CA_DeregisterLECoc) and the EATT GATT interface
|
||||
* (GATT_Deregister, which may disconnect open links). Both need live L2CAP
|
||||
* state; running them after l2c_free() dereferences the freed l2c_cb_ptr. */
|
||||
gatt_eatt_deinit();
|
||||
#endif
|
||||
|
||||
// Free the mandatory core stack components
|
||||
l2c_free();
|
||||
|
||||
|
||||
@@ -317,6 +317,21 @@ static void btu_general_alarm_process(void *param)
|
||||
TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)param;
|
||||
assert(p_tle != NULL);
|
||||
|
||||
/* Skip stale alarms: btu_free_timer removes the entry before the owning
|
||||
* structure may be freed, and btu_stop_timer clears in_use, but neither can
|
||||
* retract a SIG_BTU_GENERAL_ALARM that was already queued to the BTU task. */
|
||||
osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
bool active = hash_map_has_key(btu_general_alarm_hash_map, p_tle);
|
||||
osi_mutex_unlock(&btu_general_alarm_lock);
|
||||
if (!active) {
|
||||
osi_mutex_lock(&btu_oneshot_alarm_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
active = hash_map_has_key(btu_oneshot_alarm_hash_map, p_tle);
|
||||
osi_mutex_unlock(&btu_oneshot_alarm_lock);
|
||||
}
|
||||
if (!active || p_tle->in_use == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (p_tle->event) {
|
||||
case BTU_TTYPE_BTM_DEV_CTL:
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
@@ -392,6 +407,12 @@ static void btu_general_alarm_process(void *param)
|
||||
#endif // (GATTC_INCLUDED == TRUE)
|
||||
break;
|
||||
|
||||
case BTU_TTYPE_ATT_WAIT_FOR_CONF:
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
gatt_conf_timeout(p_tle);
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
break;
|
||||
|
||||
#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE)
|
||||
case BTU_TTYPE_SMP_PAIRING_CMD:
|
||||
smp_rsp_timeout(p_tle);
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
#include "gatt_int.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
|
||||
#define GATT_HDR_FIND_TYPE_VALUE_LEN 21
|
||||
#define GATT_OP_CODE_SIZE 1
|
||||
@@ -171,7 +174,11 @@ BT_HDR *attp_build_read_by_type_value_cmd (UINT16 payload_size, tGATT_FIND_TYPE_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) {
|
||||
if (payload_size > L2CAP_DEFAULT_MTU) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET)) != NULL) {
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
|
||||
|
||||
p_buf->offset = L2CAP_MIN_OFFSET;
|
||||
@@ -208,7 +215,11 @@ BT_HDR *attp_build_read_multi_cmd(UINT8 op_code, UINT16 payload_size, UINT16 num
|
||||
UINT8 *p;
|
||||
UINT16 i = 0;
|
||||
|
||||
if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) {
|
||||
if (payload_size > L2CAP_DEFAULT_MTU) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + (size_t)num_handle * 2 + 1 + L2CAP_MIN_OFFSET)) != NULL) {
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
|
||||
|
||||
p_buf->offset = L2CAP_MIN_OFFSET;
|
||||
@@ -321,6 +332,10 @@ BT_HDR *attp_build_value_cmd(UINT16 payload_size, UINT8 op_code,
|
||||
|
||||
/* handle Read By Type response: reserve space for pair_len */
|
||||
if (op_code == GATT_RSP_READ_BY_TYPE) {
|
||||
if (len > GATT_MAX_READ_BY_TYPE_VALUE_LEN) {
|
||||
GATT_TRACE_WARNING("ReadByType value truncated to %d", GATT_MAX_READ_BY_TYPE_VALUE_LEN);
|
||||
len = GATT_MAX_READ_BY_TYPE_VALUE_LEN;
|
||||
}
|
||||
p_pair_len = p++;
|
||||
pair_len = len + 2; /* handle(2 bytes) + value length */
|
||||
size_now += 1;
|
||||
@@ -387,9 +402,26 @@ BT_HDR *attp_build_value_cmd(UINT16 payload_size, UINT8 op_code,
|
||||
tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
|
||||
{
|
||||
UINT16 l2cap_ret;
|
||||
UINT16 lcid = p_tcb->att_lcid;
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
UINT8 op_code = *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset);
|
||||
|
||||
if (p_tcb->att_lcid == L2CAP_ATT_CID) {
|
||||
/* Exchange MTU is defined only on the legacy ATT bearer (Core Spec Vol 3
|
||||
* Part G 5.3): keep it on att_lcid even if eatt_tx_bearer/eatt_rx_bearer is
|
||||
* set. Without this, an MTU PDU flushed while an EATT response is still being
|
||||
* processed (eatt_rx_bearer not yet cleared) would be sent on an EATT bearer
|
||||
* and the peer would reject it with REQ_NOT_SUPPORTED. */
|
||||
if (op_code != GATT_REQ_MTU && op_code != GATT_RSP_MTU) {
|
||||
if (p_tcb->eatt_tx_bearer != 0) {
|
||||
lcid = p_tcb->eatt_tx_bearer;
|
||||
} else if (p_tcb->eatt_rx_bearer != 0) {
|
||||
lcid = p_tcb->eatt_rx_bearer;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lcid == L2CAP_ATT_CID) {
|
||||
/* L2CA_SendFixedChnlData() silently drops (osi_free) the buffer when the
|
||||
* ATT fixed channel is already in cong_sent state, yet still returns
|
||||
* L2CAP_DW_CONGESTED. Without distinguishing this from the post-enqueue
|
||||
@@ -404,11 +436,25 @@ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
|
||||
}
|
||||
l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP);
|
||||
} else {
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE) && (BLE_EATT_INCLUDED == TRUE)
|
||||
if (gatt_eatt_is_bearer(lcid)) {
|
||||
l2cap_ret = L2CA_LECocDataWrite(lcid, p_toL2CAP);
|
||||
} else
|
||||
#endif
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP);
|
||||
{
|
||||
l2cap_ret = (UINT16) L2CA_DataWrite(lcid, p_toL2CAP);
|
||||
}
|
||||
#else
|
||||
l2cap_ret = L2CAP_DW_FAILED;
|
||||
#endif ///CLASSIC_BT_INCLUDED == TRUE
|
||||
{
|
||||
/* No L2CAP write consumed the buffer on this BLE-only path (e.g. an
|
||||
* lcid that is neither the ATT fixed channel nor a known EATT
|
||||
* bearer). Free it here so attp_send_msg_to_l2cap always consumes
|
||||
* the buffer exactly once, matching every caller's assumption. */
|
||||
osi_free(p_toL2CAP);
|
||||
l2cap_ret = L2CAP_DW_FAILED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (l2cap_ret == L2CAP_DW_FAILED) {
|
||||
@@ -470,7 +516,7 @@ BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
|
||||
case GATT_HANDLE_VALUE_NOTIF:
|
||||
case GATT_HANDLE_VALUE_IND:
|
||||
case GATT_HANDLE_MULTI_VALUE_NOTIF:
|
||||
p_cmd = attp_build_value_cmd(p_tcb->payload_size,
|
||||
p_cmd = attp_build_value_cmd(gatt_get_att_mtu(p_tcb),
|
||||
op_code,
|
||||
p_msg->attr_value.handle,
|
||||
offset,
|
||||
@@ -552,6 +598,37 @@ tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code,
|
||||
if (p_tcb != NULL) {
|
||||
cmd_code &= ~GATT_AUTH_SIGN_MASK;
|
||||
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
UINT16 eatt_bearer = L2CAP_ATT_CID;
|
||||
if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE &&
|
||||
cmd_code != GATT_REQ_MTU) {
|
||||
eatt_bearer = gatt_eatt_get_available_bearer(p_tcb->peer_bda, cmd_code);
|
||||
}
|
||||
if (eatt_bearer != L2CAP_ATT_CID) {
|
||||
p_tcb->eatt_tx_bearer = eatt_bearer;
|
||||
att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd);
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
if (att_ret == GATT_SUCCESS) {
|
||||
gatt_eatt_mark_busy(p_tcb->peer_bda, eatt_bearer, cmd_code, clcb_idx);
|
||||
gatt_start_rsp_timer(clcb_idx);
|
||||
} else if (att_ret == GATT_CONGESTED) {
|
||||
/* Buffer is queued at L2CAP; arm the response timer just like the
|
||||
* legacy path so a lost response cannot hang the CLCB forever. */
|
||||
gatt_eatt_mark_busy(p_tcb->peer_bda, eatt_bearer, cmd_code, clcb_idx);
|
||||
gatt_start_rsp_timer(clcb_idx);
|
||||
/* Normalize to success: the buffer was accepted (queued for
|
||||
* credit) so the operation is in progress. Returning
|
||||
* GATT_CONGESTED would make gatt_act_discovery/gatt_act_read
|
||||
* treat it as failure and free the CLCB via gatt_end_operation
|
||||
* without releasing this EATT bearer, leaving it stuck busy. */
|
||||
att_ret = GATT_SUCCESS;
|
||||
} else {
|
||||
att_ret = GATT_INTERNAL_ERROR;
|
||||
}
|
||||
return att_ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no pending request or value confirmation */
|
||||
if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
|
||||
cmd_code == GATT_HANDLE_VALUE_CONF) {
|
||||
@@ -598,6 +675,16 @@ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
|
||||
UINT16 offset = 0, handle;
|
||||
|
||||
if (p_tcb != NULL) {
|
||||
/* Use the legacy ATT payload_size as the fallback MTU. gatt_get_att_mtu()
|
||||
* would return the EATT rx-bearer MTU when eatt_rx_bearer is transiently
|
||||
* set (re-entrant response handling), which could size a PDU for EATT but
|
||||
* send it on the legacy bearer and exceed its MTU. */
|
||||
UINT16 att_mtu = p_tcb->payload_size;
|
||||
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
att_mtu = gatt_eatt_mtu_for_client_op(p_tcb->peer_bda, op_code, att_mtu);
|
||||
#endif
|
||||
|
||||
switch (op_code) {
|
||||
case GATT_REQ_MTU:
|
||||
if (p_msg->mtu <= GATT_MAX_MTU_SIZE) {
|
||||
@@ -647,7 +734,7 @@ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
|
||||
case GATT_CMD_WRITE:
|
||||
case GATT_SIGN_CMD_WRITE:
|
||||
if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) {
|
||||
p_cmd = attp_build_value_cmd (p_tcb->payload_size,
|
||||
p_cmd = attp_build_value_cmd (att_mtu,
|
||||
op_code, p_msg->attr_value.handle,
|
||||
offset,
|
||||
p_msg->attr_value.len,
|
||||
@@ -662,12 +749,12 @@ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
|
||||
break;
|
||||
|
||||
case GATT_REQ_FIND_TYPE_VALUE:
|
||||
p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value);
|
||||
p_cmd = attp_build_read_by_type_value_cmd(att_mtu, &p_msg->find_type_value);
|
||||
break;
|
||||
|
||||
case GATT_REQ_READ_MULTI:
|
||||
case GATT_REQ_READ_MULTI_VAR:
|
||||
p_cmd = attp_build_read_multi_cmd(op_code, p_tcb->payload_size,
|
||||
p_cmd = attp_build_read_multi_cmd(op_code, att_mtu,
|
||||
p_msg->read_multi.num_handles,
|
||||
p_msg->read_multi.handles);
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "stack/gatt_api.h"
|
||||
#include "gatt_int.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#include "btm_int.h"
|
||||
#include "stack/sdpdefs.h"
|
||||
#include "stack/sdp_api.h"
|
||||
@@ -177,8 +180,8 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid,
|
||||
p_app_uuid128 = &p_reg->app_uuid128;
|
||||
|
||||
if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) {
|
||||
s_hdl = p_list->asgn_range.s_handle;
|
||||
GATT_TRACE_DEBUG ("Service already been created!!\n");
|
||||
return p_list->asgn_range.s_handle;
|
||||
} else {
|
||||
if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) {
|
||||
s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl;
|
||||
@@ -232,6 +235,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid,
|
||||
|
||||
if (p_list) {
|
||||
gatt_remove_an_item_from_list(p_list_info, p_list);
|
||||
gatt_purge_prepare_write_before_free_db(&p_list->svc_db);
|
||||
gatt_free_attr_value_buffer(p_list);
|
||||
gatt_free_hdl_buffer(p_list);
|
||||
}
|
||||
@@ -246,6 +250,7 @@ UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid,
|
||||
GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed\n");
|
||||
if (p_list) {
|
||||
gatt_remove_an_item_from_list(p_list_info, p_list);
|
||||
gatt_purge_prepare_write_before_free_db(&p_list->svc_db);
|
||||
gatt_free_attr_value_buffer(p_list);
|
||||
gatt_free_hdl_buffer(p_list);
|
||||
}
|
||||
@@ -395,6 +400,7 @@ BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_
|
||||
tGATTS_PENDING_NEW_SRV_START *p_buf;
|
||||
tGATT_REG *p_reg = gatt_get_regcb(gatt_if);
|
||||
tBT_UUID *p_app_uuid128;
|
||||
BOOLEAN notify_db_change = FALSE;
|
||||
|
||||
GATT_TRACE_DEBUG ("GATTS_DeleteService");
|
||||
|
||||
@@ -415,12 +421,8 @@ BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_
|
||||
GATT_TRACE_DEBUG ("Delete a new service changed item - the service has not yet started");
|
||||
osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf));
|
||||
} else {
|
||||
#if GATTS_ROBUST_CACHING_ENABLED
|
||||
gatt_update_for_database_change();
|
||||
#endif /* GATTS_ROBUST_CACHING_ENABLED */
|
||||
if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) {
|
||||
gatt_proc_srv_chg();
|
||||
}
|
||||
/* Service was started; notify clients after it is removed from sr_reg. */
|
||||
notify_db_change = TRUE;
|
||||
}
|
||||
|
||||
if ((i_sreg = gatt_sr_find_i_rcb_by_app_id (p_app_uuid128,
|
||||
@@ -438,9 +440,19 @@ BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_
|
||||
}
|
||||
|
||||
gatt_remove_an_item_from_list(p_list_info, p_list);
|
||||
gatt_purge_prepare_write_before_free_db(&p_list->svc_db);
|
||||
gatt_free_attr_value_buffer(p_list);
|
||||
gatt_free_hdl_buffer(p_list);
|
||||
|
||||
if (notify_db_change) {
|
||||
#if GATTS_ROBUST_CACHING_ENABLED
|
||||
gatt_update_for_database_change();
|
||||
#endif /* GATTS_ROBUST_CACHING_ENABLED */
|
||||
if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) {
|
||||
gatt_proc_srv_chg();
|
||||
}
|
||||
}
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
@@ -636,6 +648,13 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U
|
||||
return GATT_BUSY;
|
||||
} else {
|
||||
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
/* Route the indication over an EATT bearer (if any) so it uses the EATT
|
||||
* MTU instead of the legacy 23-byte ATT MTU. Set transiently and cleared
|
||||
* after the send; all GATT TX runs on the single BTU task. */
|
||||
UINT16 ind_bearer = gatt_eatt_get_server_tx_bearer(p_tcb->peer_bda);
|
||||
p_tcb->eatt_tx_bearer = (ind_bearer != L2CAP_ATT_CID) ? ind_bearer : 0;
|
||||
#endif
|
||||
if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) {
|
||||
cmd_status = attp_send_sr_msg (p_tcb, p_msg);
|
||||
|
||||
@@ -644,6 +663,9 @@ tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, U
|
||||
gatt_start_conf_timer(p_tcb);
|
||||
}
|
||||
}
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
#endif
|
||||
}
|
||||
return cmd_status;
|
||||
}
|
||||
@@ -691,12 +713,22 @@ tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle,
|
||||
memcpy (notif.value, p_val, val_len);
|
||||
notif.auth_req = GATT_AUTH_REQ_NONE;
|
||||
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
/* Route the notification over an EATT bearer (if any) so it uses the EATT
|
||||
* MTU instead of the legacy 23-byte ATT MTU. Set transiently and cleared
|
||||
* after the send; all GATT TX runs on the single BTU task. */
|
||||
UINT16 notif_bearer = gatt_eatt_get_server_tx_bearer(p_tcb->peer_bda);
|
||||
p_tcb->eatt_tx_bearer = (notif_bearer != L2CAP_ATT_CID) ? notif_bearer : 0;
|
||||
#endif
|
||||
if ((p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_NOTIF, (tGATT_SR_MSG *)¬if))
|
||||
!= NULL) {
|
||||
cmd_sent = attp_send_sr_msg (p_tcb, p_buf);
|
||||
} else {
|
||||
cmd_sent = GATT_NO_RESOURCES;
|
||||
}
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
#endif
|
||||
}
|
||||
return cmd_sent;
|
||||
}
|
||||
@@ -1208,7 +1240,17 @@ tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle)
|
||||
|
||||
GATT_TRACE_DEBUG ("notif_count=%d ", p_tcb->ind_count);
|
||||
/* send confirmation now */
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Route the confirmation back on the EATT bearer the indication came
|
||||
* in on (0 == legacy ATT). eatt_rx_bearer was cleared after the
|
||||
* indication was delivered, so use the saved eatt_ind_bearer. */
|
||||
p_tcb->eatt_tx_bearer = p_tcb->eatt_ind_bearer;
|
||||
ret = attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, (tGATT_CL_MSG *)&handle);
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
p_tcb->eatt_ind_bearer = 0;
|
||||
#else
|
||||
ret = attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, (tGATT_CL_MSG *)&handle);
|
||||
#endif
|
||||
|
||||
p_tcb->ind_count = 0;
|
||||
|
||||
@@ -1361,6 +1403,15 @@ void GATT_Deregister (tGATT_IF gatt_if)
|
||||
GATTS_StopService(p_sreg->s_hdl);
|
||||
}
|
||||
}
|
||||
if (gatt_if > 0 && gatt_if <= GATT_MAX_APPS) {
|
||||
UINT8 prep_idx = (UINT8)(gatt_if - 1);
|
||||
for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) {
|
||||
p_tcb = list_node(p_node);
|
||||
if (p_tcb->in_use) {
|
||||
p_tcb->prep_cnt[prep_idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* free all services db buffers if owned by this application */
|
||||
gatt_free_srvc_db_buffer_app_id(&p_reg->app_uuid128);
|
||||
#endif ///GATTS_INCLUDED == TRUE
|
||||
@@ -1775,12 +1826,24 @@ tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tupl
|
||||
|
||||
notif.auth_req = GATT_AUTH_REQ_NONE;
|
||||
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
/* Route the multi-value notification over an EATT bearer (if any) so it uses
|
||||
* the EATT MTU instead of the legacy 23-byte ATT MTU, and so gatt_get_att_mtu()
|
||||
* (used for buffer sizing in attp_build_sr_msg) matches the bearer it is sent
|
||||
* on. Set transiently and cleared after the send; all GATT TX runs on the
|
||||
* single BTU task. Mirrors GATTS_HandleValueNotification. */
|
||||
UINT16 mv_bearer = gatt_eatt_get_server_tx_bearer(p_tcb->peer_bda);
|
||||
p_tcb->eatt_tx_bearer = (mv_bearer != L2CAP_ATT_CID) ? mv_bearer : 0;
|
||||
#endif
|
||||
p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_MULTI_VALUE_NOTIF, (tGATT_SR_MSG *)¬if);
|
||||
if (p_buf != NULL) {
|
||||
cmd_sent = attp_send_sr_msg (p_tcb, p_buf);
|
||||
} else {
|
||||
cmd_sent = GATT_NO_RESOURCES;
|
||||
}
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
#endif
|
||||
|
||||
return cmd_sent;
|
||||
}
|
||||
@@ -1791,4 +1854,22 @@ tGATT_STATUS GATTS_ShowLocalDatabase(void)
|
||||
return GATT_SUCCESS;
|
||||
}
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
void GATT_EattSetChanNum(UINT8 num_chan)
|
||||
{
|
||||
gatt_eatt_set_chan_num(num_chan);
|
||||
}
|
||||
|
||||
BOOLEAN GATT_EattSetDefaultBearer(UINT16 conn_id, UINT16 lcid)
|
||||
{
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
return gatt_eatt_set_default_bearer(conn_id, lcid);
|
||||
#else
|
||||
UNUSED(conn_id);
|
||||
UNUSED(lcid);
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -131,7 +131,7 @@ static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda, tBT_TR
|
||||
|
||||
for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) {
|
||||
if (p_clcb->in_use && p_clcb->transport == transport &&
|
||||
p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) {
|
||||
!memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) {
|
||||
return p_clcb;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda, tBT_TR
|
||||
if (!p_clcb->in_use) {
|
||||
p_clcb->in_use = TRUE;
|
||||
p_clcb->conn_id = conn_id;
|
||||
p_clcb->connected = TRUE;
|
||||
p_clcb->connected = (conn_id != 0);
|
||||
p_clcb->transport = transport;
|
||||
memcpy (p_clcb->bda, bda, BD_ADDR_LEN);
|
||||
break;
|
||||
@@ -184,6 +184,57 @@ void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb)
|
||||
memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_copy_read_value
|
||||
**
|
||||
** Description Copy attribute value into read/read-blob response.
|
||||
**
|
||||
** Returns GATT_SUCCESS if successfully copied; otherwise error code.
|
||||
**
|
||||
*******************************************************************************/
|
||||
static tGATT_STATUS gatt_copy_read_value(UINT8 *value, UINT16 attr_len,
|
||||
UINT16 offset, BOOLEAN is_long,
|
||||
UINT16 mtu, tGATTS_RSP *p_rsp)
|
||||
{
|
||||
UINT16 copy_len;
|
||||
const UINT8 *src = value;
|
||||
|
||||
if (is_long) {
|
||||
if (offset > attr_len) {
|
||||
return GATT_INVALID_OFFSET;
|
||||
}
|
||||
copy_len = attr_len - offset;
|
||||
if (copy_len > 0) {
|
||||
if (value == NULL) {
|
||||
return GATT_UNKNOWN_ERROR;
|
||||
}
|
||||
src = value + offset;
|
||||
}
|
||||
} else {
|
||||
if (offset > attr_len) {
|
||||
return GATT_INVALID_OFFSET;
|
||||
}
|
||||
copy_len = attr_len;
|
||||
if (copy_len > 0 && value == NULL) {
|
||||
return GATT_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_long && mtu > 0 && copy_len > mtu) {
|
||||
copy_len = mtu;
|
||||
}
|
||||
if (copy_len > GATT_MAX_ATTR_LEN) {
|
||||
copy_len = GATT_MAX_ATTR_LEN;
|
||||
}
|
||||
|
||||
p_rsp->attr_value.len = copy_len;
|
||||
if (copy_len > 0) {
|
||||
memcpy(p_rsp->attr_value.value, src, copy_len);
|
||||
}
|
||||
return GATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_proc_read
|
||||
@@ -197,11 +248,19 @@ tGATT_STATUS gatt_proc_read (UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_RE
|
||||
{
|
||||
tGATT_STATUS status = GATT_NO_RESOURCES;
|
||||
UINT16 len = 0;
|
||||
UINT8 *value;
|
||||
UINT16 mtu = GATT_MAX_ATTR_LEN;
|
||||
UINT8 *value = NULL;
|
||||
UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id);
|
||||
tGATT_TCB *tcb = gatt_get_tcb_by_idx(tcb_idx);
|
||||
UNUSED(type);
|
||||
|
||||
GATT_TRACE_DEBUG("%s handle %x", __func__, p_data->handle);
|
||||
|
||||
/* MTU limit applies to Read Blob only; conn_id 0 is used by internal getters. */
|
||||
if (p_data->is_long && tcb != NULL && tcb->payload_size > 1) {
|
||||
mtu = tcb->payload_size - 1;
|
||||
}
|
||||
|
||||
if (p_data->is_long) {
|
||||
p_rsp->attr_value.offset = p_data->offset;
|
||||
}
|
||||
@@ -209,42 +268,41 @@ tGATT_STATUS gatt_proc_read (UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_RE
|
||||
p_rsp->attr_value.handle = p_data->handle;
|
||||
#if GATTS_ROBUST_CACHING_ENABLED
|
||||
|
||||
UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id);
|
||||
tGATT_TCB *tcb = gatt_get_tcb_by_idx(tcb_idx);
|
||||
|
||||
/* handle request for reading client supported features */
|
||||
if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) {
|
||||
if (tcb == NULL) {
|
||||
return GATT_INSUF_RESOURCE;
|
||||
}
|
||||
p_rsp->attr_value.len = 1;
|
||||
memcpy(p_rsp->attr_value.value, &tcb->cl_supp_feat, 1);
|
||||
return GATT_SUCCESS;
|
||||
return gatt_copy_read_value(&tcb->cl_supp_feat, 1, p_data->offset,
|
||||
p_data->is_long, mtu, p_rsp);
|
||||
}
|
||||
|
||||
/* handle request for reading database hash */
|
||||
if (p_data->handle == gatt_cb.handle_of_database_hash) {
|
||||
p_rsp->attr_value.len = BT_OCTET16_LEN;
|
||||
memcpy(p_rsp->attr_value.value, gatt_cb.database_hash, BT_OCTET16_LEN);
|
||||
gatt_sr_update_cl_status(tcb, true);
|
||||
return GATT_SUCCESS;
|
||||
if (tcb == NULL) {
|
||||
return GATT_INSUF_RESOURCE;
|
||||
}
|
||||
status = gatt_copy_read_value(gatt_cb.database_hash, BT_OCTET16_LEN,
|
||||
p_data->offset, p_data->is_long, mtu, p_rsp);
|
||||
if (status == GATT_SUCCESS) {
|
||||
gatt_sr_update_cl_status(tcb, true);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* handle request for reading server supported features */
|
||||
if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) {
|
||||
p_rsp->attr_value.len = 1;
|
||||
memcpy(p_rsp->attr_value.value, &gatt_cb.gatt_sr_supported_feat_mask, 1);
|
||||
return GATT_SUCCESS;
|
||||
return gatt_copy_read_value(&gatt_cb.gatt_sr_supported_feat_mask, 1,
|
||||
p_data->offset, p_data->is_long, mtu, p_rsp);
|
||||
}
|
||||
#endif /* GATTS_ROBUST_CACHING_ENABLED */
|
||||
/* handle request for reading service changed des and the others */
|
||||
status = GATTS_GetAttributeValue(p_data->handle, &len, &value);
|
||||
if(status == GATT_SUCCESS && len > 0 && value) {
|
||||
if(len > GATT_MAX_ATTR_LEN) {
|
||||
if (status == GATT_SUCCESS && (len == 0 || value != NULL)) {
|
||||
if (len > GATT_MAX_ATTR_LEN) {
|
||||
len = GATT_MAX_ATTR_LEN;
|
||||
}
|
||||
p_rsp->attr_value.len = len;
|
||||
memcpy(p_rsp->attr_value.value, value, len);
|
||||
status = gatt_copy_read_value(value, len, p_data->offset, p_data->is_long, mtu, p_rsp);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -418,7 +476,9 @@ static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
|
||||
|
||||
|
||||
if (!p_clcb->connected) {
|
||||
/* wait for connection */
|
||||
if (!connected) {
|
||||
gatt_profile_clcb_dealloc(p_clcb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -426,6 +486,10 @@ static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
|
||||
p_clcb->conn_id = conn_id;
|
||||
p_clcb->connected = TRUE;
|
||||
|
||||
if (p_clcb->ccc_stage == GATT_SVC_CHANGED_CONNECTING) {
|
||||
p_clcb->ccc_stage++;
|
||||
gatt_cl_start_config_ccc(p_clcb);
|
||||
}
|
||||
} else {
|
||||
gatt_profile_clcb_dealloc(p_clcb);
|
||||
}
|
||||
@@ -682,6 +746,8 @@ void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSP
|
||||
|
||||
if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) {
|
||||
p_clcb->connected = TRUE;
|
||||
} else {
|
||||
p_clcb->connected = FALSE;
|
||||
}
|
||||
/* hold the link here */
|
||||
GATT_Connect(gatt_cb.gatt_if, remote_bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, transport, FALSE, FALSE, 0xFF, 0xFF);
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "gatt_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#include "stack/gatt_api.h"
|
||||
#include "btm_int.h"
|
||||
|
||||
@@ -229,31 +232,40 @@ void gatt_notify_enc_cmpl(BD_ADDR bd_addr)
|
||||
tGATT_TCB *p_tcb;
|
||||
UINT8 i = 0;
|
||||
|
||||
if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE)) != NULL) {
|
||||
for (i = 0; i < GATT_MAX_APPS; i++) {
|
||||
if (gatt_cb.cl_rcb[i].in_use && gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb) {
|
||||
(*gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb)(gatt_cb.cl_rcb[i].gatt_if, bd_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) {
|
||||
gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
|
||||
|
||||
size_t count = fixed_queue_length(p_tcb->pending_enc_clcb);
|
||||
for (; count > 0; count--) {
|
||||
tGATT_PENDING_ENC_CLCB *p_buf =
|
||||
(tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0);
|
||||
if (p_buf != NULL) {
|
||||
gatt_security_check_start(p_buf->p_clcb);
|
||||
osi_free(p_buf);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE)) == NULL) {
|
||||
GATT_TRACE_DEBUG("notify GATT for encryption completion of unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < GATT_MAX_APPS; i++) {
|
||||
if (gatt_cb.cl_rcb[i].in_use && gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb) {
|
||||
(*gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb)(gatt_cb.cl_rcb[i].gatt_if, bd_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* p_tcb may be removed in p_enc_cmpl_cb (e.g. disconnect); re-lookup before use */
|
||||
if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) {
|
||||
gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
|
||||
|
||||
size_t count = fixed_queue_length(p_tcb->pending_enc_clcb);
|
||||
for (; count > 0; count--) {
|
||||
tGATT_PENDING_ENC_CLCB *p_buf =
|
||||
(tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0);
|
||||
if (p_buf != NULL) {
|
||||
gatt_security_check_start(p_buf->p_clcb);
|
||||
osi_free(p_buf);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
gatt_eatt_on_encrypted(bd_addr);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif // (SMP_INCLUDED == TRUE)
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#include <string.h>
|
||||
#include "osi/allocator.h"
|
||||
#include "gatt_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#include "l2c_int.h"
|
||||
|
||||
#define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */
|
||||
@@ -244,7 +247,7 @@ void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act)
|
||||
break;
|
||||
|
||||
case GATT_WRITE:
|
||||
if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) {
|
||||
if (p_attr->len <= (GATT_CL_ATT_MTU(p_tcb, GATT_REQ_WRITE) - GATT_HDR_SIZE)) {
|
||||
p_clcb->s_handle = p_attr->handle;
|
||||
|
||||
rt = gatt_send_write_msg(p_tcb,
|
||||
@@ -297,7 +300,7 @@ void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_E
|
||||
|
||||
rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag);
|
||||
|
||||
if (rt != GATT_SUCCESS) {
|
||||
if (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) {
|
||||
gatt_end_operation(p_clcb, rt, NULL);
|
||||
}
|
||||
}
|
||||
@@ -359,8 +362,8 @@ void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb)
|
||||
GATT_TRACE_DEBUG("gatt_send_prepare_write type=0x%x", type );
|
||||
to_send = p_attr->len - p_attr->offset;
|
||||
|
||||
if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) { /* 2 = UINT16 offset bytes */
|
||||
to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE;
|
||||
if (to_send > (GATT_CL_ATT_MTU(p_tcb, GATT_REQ_PREPARE_WRITE) - GATT_WRITE_LONG_HDR_SIZE)) { /* 2 = UINT16 offset bytes */
|
||||
to_send = GATT_CL_ATT_MTU(p_tcb, GATT_REQ_PREPARE_WRITE) - GATT_WRITE_LONG_HDR_SIZE;
|
||||
}
|
||||
|
||||
p_clcb->s_handle = p_attr->handle;
|
||||
@@ -403,6 +406,7 @@ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UIN
|
||||
{
|
||||
tGATT_DISC_RES result;
|
||||
UINT8 *p = p_data;
|
||||
UINT16 req_s_handle, prev_e_handle;
|
||||
|
||||
UNUSED(p_tcb);
|
||||
|
||||
@@ -412,6 +416,9 @@ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UIN
|
||||
return;
|
||||
}
|
||||
|
||||
req_s_handle = p_clcb->s_handle;
|
||||
prev_e_handle = req_s_handle - 1;
|
||||
|
||||
memset (&result, 0, sizeof(tGATT_DISC_RES));
|
||||
result.type.len = 2;
|
||||
result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE;
|
||||
@@ -420,6 +427,25 @@ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UIN
|
||||
while (len >= 4) {
|
||||
STREAM_TO_UINT16 (result.handle, p);
|
||||
STREAM_TO_UINT16 (result.value.group_value.e_handle, p);
|
||||
|
||||
/* Reject handles that fall outside the requested range or are not
|
||||
* strictly increasing; a malicious/buggy peer must not be able to make
|
||||
* discovery loop forever or report overlapping services. */
|
||||
if (!GATT_HANDLE_IS_VALID(result.handle) ||
|
||||
!GATT_HANDLE_IS_VALID(result.value.group_value.e_handle) ||
|
||||
result.handle < req_s_handle ||
|
||||
result.handle > p_clcb->e_handle ||
|
||||
result.handle > result.value.group_value.e_handle ||
|
||||
result.handle <= prev_e_handle ||
|
||||
result.value.group_value.e_handle <= prev_e_handle) {
|
||||
GATT_TRACE_ERROR("%s invalid handle range: s=%x e=%x req=[%x,%x]",
|
||||
__func__, result.handle, result.value.group_value.e_handle,
|
||||
req_s_handle, p_clcb->e_handle);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
prev_e_handle = result.value.group_value.e_handle;
|
||||
GATT_DISC_INFO("%s handle %x, end handle %x", __func__, result.handle, result.value.group_value.e_handle);
|
||||
memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID));
|
||||
|
||||
@@ -430,8 +456,8 @@ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UIN
|
||||
}
|
||||
}
|
||||
|
||||
/* last handle + 1 */
|
||||
p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1);
|
||||
/* last handle + 1; empty response ends discovery */
|
||||
p_clcb->s_handle = (prev_e_handle < req_s_handle) ? 0 : (prev_e_handle + 1);
|
||||
/* initiate another request */
|
||||
gatt_act_discovery(p_clcb) ;
|
||||
}
|
||||
@@ -451,6 +477,7 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
|
||||
{
|
||||
tGATT_DISC_RES result = {0};
|
||||
UINT8 *p = p_data, uuid_len = 0, type;
|
||||
UINT16 req_s_handle, prev_handle;
|
||||
|
||||
UNUSED(p_tcb);
|
||||
UNUSED(op_code);
|
||||
@@ -465,6 +492,9 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
|
||||
return;
|
||||
}
|
||||
|
||||
req_s_handle = p_clcb->s_handle;
|
||||
prev_handle = req_s_handle - 1;
|
||||
|
||||
STREAM_TO_UINT8(type, p);
|
||||
len -= 1;
|
||||
|
||||
@@ -481,6 +511,16 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
|
||||
while (len >= uuid_len + 2) {
|
||||
STREAM_TO_UINT16 (result.handle, p);
|
||||
|
||||
if (!GATT_HANDLE_IS_VALID(result.handle) ||
|
||||
result.handle < req_s_handle ||
|
||||
result.handle > p_clcb->e_handle ||
|
||||
result.handle <= prev_handle) {
|
||||
GATT_TRACE_ERROR("%s invalid handle %x req=[%x,%x]",
|
||||
__func__, result.handle, req_s_handle, p_clcb->e_handle);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uuid_len > 0) {
|
||||
if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) {
|
||||
break;
|
||||
@@ -489,6 +529,7 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
|
||||
memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID));
|
||||
}
|
||||
|
||||
prev_handle = result.handle;
|
||||
len -= (uuid_len + 2);
|
||||
|
||||
GATT_DISC_INFO("%s handle %x, uuid %s", __func__, result.handle, gatt_uuid_to_str(&result.type));
|
||||
@@ -498,7 +539,7 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
|
||||
}
|
||||
}
|
||||
|
||||
p_clcb->s_handle = (result.handle == 0) ? 0 : (result.handle + 1);
|
||||
p_clcb->s_handle = (prev_handle < req_s_handle) ? 0 : (prev_handle + 1);
|
||||
/* initiate another request */
|
||||
gatt_act_discovery(p_clcb) ;
|
||||
}
|
||||
@@ -694,6 +735,9 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
|
||||
if (value.len > GATT_MAX_ATTR_LEN) {
|
||||
GATT_TRACE_ERROR("value length larger than GATT_MAX_ATTR_LEN, discard");
|
||||
if (op_code == GATT_HANDLE_VALUE_IND) {
|
||||
attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -722,6 +766,13 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
|
||||
/* start a timer for app confirmation */
|
||||
if (p_tcb->ind_count > 0) {
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Remember the bearer this indication arrived on (0 == legacy ATT)
|
||||
* so the app's deferred confirmation is routed back to it; by the
|
||||
* time GATTC_SendHandleValueConfirm() runs, eatt_rx_bearer is
|
||||
* already cleared. */
|
||||
p_tcb->eatt_ind_bearer = p_tcb->eatt_rx_bearer;
|
||||
#endif
|
||||
gatt_start_ind_ack_timer(p_tcb);
|
||||
} else { /* no app to indicate, or invalid handle */
|
||||
attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
@@ -752,6 +803,10 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
STREAM_TO_UINT16(value.len, p);
|
||||
len -= 4;
|
||||
value.len = MIN(len, value.len);
|
||||
if (value.len > GATT_MAX_ATTR_LEN) {
|
||||
GATT_TRACE_ERROR("value length larger than GATT_MAX_ATTR_LEN, discard");
|
||||
return;
|
||||
}
|
||||
memcpy(value.value, p, value.len);
|
||||
p += value.len;
|
||||
len -= value.len;
|
||||
@@ -783,12 +838,19 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
tGATT_DISC_VALUE record_value;
|
||||
UINT8 *p = p_data, value_len, handle_len = 2;
|
||||
UINT16 handle = 0;
|
||||
UINT16 req_s_handle = 0, prev_disc_handle = 0;
|
||||
BOOLEAN is_discovery = (p_clcb->operation == GATTC_OPTYPE_DISCOVERY);
|
||||
|
||||
/* discovery procedure and no callback function registered */
|
||||
if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) {
|
||||
if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && is_discovery) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_discovery) {
|
||||
req_s_handle = p_clcb->s_handle;
|
||||
prev_disc_handle = req_s_handle - 1;
|
||||
}
|
||||
|
||||
if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) {
|
||||
GATT_TRACE_ERROR("Illegal ReadByType/ReadByGroupType Response length, discard");
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
|
||||
@@ -797,11 +859,11 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
|
||||
STREAM_TO_UINT8(value_len, p);
|
||||
|
||||
if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len - 1)) ) {
|
||||
if ((value_len > (gatt_get_att_mtu(p_tcb) - 2)) || (value_len > (len - 1)) ) {
|
||||
/* this is an error case that server's response containing a value length which is larger than MTU-2
|
||||
or value_len > message total length -1 */
|
||||
GATT_TRACE_ERROR("gatt_process_read_by_type_rsp: Discard response op_code=%d value_len=%d > (MTU-2=%d or msg_len-1=%d)",
|
||||
op_code, value_len, (p_tcb->payload_size - 2), (len - 1));
|
||||
op_code, value_len, (gatt_get_att_mtu(p_tcb) - 2), (len - 1));
|
||||
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
@@ -830,6 +892,16 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_discovery && p_clcb->op_subtype != GATT_DISC_SRVC_ALL) {
|
||||
if (handle < req_s_handle || handle > p_clcb->e_handle || handle <= prev_disc_handle) {
|
||||
GATT_TRACE_ERROR("%s invalid handle %x req=[%x,%x]",
|
||||
__func__, handle, req_s_handle, p_clcb->e_handle);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
|
||||
return;
|
||||
}
|
||||
prev_disc_handle = handle;
|
||||
}
|
||||
|
||||
memset(&result, 0, sizeof(tGATT_DISC_RES));
|
||||
memset(&record_value, 0, sizeof(tGATT_DISC_VALUE));
|
||||
|
||||
@@ -848,6 +920,19 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
return;
|
||||
} else {
|
||||
record_value.group_value.e_handle = handle;
|
||||
if (!GATT_HANDLE_IS_VALID(result.handle) ||
|
||||
result.handle < req_s_handle ||
|
||||
result.handle > p_clcb->e_handle ||
|
||||
result.handle > record_value.group_value.e_handle ||
|
||||
result.handle <= prev_disc_handle ||
|
||||
record_value.group_value.e_handle <= prev_disc_handle) {
|
||||
GATT_TRACE_ERROR("%s invalid svc range: s=%x e=%x req=[%x,%x]",
|
||||
__func__, result.handle, record_value.group_value.e_handle,
|
||||
req_s_handle, p_clcb->e_handle);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
|
||||
return;
|
||||
}
|
||||
prev_disc_handle = record_value.group_value.e_handle;
|
||||
if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) {
|
||||
GATT_TRACE_ERROR("discover all service response parsing failure");
|
||||
break;
|
||||
@@ -857,6 +942,11 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
}
|
||||
/* discover included service */
|
||||
else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) {
|
||||
if (value_len < 4) {
|
||||
GATT_TRACE_ERROR("gatt_process_read_by_type_rsp INCL_SRVC: value_len(%d) too short", value_len);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
|
||||
return;
|
||||
}
|
||||
STREAM_TO_UINT16(record_value.incl_service.s_handle, p);
|
||||
STREAM_TO_UINT16(record_value.incl_service.e_handle, p);
|
||||
|
||||
@@ -891,7 +981,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
/* value_len is the length of current record's value; use it to avoid overread when multiple records present */
|
||||
p_clcb->counter = value_len;
|
||||
p_clcb->s_handle = handle;
|
||||
UINT16 max_rbtype_val_len = (p_clcb->p_tcb->payload_size - 4);
|
||||
UINT16 max_rbtype_val_len = (gatt_get_att_mtu(p_clcb->p_tcb) - 4);
|
||||
if (max_rbtype_val_len > GATT_MAX_READ_BY_TYPE_VALUE_LEN) {
|
||||
max_rbtype_val_len = GATT_MAX_READ_BY_TYPE_VALUE_LEN;
|
||||
}
|
||||
@@ -911,6 +1001,11 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
}
|
||||
return;
|
||||
} else { /* discover characteristic */
|
||||
if (value_len < 3) {
|
||||
GATT_TRACE_ERROR("gatt_process_read_by_type_rsp CHAR: value_len(%d) too short", value_len);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
|
||||
return;
|
||||
}
|
||||
STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p);
|
||||
STREAM_TO_UINT16(record_value.dclr_value.val_handle, p);
|
||||
if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) {
|
||||
@@ -949,12 +1044,12 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
}
|
||||
}
|
||||
|
||||
p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1);
|
||||
|
||||
if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) {
|
||||
if (is_discovery) {
|
||||
p_clcb->s_handle = (prev_disc_handle < req_s_handle) ? 0 : (prev_disc_handle + 1);
|
||||
/* initiate another request */
|
||||
gatt_act_discovery(p_clcb) ;
|
||||
} else { /* read characteristic value */
|
||||
} else {
|
||||
p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1);
|
||||
gatt_act_read(p_clcb, 0);
|
||||
}
|
||||
}
|
||||
@@ -1000,7 +1095,7 @@ void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
|
||||
|
||||
/* send next request if needed */
|
||||
|
||||
if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */
|
||||
if (len == (gatt_get_att_mtu(p_tcb) - 1) && /* full packet for read or read blob rsp */
|
||||
len + offset < GATT_MAX_ATTR_LEN) {
|
||||
GATT_TRACE_DEBUG("full pkt issue read blob for remaining bytes old offset=%d len=%d new offset=%d",
|
||||
offset, len, p_clcb->counter);
|
||||
@@ -1068,6 +1163,14 @@ void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT
|
||||
UINT16 mtu;
|
||||
tGATT_STATUS status = GATT_SUCCESS;
|
||||
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
if (p_tcb->eatt_rx_bearer != 0 && gatt_eatt_is_bearer(p_tcb->eatt_rx_bearer)) {
|
||||
GATT_TRACE_ERROR("ignore MTU response on EATT bearer");
|
||||
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len < GATT_MTU_RSP_MIN_LEN) {
|
||||
GATT_TRACE_ERROR("invalid MTU response PDU received, discard.");
|
||||
status = GATT_INVALID_PDU;
|
||||
@@ -1187,31 +1290,84 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb)
|
||||
**
|
||||
*******************************************************************************/
|
||||
void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
UINT16 len, UINT8 *p_data)
|
||||
UINT16 len, UINT8 *p_data, UINT16 eatt_bearer_lcid)
|
||||
{
|
||||
tGATT_CLCB *p_clcb = NULL;
|
||||
UINT8 rsp_code;
|
||||
UINT8 req_op_code = 0;
|
||||
UINT8 rsp_code = 0;
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
UINT8 cmd_code = 0;
|
||||
UINT16 clcb_idx = 0;
|
||||
#else
|
||||
UNUSED(eatt_bearer_lcid);
|
||||
#endif
|
||||
|
||||
if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF &&
|
||||
op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) {
|
||||
p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code);
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
if (eatt_bearer_lcid != 0) {
|
||||
if (gatt_eatt_release_bearer(p_tcb->peer_bda, eatt_bearer_lcid, &cmd_code, &clcb_idx)) {
|
||||
p_clcb = gatt_clcb_find_by_idx(clcb_idx);
|
||||
if (p_clcb != NULL) {
|
||||
req_op_code = cmd_code;
|
||||
rsp_code = gatt_cmd_to_rsp_code(cmd_code);
|
||||
}
|
||||
}
|
||||
|
||||
rsp_code = gatt_cmd_to_rsp_code(rsp_code);
|
||||
if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) {
|
||||
GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \
|
||||
Request(%02x) Ignored", op_code, rsp_code);
|
||||
/* On an EATT bearer the bearer was released above to locate the
|
||||
* pending request. Since this response is wrong/unexpected, restore
|
||||
* the bearer's busy state so the still-pending request keeps it and
|
||||
* completes on the correct response or the response timer. */
|
||||
if (p_clcb != NULL) {
|
||||
gatt_eatt_mark_busy(p_tcb->peer_bda, eatt_bearer_lcid, cmd_code, clcb_idx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) {
|
||||
GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \
|
||||
Request(%02x) Ignored", op_code, rsp_code);
|
||||
btu_stop_timer (&p_clcb->rsp_timer_ent);
|
||||
p_clcb->retry_count = 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (p_tcb->pending_cl_req == p_tcb->next_slot_inq) {
|
||||
GATT_TRACE_WARNING("ATT - Unexpected response (%02x), no pending command", op_code);
|
||||
return;
|
||||
}
|
||||
|
||||
req_op_code = p_tcb->cl_cmd_q[p_tcb->pending_cl_req].op_code;
|
||||
rsp_code = gatt_cmd_to_rsp_code(req_op_code);
|
||||
|
||||
if (rsp_code != op_code && op_code != GATT_RSP_ERROR) {
|
||||
GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \
|
||||
Request(%02x) Ignored", op_code, rsp_code);
|
||||
|
||||
p_clcb = gatt_cmd_dequeue(p_tcb, &req_op_code);
|
||||
if (p_clcb != NULL) {
|
||||
btu_stop_timer(&p_clcb->rsp_timer_ent);
|
||||
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
|
||||
}
|
||||
gatt_cl_send_next_cmd_inq(p_tcb);
|
||||
return;
|
||||
}
|
||||
|
||||
p_clcb = gatt_cmd_dequeue(p_tcb, &req_op_code);
|
||||
if (p_clcb == NULL) {
|
||||
GATT_TRACE_WARNING("ATT - Response (%02x) with no CLCB", op_code);
|
||||
gatt_cl_send_next_cmd_inq(p_tcb);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
btu_stop_timer (&p_clcb->rsp_timer_ent);
|
||||
p_clcb->retry_count = 0;
|
||||
}
|
||||
}
|
||||
/* the size of the message may not be bigger than the local max PDU size*/
|
||||
/* The message has to be smaller than the agreed MTU, len does not count op_code */
|
||||
if (len >= p_tcb->payload_size) {
|
||||
GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size);
|
||||
if (len >= gatt_get_att_mtu(p_tcb)) {
|
||||
GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, gatt_get_att_mtu(p_tcb));
|
||||
if (op_code != GATT_HANDLE_VALUE_NOTIF && op_code != GATT_HANDLE_VALUE_IND &&
|
||||
op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) {
|
||||
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
|
||||
@@ -1266,13 +1422,21 @@ void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
|
||||
default:
|
||||
GATT_TRACE_ERROR("Unknown opcode = %d", op_code);
|
||||
if (p_clcb != NULL) {
|
||||
gatt_end_operation(p_clcb, GATT_ERROR, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF &&
|
||||
op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) {
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
if (eatt_bearer_lcid == 0)
|
||||
#endif
|
||||
{
|
||||
gatt_cl_send_next_cmd_inq(p_tcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,9 +236,9 @@ static tGATT_STATUS read_attr_value (void *p_attr,
|
||||
status = GATT_NO_RESOURCES;
|
||||
|
||||
if (uuid16 == GATT_UUID_PRI_SERVICE || uuid16 == GATT_UUID_SEC_SERVICE) {
|
||||
len = p_attr16->p_value->uuid.len;
|
||||
if (mtu >= p_attr16->p_value->uuid.len) {
|
||||
gatt_build_uuid_to_stream(&p, p_attr16->p_value->uuid);
|
||||
len = gatt_get_uuid_stream_len(p_attr16->p_value->uuid);
|
||||
if (mtu >= len) {
|
||||
len = gatt_build_uuid_to_stream(&p, p_attr16->p_value->uuid);
|
||||
status = GATT_SUCCESS;
|
||||
}
|
||||
} else if (uuid16 == GATT_UUID_CHAR_DECLARE) {
|
||||
@@ -370,13 +370,19 @@ tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb,
|
||||
|
||||
UINT16_TO_STREAM (p, p_attr->handle);
|
||||
|
||||
{
|
||||
UINT16 max_val_len = (UINT16)(*p_len - 2);
|
||||
if (max_val_len > GATT_MAX_READ_BY_TYPE_VALUE_LEN) {
|
||||
max_val_len = GATT_MAX_READ_BY_TYPE_VALUE_LEN;
|
||||
}
|
||||
status = read_attr_value ((void *)p_attr, 0, &p, FALSE, max_val_len, &len, sec_flag, key_size);
|
||||
/*
|
||||
* ATT Read By Type Response encodes each Handle-Value Pair length in 1 octet.
|
||||
* Therefore a single record must be <= 255 bytes including the 2-byte handle,
|
||||
* i.e. the value length must be <= 253 bytes.
|
||||
*
|
||||
* Limit the maximum value length here so that p_rsp->offset (pair_len) never
|
||||
* exceeds 255 and cannot be truncated when written to the response PDU.
|
||||
*/
|
||||
UINT16 max_value_len = (UINT16)(*p_len - 2);
|
||||
if (max_value_len > GATT_MAX_READ_BY_TYPE_VALUE_LEN) {
|
||||
max_value_len = GATT_MAX_READ_BY_TYPE_VALUE_LEN;
|
||||
}
|
||||
status = read_attr_value((void *)p_attr, 0, &p, FALSE, max_value_len, &len, sec_flag, key_size);
|
||||
if (status == GATT_PENDING) {
|
||||
|
||||
|
||||
@@ -1591,6 +1597,8 @@ static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_
|
||||
memcpy(p_attr->p_value->uuid.uu.uuid128, p_service->uu.uuid128, LEN_UUID_128);
|
||||
}
|
||||
rt = TRUE;
|
||||
} else {
|
||||
deallocate_attr_in_db(p_db, p_attr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,9 @@
|
||||
|
||||
#include "gatt_int.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#include "btm_int.h"
|
||||
#include "btm_ble_int.h"
|
||||
#include "osi/allocator.h"
|
||||
@@ -149,6 +152,10 @@ void gatt_init (void)
|
||||
#endif ///GATTS_INCLUDED == TRUE
|
||||
//init local MTU size
|
||||
gatt_default.local_mtu = GATT_MAX_MTU_SIZE;
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
gatt_eatt_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -166,37 +173,54 @@ void gatt_free(void)
|
||||
{
|
||||
GATT_TRACE_DEBUG("gatt_free()");
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
fixed_queue_free(gatt_cb.srv_chg_clt_q, NULL);
|
||||
fixed_queue_free(gatt_cb.srv_chg_clt_q, osi_free_func);
|
||||
gatt_cb.srv_chg_clt_q = NULL;
|
||||
fixed_queue_free(gatt_cb.pending_new_srv_start_q, osi_free_func);
|
||||
gatt_cb.pending_new_srv_start_q = NULL;
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
|
||||
/* Note: gatt_eatt_deinit() is intentionally invoked from btu_free_core()
|
||||
* BEFORE l2c_free(), because it deregisters L2CAP/GATT resources that
|
||||
* require live L2CAP state. Calling it here (gatt_free runs after l2c_free)
|
||||
* would dereference the already-freed l2c_cb_ptr. */
|
||||
|
||||
list_node_t *p_node = NULL;
|
||||
list_node_t *p_next = NULL;
|
||||
tGATT_TCB *p_tcb = NULL;
|
||||
for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) {
|
||||
p_tcb = list_node(p_node);
|
||||
tGATT_CLCB *p_clcb = NULL;
|
||||
|
||||
for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) {
|
||||
p_tcb = list_node(p_node);
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
fixed_queue_free(p_tcb->pending_enc_clcb, NULL);
|
||||
p_tcb->pending_enc_clcb = NULL;
|
||||
gatt_free_pending_enc_queue(p_tcb);
|
||||
#endif // (SMP_INCLUDED == TRUE)
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
gatt_free_pending_prepare_write_queue(p_tcb);
|
||||
btu_free_timer(&p_tcb->conf_timer_ent);
|
||||
memset(&p_tcb->conf_timer_ent, 0, sizeof(TIMER_LIST_ENT));
|
||||
gatt_dequeue_sr_cmd(p_tcb);
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
|
||||
#if (GATTC_INCLUDED == TRUE)
|
||||
btu_free_timer(&p_tcb->ind_ack_timer_ent);
|
||||
memset(&p_tcb->ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT));
|
||||
#endif // #if (GATTC_INCLUDED == TRUE)
|
||||
#endif // (GATTC_INCLUDED == TRUE)
|
||||
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL);
|
||||
p_tcb->sr_cmd.multi_rsp_q = NULL;
|
||||
#endif /* #if (GATTS_INCLUDED == TRUE) */
|
||||
UNUSED(p_tcb);
|
||||
}
|
||||
list_free(gatt_cb.p_tcb_list);
|
||||
|
||||
for (p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = p_next) {
|
||||
p_clcb = list_node(p_node);
|
||||
p_next = list_next(p_node);
|
||||
if (p_clcb->p_attr_buf) {
|
||||
osi_free(p_clcb->p_attr_buf);
|
||||
p_clcb->p_attr_buf = NULL;
|
||||
}
|
||||
btu_free_timer(&p_clcb->rsp_timer_ent);
|
||||
memset(&p_clcb->rsp_timer_ent, 0, sizeof(TIMER_LIST_ENT));
|
||||
list_remove(gatt_cb.p_clcb_list, p_clcb);
|
||||
}
|
||||
list_free(gatt_cb.p_clcb_list);
|
||||
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
@@ -413,11 +437,9 @@ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr,
|
||||
// p_tcb, p_tcb->pending_enc_clcb, and p_tcb->pending_ind_q have been freed in gatt_cleanup_upon_disc(),
|
||||
// but here p_tcb is get from gatt_allocate_tcb_by_bdaddr(), is too old, so we get p_tcb again
|
||||
p_tcb = gatt_find_tcb_by_addr(bd_addr, transport);
|
||||
if(p_tcb != NULL) {
|
||||
if (p_tcb != NULL) {
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
if(p_tcb->pending_enc_clcb != NULL) {
|
||||
fixed_queue_free(p_tcb->pending_enc_clcb, NULL);
|
||||
}
|
||||
gatt_free_pending_enc_queue(p_tcb);
|
||||
#endif // (SMP_INCLUDED == TRUE)
|
||||
gatt_tcb_free(p_tcb);
|
||||
}
|
||||
@@ -935,6 +957,7 @@ static void gatt_l2cif_congest_cback (UINT16 lcid, BOOLEAN congested)
|
||||
static void gatt_send_conn_cback(tGATT_TCB *p_tcb)
|
||||
{
|
||||
UINT8 i;
|
||||
UINT8 tcb_idx = p_tcb->tcb_idx;
|
||||
tGATT_REG *p_reg;
|
||||
#if (GATT_BG_CONN_DEV == TRUE)
|
||||
tGATT_BG_CONN_DEV *p_bg_dev = NULL;
|
||||
@@ -947,6 +970,9 @@ static void gatt_send_conn_cback(tGATT_TCB *p_tcb)
|
||||
|
||||
/* notifying all applications for the connection up event */
|
||||
for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) {
|
||||
if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) {
|
||||
return;
|
||||
}
|
||||
if (p_reg->in_use) {
|
||||
#if (GATT_BG_CONN_DEV == TRUE)
|
||||
if (p_bg_dev && gatt_is_bg_dev_for_app(p_bg_dev, p_reg->gatt_if)) {
|
||||
@@ -954,14 +980,19 @@ static void gatt_send_conn_cback(tGATT_TCB *p_tcb)
|
||||
}
|
||||
#endif // #if (GATT_BG_CONN_DEV == TRUE)
|
||||
if (p_reg->app_cb.p_conn_cb) {
|
||||
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if);
|
||||
conn_id = GATT_CREATE_CONN_ID(tcb_idx, p_reg->gatt_if);
|
||||
(*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id,
|
||||
TRUE, 0, p_tcb->transport);
|
||||
if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (gatt_get_tcb_by_idx(tcb_idx) != p_tcb) {
|
||||
return;
|
||||
}
|
||||
if (gatt_num_apps_hold_link(p_tcb) && p_tcb->att_lcid == L2CAP_ATT_CID ) {
|
||||
/* disable idle timeout if one or more clients are holding the link disable the idle timer */
|
||||
GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport);
|
||||
@@ -986,7 +1017,7 @@ static void gatt_send_conn_cback(tGATT_TCB *p_tcb)
|
||||
void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf)
|
||||
{
|
||||
UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
|
||||
UINT8 op_code, pseudo_op_code;
|
||||
UINT8 op_code;
|
||||
#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE)
|
||||
UINT16 msg_len;
|
||||
#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE)
|
||||
@@ -998,10 +1029,7 @@ void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf)
|
||||
#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE)
|
||||
STREAM_TO_UINT8(op_code, p);
|
||||
|
||||
/* remove the two MSBs associated with sign write and write cmd */
|
||||
pseudo_op_code = op_code & (~GATT_WRITE_CMD_MASK);
|
||||
|
||||
if (pseudo_op_code < GATT_OP_CODE_MAX) {
|
||||
if (gatt_is_valid_att_opcode(op_code)) {
|
||||
#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE)
|
||||
GATT_TRACE_DEBUG("%s opcode=%x msg_len=%u", __func__, op_code, msg_len);
|
||||
#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE)
|
||||
@@ -1017,7 +1045,7 @@ void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf)
|
||||
#endif ///GATTS_INCLUDED == TRUE
|
||||
} else {
|
||||
#if (GATTC_INCLUDED == TRUE)
|
||||
gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p);
|
||||
gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p, 0);
|
||||
#endif ///GATTC_INCLUDED == TRUE
|
||||
}
|
||||
}
|
||||
@@ -1110,10 +1138,19 @@ tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda)
|
||||
*******************************************************************************/
|
||||
void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt)
|
||||
{
|
||||
tGATT_STATUS status;
|
||||
|
||||
GATT_TRACE_DEBUG("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed );
|
||||
|
||||
if (p_srv_chg_clt->srv_changed) {
|
||||
gatt_send_srv_chg_ind(p_srv_chg_clt->bda);
|
||||
if (!p_srv_chg_clt->srv_changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
status = gatt_send_srv_chg_ind(p_srv_chg_clt->bda);
|
||||
if (status == GATT_BUSY || status == GATT_CONGESTED) {
|
||||
GATT_TRACE_DEBUG("gatt_chk_srv_chg: defer srv chg ind (status=0x%02x)", status);
|
||||
} else if (status != GATT_SUCCESS && status != GATT_PENDING) {
|
||||
GATT_TRACE_WARNING("gatt_chk_srv_chg: send srv chg ind failed (status=0x%02x)", status);
|
||||
}
|
||||
}
|
||||
#endif ///GATTS_INCLUDED == TRUE
|
||||
@@ -1174,7 +1211,6 @@ void gatt_init_srv_chg (void)
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
void gatt_proc_srv_chg (void)
|
||||
{
|
||||
BOOLEAN srv_chg_ind_pending = FALSE;
|
||||
tGATT_TCB *p_tcb;
|
||||
list_node_t *p_node = NULL;
|
||||
|
||||
@@ -1186,11 +1222,11 @@ void gatt_proc_srv_chg (void)
|
||||
for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) {
|
||||
p_tcb = list_node(p_node);
|
||||
if (p_tcb->in_use && p_tcb->ch_state == GATT_CH_OPEN) {
|
||||
srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb);
|
||||
if (!srv_chg_ind_pending) {
|
||||
gatt_send_srv_chg_ind(p_tcb->peer_bda);
|
||||
if (gatt_is_srv_chg_ind_pending(p_tcb) ||
|
||||
GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) {
|
||||
GATT_TRACE_DEBUG ("defer srv chg - indication slot busy");
|
||||
} else {
|
||||
GATT_TRACE_DEBUG ("discard srv chg - already has one in the queue");
|
||||
gatt_send_srv_chg_ind(p_tcb->peer_bda);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
#include "gatt_int.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#include "l2c_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
#define GATT_MTU_REQ_MIN_LEN 2
|
||||
|
||||
|
||||
@@ -50,12 +53,13 @@ tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len)
|
||||
UINT8 *p_m = NULL;
|
||||
UINT16 buf_len;
|
||||
tGATT_STATUS status;
|
||||
UINT16 att_mtu = gatt_get_att_mtu(p_tcb);
|
||||
|
||||
if (len > p_tcb->payload_size){
|
||||
if (len > att_mtu){
|
||||
return GATT_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET);
|
||||
buf_len = (UINT16)(sizeof(BT_HDR) + att_mtu + L2CAP_MIN_OFFSET);
|
||||
if ((p_msg = (BT_HDR *)osi_malloc(buf_len)) == NULL) {
|
||||
return GATT_NO_RESOURCES;
|
||||
}
|
||||
@@ -69,6 +73,48 @@ tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len)
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_sr_next_trans_id
|
||||
**
|
||||
** Description Allocate the next transaction ID in [1, GATT_TRANS_ID_MAX - 1].
|
||||
** Zero is reserved for enqueue failure (sr_cmd busy).
|
||||
**
|
||||
*******************************************************************************/
|
||||
static UINT32 gatt_sr_next_trans_id(tGATT_TCB *p_tcb)
|
||||
{
|
||||
UINT32 trans_id = p_tcb->trans_id % GATT_TRANS_ID_MAX;
|
||||
|
||||
trans_id = (trans_id + 1) % GATT_TRANS_ID_MAX;
|
||||
if (trans_id == 0) {
|
||||
trans_id = 1;
|
||||
}
|
||||
p_tcb->trans_id = trans_id;
|
||||
return trans_id;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_sr_busy_error_code
|
||||
**
|
||||
** Description Pick an ATT error code for a request received while another
|
||||
** server procedure is pending. Common profile codes (0xFE) are
|
||||
** not valid for all request types per ATT Table 3.44.
|
||||
**
|
||||
*******************************************************************************/
|
||||
static UINT8 gatt_sr_busy_error_code(UINT8 op_code)
|
||||
{
|
||||
switch (op_code) {
|
||||
case GATT_REQ_FIND_TYPE_VALUE:
|
||||
return GATT_REQ_NOT_SUPPORTED;
|
||||
case GATT_REQ_FIND_INFO:
|
||||
/* ATT Table 3.44: only Invalid Handle (0x01) and Not Found (0x0A) are valid. */
|
||||
return GATT_NOT_FOUND;
|
||||
default:
|
||||
return GATT_PRC_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_sr_enqueue_cmd
|
||||
@@ -84,21 +130,20 @@ UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle)
|
||||
tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd;
|
||||
UINT32 trans_id = 0;
|
||||
|
||||
if ( (p_cmd->op_code == 0) ||
|
||||
(op_code == GATT_HANDLE_VALUE_CONF)) { /* no pending request */
|
||||
if (op_code == GATT_CMD_WRITE ||
|
||||
op_code == GATT_SIGN_CMD_WRITE ||
|
||||
op_code == GATT_REQ_MTU ||
|
||||
op_code == GATT_HANDLE_VALUE_CONF) {
|
||||
trans_id = ++p_tcb->trans_id;
|
||||
} else {
|
||||
p_cmd->trans_id = ++p_tcb->trans_id;
|
||||
p_cmd->op_code = op_code;
|
||||
p_cmd->handle = handle;
|
||||
p_cmd->status = GATT_NOT_FOUND;
|
||||
p_tcb->trans_id %= GATT_TRANS_ID_MAX;
|
||||
trans_id = p_cmd->trans_id;
|
||||
}
|
||||
/* No-tracking ops do not occupy sr_cmd and may arrive while a request is pending. */
|
||||
if (op_code == GATT_CMD_WRITE ||
|
||||
op_code == GATT_SIGN_CMD_WRITE ||
|
||||
op_code == GATT_REQ_MTU ||
|
||||
op_code == GATT_HANDLE_VALUE_CONF) {
|
||||
return gatt_sr_next_trans_id(p_tcb);
|
||||
}
|
||||
|
||||
if (p_cmd->op_code == 0) {
|
||||
p_cmd->trans_id = gatt_sr_next_trans_id(p_tcb);
|
||||
p_cmd->op_code = op_code;
|
||||
p_cmd->handle = handle;
|
||||
p_cmd->status = GATT_NOT_FOUND;
|
||||
trans_id = p_cmd->trans_id;
|
||||
}
|
||||
|
||||
return trans_id;
|
||||
@@ -442,13 +487,38 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if,
|
||||
|
||||
gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE);
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* If the request arrived on an EATT bearer and this response is deferred
|
||||
* (GATT_PENDING) so eatt_rx_bearer was already cleared after synchronous
|
||||
* handling, restore the TX bearer BEFORE the response is built. Otherwise
|
||||
* gatt_get_att_mtu() below (and inside attp_build_sr_msg) would fall back to
|
||||
* the legacy ATT MTU and truncate/mis-size the response. Cleared after send. */
|
||||
BOOLEAN eatt_routed = FALSE;
|
||||
if (gatt_sr_is_cback_cnt_zero(p_tcb) &&
|
||||
p_tcb->eatt_tx_bearer == 0 && p_tcb->eatt_rx_bearer == 0 &&
|
||||
p_tcb->sr_cmd.eatt_lcid != 0) {
|
||||
p_tcb->eatt_tx_bearer = p_tcb->sr_cmd.eatt_lcid;
|
||||
eatt_routed = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (op_code == GATT_REQ_READ_MULTI) {
|
||||
/* If no error and still waiting, just return */
|
||||
if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) {
|
||||
if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, gatt_get_att_mtu(p_tcb))) {
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
if (eatt_routed) {
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
}
|
||||
#endif
|
||||
return (GATT_SUCCESS);
|
||||
}
|
||||
} else if (op_code == GATT_REQ_READ_MULTI_VAR) {
|
||||
if (!process_read_multi_var_rsp(&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) {
|
||||
if (!process_read_multi_var_rsp(&p_tcb->sr_cmd, status, p_msg, gatt_get_att_mtu(p_tcb))) {
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
if (eatt_routed) {
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
}
|
||||
#endif
|
||||
return (GATT_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
@@ -458,6 +528,19 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if,
|
||||
|
||||
if (op_code == GATT_REQ_EXEC_WRITE && status != GATT_SUCCESS) {
|
||||
gatt_sr_reset_cback_cnt(p_tcb);
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* reset_cback_cnt() may have just forced the count to zero. If the
|
||||
* EATT restore above was skipped because the count was still
|
||||
* non-zero at that point (multi-app EXEC_WRITE), redo it now so the
|
||||
* error response goes out on the originating EATT bearer instead of
|
||||
* falling back to the legacy ATT fixed channel. */
|
||||
if (!eatt_routed && gatt_sr_is_cback_cnt_zero(p_tcb) &&
|
||||
p_tcb->eatt_tx_bearer == 0 && p_tcb->eatt_rx_bearer == 0 &&
|
||||
p_tcb->sr_cmd.eatt_lcid != 0) {
|
||||
p_tcb->eatt_tx_bearer = p_tcb->sr_cmd.eatt_lcid;
|
||||
eatt_routed = TRUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
p_tcb->sr_cmd.status = status;
|
||||
@@ -472,6 +555,8 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if,
|
||||
}
|
||||
}
|
||||
if (gatt_sr_is_cback_cnt_zero(p_tcb)) {
|
||||
/* eatt_tx_bearer was already restored above (before the response was
|
||||
* built) so gatt_get_att_mtu() used the correct EATT MTU. */
|
||||
if ( (p_tcb->sr_cmd.status == GATT_SUCCESS) && (p_tcb->sr_cmd.p_rsp_msg) ) {
|
||||
ret_code = attp_send_sr_msg (p_tcb, p_tcb->sr_cmd.p_rsp_msg);
|
||||
p_tcb->sr_cmd.p_rsp_msg = NULL;
|
||||
@@ -482,6 +567,11 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if,
|
||||
ret_code = gatt_send_error_rsp (p_tcb, status, op_code, p_tcb->sr_cmd.handle, FALSE);
|
||||
}
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
if (eatt_routed) {
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
}
|
||||
#endif
|
||||
gatt_dequeue_sr_cmd(p_tcb);
|
||||
}
|
||||
|
||||
@@ -515,6 +605,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
BOOLEAN sr_cmd_already_dequeued = FALSE;
|
||||
tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL;
|
||||
tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL;
|
||||
tGATTS_DATA sr_data = {0};
|
||||
|
||||
/* Fix: Validate minimum length (flags: 1 byte) */
|
||||
if (len < 1) {
|
||||
@@ -538,6 +629,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
|
||||
/* mask the flag */
|
||||
flag &= GATT_PREP_WRITE_EXEC;
|
||||
sr_data.exec_write = flag;
|
||||
|
||||
prepare_record = &(p_tcb->prepare_write_record);
|
||||
queue_num = fixed_queue_length(prepare_record->queue);
|
||||
@@ -614,7 +706,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
gatt_sr_send_req_callback(conn_id,
|
||||
trans_id,
|
||||
GATTS_REQ_TYPE_WRITE_EXEC,
|
||||
(tGATTS_DATA *)&flag);
|
||||
&sr_data);
|
||||
p_tcb->prep_cnt[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -696,7 +788,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
gatt_sr_send_req_callback(conn_id,
|
||||
trans_id,
|
||||
GATTS_REQ_TYPE_WRITE_EXEC,
|
||||
(tGATTS_DATA *)&flag);
|
||||
&sr_data);
|
||||
p_tcb->prep_cnt[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -724,6 +816,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
tGATT_STATUS err = GATT_SUCCESS;
|
||||
UINT8 sec_flag, key_size;
|
||||
tGATTS_RSP *p_msg;
|
||||
BOOLEAN sr_cmd_enqueued = FALSE;
|
||||
|
||||
GATT_TRACE_DEBUG("gatt_process_read_multi_req" );
|
||||
p_tcb->sr_cmd.multi_req.num_handles = 0;
|
||||
@@ -782,6 +875,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
|
||||
if (err == GATT_SUCCESS) {
|
||||
if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) {
|
||||
sr_cmd_enqueued = TRUE;
|
||||
gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/
|
||||
|
||||
for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) {
|
||||
@@ -805,12 +899,16 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
|
||||
if (err == GATT_SUCCESS || err == GATT_STACK_RSP) {
|
||||
gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if , trans_id, op_code, GATT_SUCCESS, p_msg);
|
||||
} else if (err != GATT_PENDING && err != GATT_BUSY) {
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
}
|
||||
/* either not using or done using the buffer, release it now */
|
||||
osi_free(p_msg);
|
||||
} else {
|
||||
err = GATT_NO_RESOURCES;
|
||||
gatt_dequeue_sr_cmd(p_tcb);
|
||||
sr_cmd_enqueued = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -820,7 +918,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
}
|
||||
/* in theroy BUSY is not possible(should already been checked), protected check */
|
||||
if (err != GATT_SUCCESS && err != GATT_STACK_RSP && err != GATT_PENDING && err != GATT_BUSY) {
|
||||
gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE);
|
||||
gatt_send_error_rsp(p_tcb, err, op_code, handle, sr_cmd_enqueued);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,7 +958,7 @@ static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_
|
||||
p_rcb->type == GATT_UUID_PRI_SERVICE) {
|
||||
if ((p_uuid = gatts_get_service_uuid (p_rcb->p_db)) != NULL) {
|
||||
if (op_code == GATT_REQ_READ_BY_GRP_TYPE) {
|
||||
handle_len = 4 + p_uuid->len;
|
||||
handle_len = 4 + gatt_get_uuid_stream_len(*p_uuid);
|
||||
}
|
||||
|
||||
/* get the length byte in the response */
|
||||
@@ -875,7 +973,7 @@ static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_
|
||||
}
|
||||
}
|
||||
|
||||
if (p_msg->len + p_msg->offset <= p_tcb->payload_size &&
|
||||
if (p_msg->len + p_msg->offset <= gatt_get_att_mtu(p_tcb) &&
|
||||
handle_len == p_msg->offset) {
|
||||
if (op_code != GATT_REQ_FIND_TYPE_VALUE ||
|
||||
gatt_uuid_compare(value, *p_uuid)) {
|
||||
@@ -1053,7 +1151,7 @@ void gatts_process_primary_service_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 l
|
||||
UINT16 s_hdl = 0, e_hdl = 0;
|
||||
tBT_UUID uuid, value, primary_service = {LEN_UUID_16, {GATT_UUID_PRI_SERVICE}};
|
||||
BT_HDR *p_msg = NULL;
|
||||
UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET);
|
||||
UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + gatt_get_att_mtu(p_tcb) + L2CAP_MIN_OFFSET);
|
||||
|
||||
memset (&value, 0, sizeof(tBT_UUID));
|
||||
reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl);
|
||||
@@ -1119,7 +1217,7 @@ static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
reason = gatts_validate_packet_format(op_code, &len, &p_data, NULL, &s_hdl, &e_hdl);
|
||||
|
||||
if (reason == GATT_SUCCESS) {
|
||||
buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET);
|
||||
buf_len = (UINT16)(sizeof(BT_HDR) + gatt_get_att_mtu(p_tcb) + L2CAP_MIN_OFFSET);
|
||||
|
||||
if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) {
|
||||
reason = GATT_NO_RESOURCES;
|
||||
@@ -1130,7 +1228,7 @@ static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
*p ++ = op_code + 1;
|
||||
p_msg->len = 2;
|
||||
|
||||
buf_len = p_tcb->payload_size - 2;
|
||||
buf_len = gatt_get_att_mtu(p_tcb) - 2;
|
||||
|
||||
p_srv = p_list->p_first;
|
||||
|
||||
@@ -1140,11 +1238,15 @@ static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
if (p_rcb->in_use &&
|
||||
!(p_rcb->s_hdl > e_hdl ||
|
||||
p_rcb->e_hdl < s_hdl)) {
|
||||
reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl);
|
||||
if (reason == GATT_NO_RESOURCES) {
|
||||
tGATT_STATUS build_status = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl);
|
||||
if (build_status == GATT_SUCCESS) {
|
||||
reason = GATT_SUCCESS;
|
||||
} else if (build_status == GATT_NO_RESOURCES) {
|
||||
reason = GATT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
/* GATT_NOT_FOUND for this service: keep reason (do not discard
|
||||
* attributes already added from other services). */
|
||||
}
|
||||
p_srv = p_srv->p_next;
|
||||
}
|
||||
@@ -1181,6 +1283,15 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data)
|
||||
UINT8 *p = p_data, i;
|
||||
BT_HDR *p_buf;
|
||||
UINT16 conn_id;
|
||||
tGATTS_DATA sr_data = {0};
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Exchange MTU applies to Legacy ATT bearer only (Core Spec Vol 3 Part G 5.3). */
|
||||
if (p_tcb->eatt_rx_bearer != 0 && gatt_eatt_is_bearer(p_tcb->eatt_rx_bearer)) {
|
||||
gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, GATT_REQ_MTU, 0, FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* BR/EDR connection, send error response */
|
||||
if (p_tcb->att_lcid != L2CAP_ATT_CID) {
|
||||
@@ -1210,11 +1321,12 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data)
|
||||
/* Notify all registered application with new MTU size. Us a transaction ID */
|
||||
/* of 0, as no response is allowed from applications */
|
||||
|
||||
sr_data.mtu = p_tcb->payload_size;
|
||||
for (i = 0; i < GATT_MAX_APPS; i ++) {
|
||||
if (gatt_cb.cl_rcb[i].in_use ) {
|
||||
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if);
|
||||
gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU,
|
||||
(tGATTS_DATA *)&p_tcb->payload_size);
|
||||
&sr_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1241,7 +1353,12 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
{
|
||||
tBT_UUID uuid;
|
||||
tGATT_SR_REG *p_rcb;
|
||||
UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET),
|
||||
/* Cache the MTU once: gatt_get_att_mtu() reads dynamic EATT bearer state, so
|
||||
* calling it separately for the allocation size and the write limit could
|
||||
* (if it ever changed between calls) let buf_len exceed the allocated buffer.
|
||||
* One read keeps both consistent, matching gatt_send_packet(). */
|
||||
UINT16 att_mtu = gatt_get_att_mtu(p_tcb);
|
||||
UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + att_mtu + L2CAP_MIN_OFFSET),
|
||||
buf_len,
|
||||
s_hdl, e_hdl, err_hdl = 0;
|
||||
BT_HDR *p_msg = NULL;
|
||||
@@ -1274,7 +1391,7 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
*p ++ = op_code + 1;
|
||||
/* reserve length byte */
|
||||
p_msg->len = 2;
|
||||
buf_len = p_tcb->payload_size - 2;
|
||||
buf_len = att_mtu - 2;
|
||||
|
||||
reason = GATT_NOT_FOUND;
|
||||
|
||||
@@ -1317,7 +1434,13 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len,
|
||||
}
|
||||
p_srv = p_srv->p_next;
|
||||
}
|
||||
*p = (UINT8)p_msg->offset;
|
||||
/* Defensive: Read By Type response record length is a 1-octet field (<= 255). */
|
||||
if (p_msg->offset > UINT8_MAX) {
|
||||
GATT_TRACE_ERROR("%s: invalid ReadByType pair_len=%u (>255)", __func__, p_msg->offset);
|
||||
reason = GATT_INVALID_PDU;
|
||||
} else {
|
||||
*p = (UINT8)p_msg->offset;
|
||||
}
|
||||
p_msg->offset = L2CAP_MIN_OFFSET;
|
||||
}
|
||||
}
|
||||
@@ -1614,7 +1737,7 @@ void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 hand
|
||||
static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code,
|
||||
UINT16 handle, UINT16 len, UINT8 *p_data)
|
||||
{
|
||||
UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET);
|
||||
UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + gatt_get_att_mtu(p_tcb) + L2CAP_MIN_OFFSET);
|
||||
tGATT_STATUS reason;
|
||||
BT_HDR *p_msg = NULL;
|
||||
UINT8 sec_flag, key_size, *p;
|
||||
@@ -1639,7 +1762,7 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8
|
||||
p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET;
|
||||
*p ++ = op_code + 1;
|
||||
p_msg->len = 1;
|
||||
buf_len = p_tcb->payload_size - 1;
|
||||
buf_len = gatt_get_att_mtu(p_tcb) - 1;
|
||||
|
||||
gatt_sr_get_sec_info(p_tcb->peer_bda,
|
||||
p_tcb->transport,
|
||||
@@ -1769,7 +1892,7 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb )
|
||||
void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb )
|
||||
{
|
||||
tGATTS_SRV_CHG_REQ req;
|
||||
tGATTS_SRV_CHG *p_buf = NULL;
|
||||
@@ -1842,6 +1965,10 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code)
|
||||
for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) {
|
||||
if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) {
|
||||
trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle);
|
||||
if (trans_id == 0) {
|
||||
GATT_TRACE_ERROR("%s: no trans_id for handle conf 0x%04x", __func__, handle);
|
||||
continue;
|
||||
}
|
||||
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if);
|
||||
tGATTS_DATA p_data = {0};
|
||||
p_data.handle = handle;
|
||||
@@ -1850,6 +1977,15 @@ void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Retry Service Changed if a previous attempt was deferred (GATT_BUSY). */
|
||||
{
|
||||
tGATTS_SRV_CHG *p_srv_chg_clt;
|
||||
|
||||
if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) {
|
||||
gatt_chk_srv_chg(p_srv_chg_clt);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GATT_TRACE_ERROR("unexpected handle value confirmation");
|
||||
}
|
||||
@@ -1950,16 +2086,20 @@ static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
UINT16 len, UINT8 *p_data)
|
||||
{
|
||||
/* there is pending command, discard this one */
|
||||
if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) {
|
||||
GATT_TRACE_WARNING("%s discard command opcode=%02x", __func__, op_code);
|
||||
return;
|
||||
if (!gatt_sr_cmd_empty(p_tcb)) {
|
||||
if (op_code == GATT_CMD_WRITE || op_code == GATT_SIGN_CMD_WRITE) {
|
||||
/* ATT commands have no flow control and may arrive while a request is pending. */
|
||||
} else if (op_code != GATT_HANDLE_VALUE_CONF && op_code != GATT_REQ_MTU) {
|
||||
GATT_TRACE_WARNING("%s reject opcode=%02x, procedure in progress", __func__, op_code);
|
||||
gatt_send_error_rsp(p_tcb, gatt_sr_busy_error_code(op_code), op_code, 0, FALSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* the size of the message may not be bigger than the local max PDU size*/
|
||||
/* The message has to be smaller than the agreed MTU, len does not include op code */
|
||||
if (len >= p_tcb->payload_size) {
|
||||
GATT_TRACE_ERROR("server receive invalid PDU size:%d pdu size:%d", len + 1, p_tcb->payload_size );
|
||||
if (len >= gatt_get_att_mtu(p_tcb)) {
|
||||
GATT_TRACE_ERROR("server receive invalid PDU size:%d pdu size:%d", len + 1, gatt_get_att_mtu(p_tcb) );
|
||||
/* for invalid request expecting response, send it now */
|
||||
if (op_code != GATT_CMD_WRITE &&
|
||||
op_code != GATT_SIGN_CMD_WRITE &&
|
||||
|
||||
@@ -38,6 +38,14 @@ static const char *gatt_get_attr_name(UINT16 uuid)
|
||||
return "Unknown Attribute";
|
||||
}
|
||||
|
||||
/* GATT declaration attribute types (Primary Service, Characteristic, etc.) are
|
||||
* always stored with 16-bit attribute UUID. Do not compare p_attr->uuid unless
|
||||
* uuid_type is 16, or a 128/32-bit characteristic UUID may be misread. */
|
||||
static BOOLEAN gatt_attr_is_uuid16(const tGATT_ATTR16 *p_attr, UINT16 uuid16)
|
||||
{
|
||||
return (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16 && p_attr->uuid == uuid16);
|
||||
}
|
||||
|
||||
static void attr_uuid_to_bt_uuid(void *p_attr, tBT_UUID *p_uuid)
|
||||
{
|
||||
tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr;
|
||||
@@ -56,15 +64,6 @@ static void attr_uuid_to_bt_uuid(void *p_attr, tBT_UUID *p_uuid)
|
||||
}
|
||||
}
|
||||
|
||||
static UINT8 get_uuid_stream_len(tBT_UUID uuid)
|
||||
{
|
||||
// gatt_build_uuid_to_stream always converts 32-bit UUID to 128-bit UUID
|
||||
if (uuid.len == LEN_UUID_32) {
|
||||
return LEN_UUID_128;
|
||||
}
|
||||
return uuid.len;
|
||||
}
|
||||
|
||||
static size_t calculate_database_info_size(void)
|
||||
{
|
||||
UINT8 i;
|
||||
@@ -77,31 +76,45 @@ static size_t calculate_database_info_size(void)
|
||||
if (p_db && p_db->p_attr_list) {
|
||||
p_attr = (tGATT_ATTR16 *)p_db->p_attr_list;
|
||||
while (p_attr) {
|
||||
if (p_attr->uuid == GATT_UUID_PRI_SERVICE ||
|
||||
p_attr->uuid == GATT_UUID_SEC_SERVICE) {
|
||||
if (gatt_attr_is_uuid16(p_attr, GATT_UUID_PRI_SERVICE) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_SEC_SERVICE)) {
|
||||
// Service declaration
|
||||
len += 4 + get_uuid_stream_len(p_attr->p_value->uuid);
|
||||
} else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) {
|
||||
if (p_attr->p_value == NULL) {
|
||||
GATT_TRACE_WARNING("%s: service decl at handle %u missing p_value",
|
||||
__func__, p_attr->handle);
|
||||
} else {
|
||||
len += 4 + gatt_get_uuid_stream_len(p_attr->p_value->uuid);
|
||||
}
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_INCLUDE_SERVICE)) {
|
||||
// Included service declaration
|
||||
len += 8 + get_uuid_stream_len(p_attr->p_value->incl_handle.service_type);
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) {
|
||||
if (p_attr->p_value == NULL) {
|
||||
GATT_TRACE_WARNING("%s: include service at handle %u missing p_value",
|
||||
__func__, p_attr->handle);
|
||||
} else {
|
||||
len += 8 + gatt_get_uuid_stream_len(p_attr->p_value->incl_handle.service_type);
|
||||
}
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_DECLARE)) {
|
||||
tBT_UUID char_uuid = {0};
|
||||
if (p_attr->p_next == NULL) {
|
||||
if (p_attr->p_value == NULL) {
|
||||
GATT_TRACE_WARNING("%s: char decl at handle %u missing p_value",
|
||||
__func__, p_attr->handle);
|
||||
} else if (p_attr->p_next == NULL) {
|
||||
GATT_TRACE_ERROR("%s: malformed DB, char decl at handle %u has no value attr",
|
||||
__func__, p_attr->handle);
|
||||
break;
|
||||
} else {
|
||||
p_attr = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid);
|
||||
len += 7 + gatt_get_uuid_stream_len(char_uuid);
|
||||
}
|
||||
p_attr = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid);
|
||||
len += 7 + get_uuid_stream_len(char_uuid);
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) {
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_DESCRIPTION) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_CLIENT_CONFIG) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_SRVR_CONFIG) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_PRESENT_FORMAT) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_AGG_FORMAT)) {
|
||||
// Descriptor
|
||||
len += 4;
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) {
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_EXT_PROP)) {
|
||||
// Descriptor
|
||||
len += 6;
|
||||
}
|
||||
@@ -124,47 +137,55 @@ static void fill_database_info(UINT8 *p_data)
|
||||
if (p_db && p_db->p_attr_list) {
|
||||
p_attr = (tGATT_ATTR16 *)p_db->p_attr_list;
|
||||
while (p_attr) {
|
||||
if (p_attr->uuid == GATT_UUID_PRI_SERVICE ||
|
||||
p_attr->uuid == GATT_UUID_SEC_SERVICE) {
|
||||
if (gatt_attr_is_uuid16(p_attr, GATT_UUID_PRI_SERVICE) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_SEC_SERVICE)) {
|
||||
// Service declaration
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->uuid);
|
||||
gatt_build_uuid_to_stream(&p_data, p_attr->p_value->uuid);
|
||||
} else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) {
|
||||
if (p_attr->p_value != NULL) {
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->uuid);
|
||||
gatt_build_uuid_to_stream(&p_data, p_attr->p_value->uuid);
|
||||
}
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_INCLUDE_SERVICE)) {
|
||||
// Included service declaration
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, GATT_UUID_INCLUDE_SERVICE);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.s_handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.e_handle);
|
||||
gatt_build_uuid_to_stream(&p_data, p_attr->p_value->incl_handle.service_type);
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) {
|
||||
if (p_attr->p_value != NULL) {
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, GATT_UUID_INCLUDE_SERVICE);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.s_handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.e_handle);
|
||||
gatt_build_uuid_to_stream(&p_data, p_attr->p_value->incl_handle.service_type);
|
||||
}
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_DECLARE)) {
|
||||
tBT_UUID char_uuid = {0};
|
||||
if (p_attr->p_next == NULL) {
|
||||
if (p_attr->p_value == NULL) {
|
||||
GATT_TRACE_WARNING("%s: char decl at handle %u missing p_value",
|
||||
__func__, p_attr->handle);
|
||||
} else if (p_attr->p_next == NULL) {
|
||||
GATT_TRACE_ERROR("%s: malformed DB, char decl at handle %u has no value attr",
|
||||
__func__, p_attr->handle);
|
||||
break;
|
||||
} else {
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, GATT_UUID_CHAR_DECLARE);
|
||||
UINT8_TO_STREAM(p_data, p_attr->p_value->char_decl.property);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->char_decl.char_val_handle);
|
||||
p_attr = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid);
|
||||
gatt_build_uuid_to_stream(&p_data, char_uuid);
|
||||
}
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, GATT_UUID_CHAR_DECLARE);
|
||||
UINT8_TO_STREAM(p_data, p_attr->p_value->char_decl.property);
|
||||
UINT16_TO_STREAM(p_data, p_attr->p_value->char_decl.char_val_handle);
|
||||
p_attr = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid);
|
||||
gatt_build_uuid_to_stream(&p_data, char_uuid);
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT ||
|
||||
p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) {
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_DESCRIPTION) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_CLIENT_CONFIG) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_SRVR_CONFIG) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_PRESENT_FORMAT) ||
|
||||
gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_AGG_FORMAT)) {
|
||||
// Descriptor
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->uuid);
|
||||
} else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) {
|
||||
} else if (gatt_attr_is_uuid16(p_attr, GATT_UUID_CHAR_EXT_PROP)) {
|
||||
// Descriptor
|
||||
UINT16_TO_STREAM(p_data, p_attr->handle);
|
||||
UINT16_TO_STREAM(p_data, p_attr->uuid);
|
||||
// TODO: process extended properties descriptor
|
||||
if (p_attr->p_value->attr_val.attr_len == 2) {
|
||||
if (p_attr->p_value != NULL && p_attr->p_value->attr_val.attr_val != NULL
|
||||
&& p_attr->p_value->attr_val.attr_len == 2) {
|
||||
memcpy(p_data, p_attr->p_value->attr_val.attr_val, 2);
|
||||
p_data += 2;
|
||||
} else {
|
||||
@@ -180,8 +201,8 @@ static void fill_database_info(UINT8 *p_data)
|
||||
tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash)
|
||||
{
|
||||
UINT8 tmp;
|
||||
UINT16 i;
|
||||
UINT16 j;
|
||||
size_t i;
|
||||
size_t j;
|
||||
size_t len;
|
||||
UINT8 *data_buf = NULL;
|
||||
|
||||
@@ -194,22 +215,31 @@ tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash)
|
||||
|
||||
data_buf = (UINT8 *)osi_malloc(len);
|
||||
if (data_buf == NULL) {
|
||||
GATT_TRACE_ERROR ("%s failed to allocate buffer (%u)\n", __func__, len);
|
||||
GATT_TRACE_ERROR ("%s failed to allocate buffer (%u)\n", __func__, (unsigned)len);
|
||||
return GATT_NO_RESOURCES;
|
||||
}
|
||||
|
||||
fill_database_info(data_buf);
|
||||
|
||||
// reverse database info
|
||||
for (i = 0, j = len-1; i < j; i++, j--) {
|
||||
for (i = 0, j = len - 1; i < j; i++, j--) {
|
||||
tmp = data_buf[i];
|
||||
data_buf[i] = data_buf[j];
|
||||
data_buf[j] = tmp;
|
||||
}
|
||||
|
||||
#if SMP_INCLUDED == TRUE
|
||||
if (len > UINT16_MAX) {
|
||||
GATT_TRACE_ERROR("%s: database info too large (%u)", __func__, (unsigned)len);
|
||||
osi_free(data_buf);
|
||||
return GATT_NO_RESOURCES;
|
||||
}
|
||||
|
||||
BT_OCTET16 key = {0};
|
||||
aes_cipher_msg_auth_code(key, data_buf, len, 16, hash);
|
||||
if (!aes_cipher_msg_auth_code(key, data_buf, (UINT16)len, 16, hash)) {
|
||||
osi_free(data_buf);
|
||||
return GATT_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
osi_free(data_buf);
|
||||
@@ -228,25 +258,38 @@ void gatts_show_local_database(void)
|
||||
if (p_db && p_db->p_attr_list) {
|
||||
p_attr = (tGATT_ATTR16 *)p_db->p_attr_list;
|
||||
while (p_attr) {
|
||||
if (p_attr->uuid_type != GATT_ATTR_UUID_TYPE_16) {
|
||||
p_attr = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (p_attr->uuid) {
|
||||
case GATT_UUID_PRI_SERVICE:
|
||||
case GATT_UUID_SEC_SERVICE:
|
||||
// Service declaration
|
||||
printf("%s\n", gatt_get_attr_name(p_attr->uuid));
|
||||
printf("\tuuid %s\n", gatt_uuid_to_str(&p_attr->p_value->uuid));
|
||||
if (p_attr->p_value != NULL) {
|
||||
printf("\tuuid %s\n", gatt_uuid_to_str(&p_attr->p_value->uuid));
|
||||
}
|
||||
printf("\thandle %d\n", p_attr->handle);
|
||||
printf("\tend_handle %d\n",p_db->end_handle-1);
|
||||
printf("\tend_handle %d\n", p_db->end_handle - 1);
|
||||
break;
|
||||
case GATT_UUID_INCLUDE_SERVICE:
|
||||
// Included service declaration
|
||||
printf("%s\n", gatt_get_attr_name(p_attr->uuid));
|
||||
printf("\tuuid %s\t", gatt_uuid_to_str(&p_attr->p_value->incl_handle.service_type));
|
||||
printf("\thandle %d\n", p_attr->p_value->incl_handle.s_handle);
|
||||
printf("\tend_handle %d\n", p_attr->p_value->incl_handle.e_handle);
|
||||
if (p_attr->p_value != NULL) {
|
||||
printf("\tuuid %s\t", gatt_uuid_to_str(&p_attr->p_value->incl_handle.service_type));
|
||||
printf("\thandle %d\n", p_attr->p_value->incl_handle.s_handle);
|
||||
printf("\tend_handle %d\n", p_attr->p_value->incl_handle.e_handle);
|
||||
}
|
||||
break;
|
||||
case GATT_UUID_CHAR_DECLARE: {
|
||||
tBT_UUID char_uuid = {0};
|
||||
tGATT_ATTR16 *p_char_val;
|
||||
if (p_attr->p_value == NULL) {
|
||||
printf("characteristic (malformed - no decl value)\n");
|
||||
break;
|
||||
}
|
||||
p_char_val = (tGATT_ATTR16 *)p_attr->p_next;
|
||||
if (p_char_val == NULL) {
|
||||
printf("characteristic (malformed - no value attr)\n");
|
||||
@@ -258,7 +301,8 @@ void gatts_show_local_database(void)
|
||||
printf("\tuuid %s\n", gatt_uuid_to_str(&char_uuid));
|
||||
printf("\tdef_handle %d\n", p_attr->handle);
|
||||
printf("\tval_handle %d\n", p_attr->p_value->char_decl.char_val_handle);
|
||||
printf("\tperm 0x%04x, prop 0x%02x\n", p_char_val->permission, p_attr->p_value->char_decl.property);
|
||||
printf("\tperm 0x%04x, prop 0x%02x\n", p_char_val->permission,
|
||||
p_attr->p_value->char_decl.property);
|
||||
break;
|
||||
}
|
||||
case GATT_UUID_CHAR_EXT_PROP:
|
||||
@@ -270,6 +314,8 @@ void gatts_show_local_database(void)
|
||||
printf("%s\n", gatt_get_attr_name(p_attr->uuid));
|
||||
printf("\thandle %d\n", p_attr->handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p_attr = (tGATT_ATTR16 *) p_attr->p_next;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
#include "stack/gattdefs.h"
|
||||
#include "stack/sdp_api.h"
|
||||
#include "btm_int.h"
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
#include "gatt_eatt_int.h"
|
||||
#endif
|
||||
/* check if [x, y] and [a, b] have overlapping range */
|
||||
#define GATT_VALIDATE_HANDLE_RANGE(x, y, a, b) (y >= a && x <= b)
|
||||
|
||||
@@ -131,6 +134,119 @@ void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb)
|
||||
p_tcb->prepare_write_record.total_num = 0;
|
||||
p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_attr_in_svc_db
|
||||
**
|
||||
** Description Return TRUE if p_attr belongs to the given service database.
|
||||
**
|
||||
*******************************************************************************/
|
||||
static BOOLEAN gatt_attr_in_svc_db(tGATT_SVC_DB *p_db, tGATT_ATTR16 *p_attr)
|
||||
{
|
||||
tGATT_ATTR16 *p;
|
||||
|
||||
if (p_db == NULL || p_attr == NULL || p_db->p_attr_list == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (p = (tGATT_ATTR16 *)p_db->p_attr_list; p != NULL; p = p->p_next) {
|
||||
if (p == p_attr) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_purge_prepare_write_for_svc_db
|
||||
**
|
||||
** Description Remove queued prepare-write entries that reference attributes
|
||||
** in p_db. Must be called before freeing that service database.
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void gatt_purge_prepare_write_for_svc_db(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db)
|
||||
{
|
||||
tGATT_PREPARE_WRITE_QUEUE_DATA *queue_data;
|
||||
tGATT_PREPARE_WRITE_RECORD *prepare_record;
|
||||
fixed_queue_t *old_queue;
|
||||
fixed_queue_t *new_queue;
|
||||
UINT16 purged = 0;
|
||||
|
||||
if (p_tcb == NULL || p_db == NULL || !p_tcb->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
prepare_record = &p_tcb->prepare_write_record;
|
||||
old_queue = prepare_record->queue;
|
||||
if (old_queue == NULL || fixed_queue_is_empty(old_queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
new_queue = fixed_queue_new(QUEUE_SIZE_MAX);
|
||||
if (new_queue == NULL) {
|
||||
GATT_TRACE_ERROR("%s: failed to allocate queue, dropping all prepare writes", __func__);
|
||||
gatt_free_pending_prepare_write_queue(p_tcb);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!fixed_queue_is_empty(old_queue)) {
|
||||
queue_data = fixed_queue_dequeue(old_queue, FIXED_QUEUE_MAX_TIMEOUT);
|
||||
if (gatt_attr_in_svc_db(p_db, queue_data->p_attr)) {
|
||||
osi_free(queue_data);
|
||||
purged++;
|
||||
} else {
|
||||
if (!fixed_queue_enqueue(new_queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT)) {
|
||||
GATT_TRACE_ERROR("%s: failed to re-queue prepare write entry", __func__);
|
||||
osi_free(queue_data);
|
||||
purged++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixed_queue_free(old_queue, NULL);
|
||||
if (fixed_queue_is_empty(new_queue)) {
|
||||
fixed_queue_free(new_queue, NULL);
|
||||
prepare_record->queue = NULL;
|
||||
} else {
|
||||
prepare_record->queue = new_queue;
|
||||
}
|
||||
|
||||
if (purged > 0) {
|
||||
if (prepare_record->total_num >= purged) {
|
||||
prepare_record->total_num -= purged;
|
||||
} else {
|
||||
prepare_record->total_num = 0;
|
||||
}
|
||||
if (prepare_record->queue == NULL && prepare_record->total_num == 0) {
|
||||
prepare_record->error_code_app = GATT_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_purge_prepare_write_before_free_db
|
||||
**
|
||||
** Description Purge prepare-write queue entries for p_db on all active TCBs.
|
||||
** Call before freeing a service database.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void gatt_purge_prepare_write_before_free_db(tGATT_SVC_DB *p_db)
|
||||
{
|
||||
list_node_t *p_node;
|
||||
list_node_t *p_next;
|
||||
tGATT_TCB *p_tcb;
|
||||
|
||||
for (p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = p_next) {
|
||||
p_tcb = list_node(p_node);
|
||||
p_next = list_next(p_node);
|
||||
if (p_tcb->in_use) {
|
||||
gatt_purge_prepare_write_for_svc_db(p_tcb, p_db);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
@@ -410,6 +526,7 @@ void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p)
|
||||
p_value = p_attr->p_value;
|
||||
if ((p_value != NULL) && (p_value->attr_val.attr_val != NULL)){
|
||||
osi_free(p_value->attr_val.attr_val);
|
||||
p_value->attr_val.attr_val = NULL;
|
||||
}
|
||||
}
|
||||
p_attr = p_attr->p_next;
|
||||
@@ -457,6 +574,8 @@ void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id)
|
||||
if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) {
|
||||
/* Remove from linked list first */
|
||||
gatt_remove_an_item_from_list(p_list_info, p_elem);
|
||||
/* Drop prepare-write queue entries pointing into this DB before free */
|
||||
gatt_purge_prepare_write_before_free_db(&p_elem->svc_db);
|
||||
/* Free attribute value buffers */
|
||||
gatt_free_attr_value_buffer(p_elem);
|
||||
/* Free the handle buffer completely (including svc_buffer and setting in_use = FALSE) */
|
||||
@@ -487,7 +606,7 @@ BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM
|
||||
|
||||
p_svc_uuid = gatts_get_service_uuid (p_rcb->p_db);
|
||||
|
||||
if (gatt_uuid_compare(value, *p_svc_uuid)) {
|
||||
if (p_svc_uuid && gatt_uuid_compare(value, *p_svc_uuid)) {
|
||||
is_last_attribute = FALSE;
|
||||
break;
|
||||
|
||||
@@ -1126,6 +1245,23 @@ BOOLEAN gatt_uuid_compare (tBT_UUID src, tBT_UUID tar)
|
||||
return (memcmp(ps, pt, LEN_UUID_128) == 0);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_get_uuid_stream_len
|
||||
**
|
||||
** Description Get the number of bytes gatt_build_uuid_to_stream writes.
|
||||
**
|
||||
** Returns UUID stream length.
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT8 gatt_get_uuid_stream_len(tBT_UUID uuid)
|
||||
{
|
||||
if (uuid.len == LEN_UUID_32) {
|
||||
return LEN_UUID_128;
|
||||
}
|
||||
return uuid.len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_build_uuid_to_stream
|
||||
@@ -1256,9 +1392,40 @@ void gatt_start_rsp_timer(UINT16 clcb_idx)
|
||||
void gatt_start_conf_timer(tGATT_TCB *p_tcb)
|
||||
{
|
||||
p_tcb->conf_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb;
|
||||
btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP,
|
||||
btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_CONF,
|
||||
GATT_WAIT_FOR_RSP_TOUT);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function gatt_conf_timeout
|
||||
**
|
||||
** Description Called when GATT wait for indication confirmation timer expires
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void gatt_conf_timeout(TIMER_LIST_ENT *p_tle)
|
||||
{
|
||||
tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param;
|
||||
|
||||
if (p_tcb == NULL || gatt_get_tcb_by_idx(p_tcb->tcb_idx) != p_tcb) {
|
||||
GATT_TRACE_WARNING("gatt_conf_timeout tcb is already deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) {
|
||||
/* Server-only remotes may ignore Service Changed indication; do not disconnect. */
|
||||
GATT_TRACE_WARNING("gatt_conf_timeout Service Changed indication timed out, not disconnecting");
|
||||
p_tcb->indicate_handle = 0;
|
||||
gatts_proc_srv_chg_ind_ack(p_tcb);
|
||||
return;
|
||||
}
|
||||
|
||||
GATT_TRACE_WARNING("gatt_conf_timeout handle=%u disconnecting...", p_tcb->indicate_handle);
|
||||
p_tcb->indicate_handle = 0;
|
||||
gatt_disconnect(p_tcb);
|
||||
}
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
|
||||
#if (GATTC_INCLUDED == TRUE)
|
||||
@@ -1301,6 +1468,20 @@ void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle)
|
||||
p_clcb->retry_count < GATT_REQ_RETRY_LIMIT) {
|
||||
UINT8 rsp_code;
|
||||
GATT_TRACE_WARNING("gatt_rsp_timeout retry discovery primary service");
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Operations sent over an EATT bearer are tracked in the EATT bearer
|
||||
* table, not the legacy cl_cmd_q. Calling gatt_cmd_dequeue for them would
|
||||
* consume an unrelated legacy command and report "out of sync". Release
|
||||
* the EATT bearer and retry directly (gatt_act_discovery re-acquires a
|
||||
* bearer via attp_cl_send_cmd). */
|
||||
if (gatt_eatt_release_bearer_by_clcb(p_clcb->p_tcb->peer_bda, p_clcb->clcb_idx)) {
|
||||
p_clcb->retry_count++;
|
||||
#if (GATTC_INCLUDED == TRUE)
|
||||
gatt_act_discovery(p_clcb);
|
||||
#endif ///GATTC_INCLUDED == TRUE
|
||||
return;
|
||||
}
|
||||
#endif ///BLE_EATT_INCLUDED == TRUE
|
||||
if (p_clcb != gatt_cmd_dequeue(p_clcb->p_tcb, &rsp_code)) {
|
||||
GATT_TRACE_ERROR("gatt_rsp_timeout command queue out of sync, disconnect");
|
||||
} else {
|
||||
@@ -1330,13 +1511,24 @@ void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle)
|
||||
{
|
||||
tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param;
|
||||
|
||||
GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now");
|
||||
|
||||
if (p_tcb != NULL) {
|
||||
p_tcb->ind_count = 0;
|
||||
if (p_tcb == NULL || gatt_get_tcb_by_idx(p_tcb->tcb_idx) != p_tcb) {
|
||||
GATT_TRACE_WARNING("gatt_ind_ack_timeout tcb is already deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
attp_send_cl_msg(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now");
|
||||
|
||||
p_tcb->ind_count = 0;
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
/* Auto-ack on the bearer the indication arrived on (0 == legacy ATT). */
|
||||
p_tcb->eatt_tx_bearer = p_tcb->eatt_ind_bearer;
|
||||
attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
p_tcb->eatt_tx_bearer = 0;
|
||||
p_tcb->eatt_ind_bearer = 0;
|
||||
return;
|
||||
#endif
|
||||
attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
}
|
||||
#endif // (GATTC_INCLUDED == TRUE)
|
||||
|
||||
@@ -2677,6 +2869,7 @@ BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN
|
||||
for (j = i + 1; j < GATT_MAX_APPS; j ++) {
|
||||
p_dev->gatt_if[j - 1] = p_dev->gatt_if[j];
|
||||
}
|
||||
p_dev->gatt_if[GATT_MAX_APPS - 1] = 0;
|
||||
|
||||
if (p_dev->gatt_if[0] == 0) {
|
||||
ret = BTM_BleUpdateBgConnDev(FALSE, p_dev->remote_bda);
|
||||
@@ -2694,6 +2887,7 @@ BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN
|
||||
for (j = i + 1; j < GATT_MAX_APPS; j ++) {
|
||||
p_dev->listen_gif[j - 1] = p_dev->listen_gif[j];
|
||||
}
|
||||
p_dev->listen_gif[GATT_MAX_APPS - 1] = 0;
|
||||
|
||||
if (p_dev->listen_gif[0] == 0) {
|
||||
// To check, we do not support background connection, code will not be called here
|
||||
@@ -2739,6 +2933,7 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if)
|
||||
for (k = j + 1; k < GATT_MAX_APPS; k ++) {
|
||||
p_dev_list->gatt_if[k - 1] = p_dev_list->gatt_if[k];
|
||||
}
|
||||
p_dev_list->gatt_if[GATT_MAX_APPS - 1] = 0;
|
||||
|
||||
if (p_dev_list->gatt_if[0] == 0) {
|
||||
BTM_BleUpdateBgConnDev(FALSE, p_dev_list->remote_bda);
|
||||
@@ -2756,6 +2951,7 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if)
|
||||
for (k = j + 1; k < GATT_MAX_APPS; k ++) {
|
||||
p_dev_list->listen_gif[k - 1] = p_dev_list->listen_gif[k];
|
||||
}
|
||||
p_dev_list->listen_gif[GATT_MAX_APPS - 1] = 0;
|
||||
|
||||
if (p_dev_list->listen_gif[0] == 0) {
|
||||
// To check, we do not support background connection, code will not be called here
|
||||
@@ -2763,6 +2959,10 @@ void gatt_deregister_bgdev_list(tGATT_IF gatt_if)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_dev_list->gatt_if[0] == 0 && p_dev_list->listen_gif[0] == 0) {
|
||||
memset(p_dev_list, 0, sizeof(tGATT_BG_CONN_DEV));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* EATT (Enhanced ATT) internal definitions. */
|
||||
|
||||
#ifndef GATT_EATT_INT_H
|
||||
#define GATT_EATT_INT_H
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
|
||||
#include "stack/bt_types.h"
|
||||
#include "gatt_int.h"
|
||||
|
||||
#define GATT_EATT_PSM 0x0027
|
||||
|
||||
typedef void (tGATT_EATT_EVT_CBACK)(UINT16 conn_id, UINT8 status, UINT16 cid);
|
||||
|
||||
void gatt_eatt_init(void);
|
||||
void gatt_eatt_deinit(void);
|
||||
void gatt_eatt_register_evt_cback(tGATT_EATT_EVT_CBACK *p_cback);
|
||||
void gatt_eatt_set_chan_num(UINT8 num_chan);
|
||||
void gatt_eatt_on_encrypted(BD_ADDR bd_addr);
|
||||
|
||||
BOOLEAN gatt_eatt_is_bearer(UINT16 lcid);
|
||||
UINT16 gatt_eatt_get_available_bearer(BD_ADDR bd_addr, UINT8 op);
|
||||
#if (BLE_EATT_SERVER_INCLUDED == TRUE)
|
||||
/* Pick an EATT bearer for a server-initiated PDU (notification/indication),
|
||||
* round-robin across the connection's bearers. Returns L2CAP_ATT_CID when no
|
||||
* EATT bearer is available so the caller falls back to the legacy ATT channel. */
|
||||
UINT16 gatt_eatt_get_server_tx_bearer(BD_ADDR bd_addr);
|
||||
#endif
|
||||
BOOLEAN gatt_eatt_set_default_bearer(UINT16 conn_id, UINT16 lcid);
|
||||
BOOLEAN gatt_eatt_mark_busy(BD_ADDR bd_addr, UINT16 lcid, UINT8 op, UINT16 clcb_idx);
|
||||
BOOLEAN gatt_eatt_release_bearer(BD_ADDR bd_addr, UINT16 lcid, UINT8 *p_op, UINT16 *p_clcb_idx);
|
||||
BOOLEAN gatt_eatt_release_bearer_by_clcb(BD_ADDR bd_addr, UINT16 clcb_idx);
|
||||
|
||||
void gatt_eatt_data_ind(UINT16 lcid, BT_HDR *p_buf);
|
||||
void gatt_eatt_on_chan_mtu_changed(BD_ADDR bd_addr, UINT16 lcid);
|
||||
UINT16 gatt_eatt_mtu_for_client_op(BD_ADDR bd_addr, UINT8 op_code, UINT16 legacy_mtu);
|
||||
|
||||
#endif /* BLE_EATT_INCLUDED == TRUE */
|
||||
|
||||
#endif /* GATT_EATT_INT_H */
|
||||
@@ -74,6 +74,20 @@ typedef UINT8 tGATT_SEC_ACTION;
|
||||
#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/
|
||||
#define GATT_AUTH_SIGN_LEN 12
|
||||
|
||||
/* Only Write Command (0x52) and Signed Write Command (0xD2) may set the
|
||||
* command/signature bits in the top two MSBs; all other opcodes must be
|
||||
* strictly below GATT_OP_CODE_MAX with those bits clear. */
|
||||
static inline BOOLEAN gatt_is_valid_att_opcode(UINT8 op_code)
|
||||
{
|
||||
if (op_code == GATT_CMD_WRITE || op_code == GATT_SIGN_CMD_WRITE) {
|
||||
return TRUE;
|
||||
}
|
||||
if (op_code & GATT_WRITE_CMD_MASK) {
|
||||
return FALSE;
|
||||
}
|
||||
return op_code < GATT_OP_CODE_MAX;
|
||||
}
|
||||
|
||||
#define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */
|
||||
|
||||
/* ATT Read By Type Response: Length field is 1 octet (max 255). */
|
||||
@@ -301,6 +315,11 @@ typedef struct {
|
||||
UINT8 op_code;
|
||||
UINT8 status;
|
||||
UINT8 cback_cnt[GATT_MAX_APPS];
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
UINT16 eatt_lcid; /* EATT bearer the request arrived on, so an
|
||||
* async server response is routed back to it
|
||||
* after eatt_rx_bearer has been cleared. */
|
||||
#endif
|
||||
} tGATT_SR_CMD;
|
||||
|
||||
#define GATT_CH_CLOSE 0
|
||||
@@ -388,6 +407,13 @@ typedef struct {
|
||||
UINT32 trans_id;
|
||||
|
||||
UINT16 att_lcid; /* L2CAP channel ID for ATT */
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
UINT16 eatt_rx_bearer; /* active EATT bearer for RX/response routing */
|
||||
UINT16 eatt_tx_bearer; /* transient TX bearer override */
|
||||
UINT16 eatt_ind_bearer; /* EATT bearer an indication arrived on, so a
|
||||
* deferred app confirmation is sent back on it */
|
||||
UINT16 eatt_att_mtu; /* negotiated L2CAP MTU for EATT bearers */
|
||||
#endif
|
||||
UINT16 payload_size;
|
||||
|
||||
tGATT_CH_STATE ch_state;
|
||||
@@ -635,6 +661,19 @@ extern UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda);
|
||||
|
||||
|
||||
/* Functions provided by att_protocol.c */
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
extern UINT16 gatt_get_att_mtu(tGATT_TCB *p_tcb);
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
extern UINT16 gatt_eatt_mtu_for_client_op(BD_ADDR bd_addr, UINT8 op_code, UINT16 legacy_mtu);
|
||||
#define GATT_CL_ATT_MTU(p_tcb, op) \
|
||||
gatt_eatt_mtu_for_client_op((p_tcb)->peer_bda, (op), (p_tcb)->payload_size)
|
||||
#else
|
||||
#define GATT_CL_ATT_MTU(p_tcb, op) ((p_tcb)->payload_size)
|
||||
#endif
|
||||
#else
|
||||
#define gatt_get_att_mtu(p_tcb) ((p_tcb)->payload_size)
|
||||
#define GATT_CL_ATT_MTU(p_tcb, op) ((p_tcb)->payload_size)
|
||||
#endif
|
||||
extern tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg);
|
||||
extern BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg);
|
||||
extern tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg);
|
||||
@@ -646,6 +685,7 @@ extern UINT8 *gatt_dbg_op_name(UINT8 op_code);
|
||||
extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl);
|
||||
#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE
|
||||
extern BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid, UINT16 len, UINT8 **p_data);
|
||||
extern UINT8 gatt_get_uuid_stream_len(tBT_UUID uuid);
|
||||
extern UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid);
|
||||
extern BOOLEAN gatt_uuid_compare(tBT_UUID src, tBT_UUID tar);
|
||||
extern void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32);
|
||||
@@ -653,6 +693,8 @@ extern char *gatt_uuid_to_str(const tBT_UUID *uuid);
|
||||
extern void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size);
|
||||
extern void gatt_start_rsp_timer(UINT16 clcb_idx);
|
||||
extern void gatt_start_conf_timer(tGATT_TCB *p_tcb);
|
||||
extern void gatt_conf_timeout(TIMER_LIST_ENT *p_tle);
|
||||
extern void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb);
|
||||
extern void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle);
|
||||
extern void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle);
|
||||
extern void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb);
|
||||
@@ -678,6 +720,7 @@ extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_hand
|
||||
extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void);
|
||||
extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p);
|
||||
extern void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p);
|
||||
extern void gatt_purge_prepare_write_before_free_db(tGATT_SVC_DB *p_db);
|
||||
extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value);
|
||||
extern void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list);
|
||||
extern BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new);
|
||||
@@ -743,6 +786,12 @@ extern void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb);
|
||||
extern UINT8 gatt_send_write_msg(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle,
|
||||
UINT16 len, UINT16 offset, UINT8 *p_data);
|
||||
extern void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport);
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
extern void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb);
|
||||
#endif // (SMP_INCLUDED == TRUE)
|
||||
#if (GATTS_INCLUDED == TRUE)
|
||||
extern void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb);
|
||||
#endif // (GATTS_INCLUDED == TRUE)
|
||||
extern void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data);
|
||||
|
||||
extern void gatt_act_discovery(tGATT_CLCB *p_clcb);
|
||||
@@ -753,7 +802,7 @@ extern UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT
|
||||
extern tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_opcode);
|
||||
extern BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf);
|
||||
extern void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
UINT16 len, UINT8 *p_data);
|
||||
UINT16 len, UINT8 *p_data, UINT16 eatt_bearer_lcid);
|
||||
extern void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag);
|
||||
|
||||
/* gatt_auth.c */
|
||||
|
||||
@@ -1169,7 +1169,7 @@ BOOLEAN btsnd_hcic_ble_set_phy(UINT16 conn_handle,
|
||||
}
|
||||
|
||||
#if (BLE_50_DTM_TEST_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy,
|
||||
BOOLEAN btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy,
|
||||
UINT8 modulation_idx)
|
||||
{
|
||||
BT_HDR *p;
|
||||
@@ -1190,7 +1190,7 @@ UINT8 btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len,
|
||||
BOOLEAN btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len,
|
||||
UINT8 packect,
|
||||
UINT8 phy)
|
||||
{
|
||||
@@ -2160,7 +2160,7 @@ BOOLEAN btsnd_hcic_ble_set_vendor_evt_mask (UINT32 evt_mask)
|
||||
#if (BLE_FEAT_ISO_EN == TRUE)
|
||||
|
||||
#if (BLE_FEAT_ISO_BIG_BROADCASTER_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
BOOLEAN btsnd_hcic_ble_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
uint32_t sdu_interval, uint16_t max_sdu, uint16_t max_transport_latency,
|
||||
uint8_t rtn, uint8_t phy, uint8_t packing, uint8_t framing,
|
||||
uint8_t encryption, uint8_t *broadcast_code)
|
||||
@@ -2196,7 +2196,7 @@ UINT8 btsnd_hcic_ble_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_big_create_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
BOOLEAN btsnd_hcic_ble_big_create_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse,
|
||||
uint16_t max_sdu, uint16_t max_pdu, uint8_t phy,
|
||||
uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc,
|
||||
@@ -2237,7 +2237,7 @@ UINT8 btsnd_hcic_ble_big_create_test(uint8_t big_handle, uint8_t adv_handle, uin
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_big_terminate(uint8_t big_handle, uint8_t reason)
|
||||
BOOLEAN btsnd_hcic_ble_big_terminate(uint8_t big_handle, uint8_t reason)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -2259,7 +2259,7 @@ UINT8 btsnd_hcic_ble_big_terminate(uint8_t big_handle, uint8_t reason)
|
||||
}
|
||||
#endif // #if (BLE_FEAT_ISO_BIG_BROADCASTER_EN == TRUE)
|
||||
#if (BLE_FEAT_ISO_BIG_SYNCER_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
|
||||
BOOLEAN btsnd_hcic_ble_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
|
||||
uint8_t encryption, uint8_t *bc_code,
|
||||
uint8_t mse, uint16_t big_sync_timeout,
|
||||
uint8_t num_bis, uint8_t *bis)
|
||||
@@ -2471,7 +2471,7 @@ UINT8 btsnd_hcic_ble_iso_set_cig_params_test(uint8_t cig_id, uint32_t sdu_int_c_
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_iso_create_cis(uint8_t cis_count, struct ble_hci_cis_hdls *cis_hdls)
|
||||
BOOLEAN btsnd_hcic_ble_iso_create_cis(uint8_t cis_count, struct ble_hci_cis_hdls *cis_hdls)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -2524,7 +2524,7 @@ UINT8 btsnd_hcic_ble_iso_remove_cig(uint8_t cig_id)
|
||||
#endif // #if (BLE_FEAT_ISO_CIG_CENTRAL_EN == TRUE)
|
||||
|
||||
#if (BLE_FEAT_ISO_CIG_PERIPHERAL_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_iso_accept_cis_req(uint16_t cis_handle)
|
||||
BOOLEAN btsnd_hcic_ble_iso_accept_cis_req(uint16_t cis_handle)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -2802,7 +2802,7 @@ UINT8 btsnd_hcic_ble_enh_read_trans_power_level(uint16_t conn_handle, uint8_t ph
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_read_remote_trans_power_level(uint16_t conn_handle, uint8_t phy)
|
||||
BOOLEAN btsnd_hcic_ble_read_remote_trans_power_level(uint16_t conn_handle, uint8_t phy)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -2916,7 +2916,7 @@ UINT8 btsnd_hcic_ble_set_default_subrate(UINT16 subrate_min, UINT16 subrate_max,
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_subrate_request(UINT16 conn_handle, UINT16 subrate_min, UINT16 subrate_max, UINT16 max_latency,
|
||||
BOOLEAN btsnd_hcic_ble_subrate_request(UINT16 conn_handle, UINT16 subrate_min, UINT16 subrate_max, UINT16 max_latency,
|
||||
UINT16 continuation_number, UINT16 supervision_timeout)
|
||||
{
|
||||
BT_HDR *p;
|
||||
@@ -3451,7 +3451,7 @@ UINT8 btsnd_hcic_ble_set_periodic_sync_subevt(UINT16 sync_handle, UINT16 periodi
|
||||
#endif // #if (BT_BLE_FEAT_PAWR_EN == TRUE)
|
||||
|
||||
#if (BT_BLE_FEAT_CHANNEL_SOUNDING == TRUE)
|
||||
UINT8 btsnd_hcic_ble_cs_read_local_supported_caps(void)
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_local_supported_caps(void)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -3469,7 +3469,7 @@ UINT8 btsnd_hcic_ble_cs_read_local_supported_caps(void)
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_read_remote_supported_capabilities(UINT16 conn_handle)
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_remote_supported_capabilities(UINT16 conn_handle)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -3541,7 +3541,7 @@ UINT8 btsnd_hcic_ble_cs_write_cached_remote_supported_capabilities(UINT16 conn_h
|
||||
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_security_enable(UINT16 conn_handle)
|
||||
BOOLEAN btsnd_hcic_ble_cs_security_enable(UINT16 conn_handle)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -3581,7 +3581,7 @@ UINT8 btsnd_hcic_ble_cs_set_default_settings(UINT16 conn_handle, UINT8 role_enab
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_read_remote_fae_table(UINT16 conn_handle)
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_remote_fae_table(UINT16 conn_handle)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -3620,7 +3620,7 @@ UINT8 btsnd_hcic_ble_cs_write_cached_remote_fae_table(UINT16 conn_handle, UINT8
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_create_config(UINT16 conn_handle, UINT8 config_id, UINT8 create_context,
|
||||
BOOLEAN btsnd_hcic_ble_cs_create_config(UINT16 conn_handle, UINT8 config_id, UINT8 create_context,
|
||||
UINT8 main_mode_type, UINT8 sub_mode_type, UINT8 min_main_mode_steps,
|
||||
UINT8 max_main_mode_steps, UINT8 main_mode_repetition, UINT8 mode_0_steps,
|
||||
UINT8 role, UINT8 rtt_type, UINT8 cs_sync_phy, UINT8 *channel_map,
|
||||
@@ -3667,7 +3667,7 @@ UINT8 btsnd_hcic_ble_cs_create_config(UINT16 conn_handle, UINT8 config_id, UINT8
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_remove_config(UINT16 conn_handle, UINT8 config_id)
|
||||
BOOLEAN btsnd_hcic_ble_cs_remove_config(UINT16 conn_handle, UINT8 config_id)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
@@ -3748,7 +3748,7 @@ UINT8 btsnd_hcic_ble_cs_set_procedure_params(UINT16 conn_handle, UINT8 config_id
|
||||
return btu_hcif_send_cmd_sync(LOCAL_BR_EDR_CONTROLLER_ID, p);
|
||||
}
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_procedure_enable(UINT16 conn_handle, UINT8 config_id, UINT8 enable)
|
||||
BOOLEAN btsnd_hcic_ble_cs_procedure_enable(UINT16 conn_handle, UINT8 config_id, UINT8 enable)
|
||||
{
|
||||
BT_HDR *p;
|
||||
UINT8 *pp;
|
||||
|
||||
@@ -2635,6 +2635,54 @@ bool BTM_GetLocalIRK(uint8_t *irk);
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleGetCurrentAddress(BD_ADDR addr, uint8_t *addr_type);
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleGetRealPeerByPseudo
|
||||
**
|
||||
** Description Reverse map a Host pseudo address to the real peer identity
|
||||
** for a dual-identity (pseudo-address bond) link.
|
||||
**
|
||||
** Returns TRUE if the pseudo is known, FALSE otherwise.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleGetRealPeerByPseudo(BD_ADDR pseudo, BD_ADDR real_peer);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleGetConnIdentityByPseudo
|
||||
**
|
||||
** Description Return the real peer + local identity (and address types)
|
||||
** for a connected dual-identity link keyed by its pseudo.
|
||||
**
|
||||
** Returns TRUE if the pseudo belongs to a finalized link.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleGetConnIdentityByPseudo(BD_ADDR pseudo, BD_ADDR peer, BD_ADDR local,
|
||||
UINT8 *peer_type, UINT8 *local_type);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleComputePseudoForIdentity
|
||||
**
|
||||
** Description Recompute the deterministic Host pseudo for a (local, peer)
|
||||
** identity pair (e.g. to remove a stored bond by identity).
|
||||
*******************************************************************************/
|
||||
void BTM_BleComputePseudoForIdentity(BD_ADDR local, UINT8 local_type,
|
||||
BD_ADDR peer, UINT8 peer_type, BD_ADDR pseudo);
|
||||
|
||||
/*******************************************************************************
|
||||
** Function BTM_BleMarkPseudoBond
|
||||
**
|
||||
** Description Mark the device record for bd_addr as a pseudo-address bond
|
||||
** (dual local-identity). Normally invoked from bta_dm_add_ble_device
|
||||
** on the BTU thread when BTA_DmAddBleDevice is called with
|
||||
** is_pseudo_bond=TRUE while loading bonds from NVS. There is no live
|
||||
** connection at boot, so the side table cannot be consulted. The mark
|
||||
** prevents the BTM_LE_KEY_PID handler from consolidating two pseudo
|
||||
** bonds (which share the peer IRK / Identity) into one record and
|
||||
** losing one LTK after reboot.
|
||||
**
|
||||
** Returns TRUE if a record was found and marked.
|
||||
*******************************************************************************/
|
||||
BOOLEAN BTM_BleMarkPseudoBond(BD_ADDR bd_addr);
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function BTM__BLEReadDiscoverability
|
||||
|
||||
@@ -174,6 +174,7 @@ typedef void (*tBTU_EVENT_CALLBACK)(BT_HDR *p_hdr);
|
||||
|
||||
/* L2CAP host-driven Create_Connection retry back-off timer */
|
||||
#define BTU_TTYPE_L2CAP_LINK_RETRY 113
|
||||
#define BTU_TTYPE_ATT_WAIT_FOR_CONF 114
|
||||
|
||||
/* BTU Task Signal */
|
||||
typedef enum {
|
||||
|
||||
@@ -1278,6 +1278,11 @@ extern tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HL
|
||||
*******************************************************************************/
|
||||
extern tGATT_STATUS GATTS_ShowLocalDatabase(void);
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
extern void GATT_EattSetChanNum(UINT8 num_chan);
|
||||
extern BOOLEAN GATT_EattSetDefaultBearer(UINT16 conn_id, UINT16 lcid);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
}
|
||||
|
||||
@@ -1056,10 +1056,10 @@ BOOLEAN btsnd_hcic_ble_set_phy(UINT16 conn_handle,
|
||||
UINT8 rx_phys, UINT16 phy_options);
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
#if (BLE_50_DTM_TEST_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy,
|
||||
BOOLEAN btsnd_hcic_ble_enhand_rx_test(UINT8 rx_channel, UINT8 phy,
|
||||
UINT8 modulation_idx);
|
||||
|
||||
UINT8 btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len,
|
||||
BOOLEAN btsnd_hcic_ble_enhand_tx_test(UINT8 tx_channel, UINT8 len,
|
||||
UINT8 packect,
|
||||
UINT8 phy);
|
||||
#endif // #if (BLE_50_DTM_TEST_EN == TRUE)
|
||||
@@ -1227,32 +1227,32 @@ UINT8 btsnd_hcic_ble_iso_set_cig_params(uint8_t cig_id, uint32_t sdu_int_c_to_p,
|
||||
UINT8 btsnd_hcic_ble_iso_set_cig_params_test(uint8_t cig_id, uint32_t sdu_int_c_to_p, uint32_t sdu_int_p_to_c, uint8_t ft_c_to_p, uint8_t ft_p_to_c,
|
||||
uint16_t iso_interval, uint8_t worse_case_SCA, uint8_t packing, uint8_t framing, uint8_t cis_cnt,
|
||||
struct ble_hci_le_cis_params_test *cis_params_test);
|
||||
UINT8 btsnd_hcic_ble_iso_create_cis(uint8_t cis_count, struct ble_hci_cis_hdls *cis_hdls);
|
||||
BOOLEAN btsnd_hcic_ble_iso_create_cis(uint8_t cis_count, struct ble_hci_cis_hdls *cis_hdls);
|
||||
UINT8 btsnd_hcic_ble_iso_remove_cig(uint8_t cig_id);
|
||||
#endif // (BLE_FEAT_ISO_CIG_CENTRAL_EN == TRUE)
|
||||
|
||||
#if (BLE_FEAT_ISO_CIG_PERIPHERAL_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_iso_accept_cis_req(uint16_t cis_handle);
|
||||
BOOLEAN btsnd_hcic_ble_iso_accept_cis_req(uint16_t cis_handle);
|
||||
UINT8 btsnd_hcic_ble_iso_reject_cis_req(uint16_t cis_handle, uint8_t reason);
|
||||
#endif // #if (BLE_FEAT_ISO_CIG_PERIPHERAL_EN == TRUE)
|
||||
|
||||
#if (BLE_FEAT_ISO_BIG_BROADCASTER_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
BOOLEAN btsnd_hcic_ble_big_create(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
uint32_t sdu_interval, uint16_t max_sdu, uint16_t max_transport_latency,
|
||||
uint8_t rtn, uint8_t phy, uint8_t packing, uint8_t framing,
|
||||
uint8_t encryption, uint8_t *broadcast_code);
|
||||
|
||||
UINT8 btsnd_hcic_ble_big_create_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
BOOLEAN btsnd_hcic_ble_big_create_test(uint8_t big_handle, uint8_t adv_handle, uint8_t num_bis,
|
||||
uint32_t sdu_interval, uint16_t iso_interval, uint8_t nse,
|
||||
uint16_t max_sdu, uint16_t max_pdu, uint8_t phy,
|
||||
uint8_t packing, uint8_t framing, uint8_t bn, uint8_t irc,
|
||||
uint8_t pto, uint8_t encryption, uint8_t *broadcast_code);
|
||||
|
||||
UINT8 btsnd_hcic_ble_big_terminate(uint8_t big_handle, uint8_t reason);
|
||||
BOOLEAN btsnd_hcic_ble_big_terminate(uint8_t big_handle, uint8_t reason);
|
||||
#endif // #if (BLE_FEAT_ISO_BIG_BROADCASTER_EN == TRUE)
|
||||
|
||||
#if (BLE_FEAT_ISO_BIG_SYNCER_EN == TRUE)
|
||||
UINT8 btsnd_hcic_ble_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
|
||||
BOOLEAN btsnd_hcic_ble_big_sync_create(uint8_t big_handle, uint16_t sync_handle,
|
||||
uint8_t encryption, uint8_t *bc_code,
|
||||
uint8_t mse, uint16_t big_sync_timeout,
|
||||
uint8_t num_bis, uint8_t *bis);
|
||||
@@ -1309,7 +1309,7 @@ UINT8 btsnd_hcic_ble_read_antenna_info(void);
|
||||
#define HCIC_PARAM_SIZE_SET_TRANS_PWR_REPORTING_ENABLE 4
|
||||
|
||||
UINT8 btsnd_hcic_ble_enh_read_trans_power_level(uint16_t conn_handle, uint8_t phy);
|
||||
UINT8 btsnd_hcic_ble_read_remote_trans_power_level(uint16_t conn_handle, uint8_t phy);
|
||||
BOOLEAN btsnd_hcic_ble_read_remote_trans_power_level(uint16_t conn_handle, uint8_t phy);
|
||||
UINT8 btsnd_hcic_ble_set_path_loss_rpt_params(uint16_t conn_handle, uint8_t high_threshold, uint8_t high_hysteresis,
|
||||
uint8_t low_threshold, uint8_t low_hysteresis, uint16_t min_time_spent);
|
||||
UINT8 btsnd_hcic_ble_set_path_loss_rpt_enable(uint16_t conn_handle, uint8_t enable);
|
||||
@@ -1322,7 +1322,7 @@ UINT8 btsnd_hcic_ble_set_trans_pwr_rpt_enable(uint16_t conn_handle, uint8_t loca
|
||||
UINT8 btsnd_hcic_ble_set_default_subrate(UINT16 subrate_min, UINT16 subrate_max, UINT16 max_latency,
|
||||
UINT16 continuation_number, UINT16 supervision_timeout);
|
||||
|
||||
UINT8 btsnd_hcic_ble_subrate_request(UINT16 conn_handle, UINT16 subrate_min, UINT16 subrate_max, UINT16 max_latency,
|
||||
BOOLEAN btsnd_hcic_ble_subrate_request(UINT16 conn_handle, UINT16 subrate_min, UINT16 subrate_max, UINT16 max_latency,
|
||||
UINT16 continuation_number, UINT16 supervision_timeout);
|
||||
#endif // #if (BLE_FEAT_CONN_SUBRATING == TRUE)
|
||||
|
||||
@@ -1384,8 +1384,8 @@ UINT8 btsnd_hcic_ble_set_ext_adv_params_v2(UINT8 adv_handle, UINT16 properties,
|
||||
#define HCIC_PARAM_SIZE_SET_PROCEDURE_PARAMS_LEN 23
|
||||
#define HCIC_PARAM_SIZE_SET_PROCEDURE_ENABLE_PARAMS_LEN 4
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_read_local_supported_caps(void);
|
||||
UINT8 btsnd_hcic_ble_cs_read_remote_supported_capabilities(UINT16 conn_handle);
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_local_supported_caps(void);
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_remote_supported_capabilities(UINT16 conn_handle);
|
||||
UINT8 btsnd_hcic_ble_cs_write_cached_remote_supported_capabilities(UINT16 conn_handle, UINT8 num_config_supported, UINT16 max_consecutive_proc_supported,
|
||||
UINT8 num_ant_supported, UINT8 max_ant_paths_supported, UINT8 roles_supported,
|
||||
UINT8 modes_supported, UINT8 rtt_capability, UINT8 rtt_aa_only_n,
|
||||
@@ -1394,17 +1394,17 @@ UINT8 btsnd_hcic_ble_cs_write_cached_remote_supported_capabilities(UINT16 conn_h
|
||||
UINT16 T_IP1_times_supported, UINT16 T_IP2_times_supported, UINT16 T_FCS_times_supported,
|
||||
UINT16 T_PM_times_supported, UINT8 T_SW_times_supported, UINT8 TX_SNR_capability);
|
||||
|
||||
UINT8 btsnd_hcic_ble_cs_security_enable(UINT16 conn_handle);
|
||||
BOOLEAN btsnd_hcic_ble_cs_security_enable(UINT16 conn_handle);
|
||||
UINT8 btsnd_hcic_ble_cs_set_default_settings(UINT16 conn_handle, UINT8 role_enable, UINT8 cs_sync_ant_selection, INT8 max_tx_power);
|
||||
UINT8 btsnd_hcic_ble_cs_read_remote_fae_table(UINT16 conn_handle);
|
||||
BOOLEAN btsnd_hcic_ble_cs_read_remote_fae_table(UINT16 conn_handle);
|
||||
UINT8 btsnd_hcic_ble_cs_write_cached_remote_fae_table(UINT16 conn_handle, UINT8 *remote_fae_table);
|
||||
UINT8 btsnd_hcic_ble_cs_create_config(UINT16 conn_handle, UINT8 config_id, UINT8 create_context,
|
||||
BOOLEAN btsnd_hcic_ble_cs_create_config(UINT16 conn_handle, UINT8 config_id, UINT8 create_context,
|
||||
UINT8 main_mode_type, UINT8 sub_mode_type, UINT8 min_main_mode_steps,
|
||||
UINT8 max_main_mode_steps, UINT8 main_mode_repetition, UINT8 mode_0_steps,
|
||||
UINT8 role, UINT8 rtt_type, UINT8 cs_sync_phy, UINT8 *channel_map,
|
||||
UINT8 channel_map_repetition, UINT8 channel_selection_type, UINT8 ch3c_shape,
|
||||
UINT8 ch3c_jump,UINT8 reserved);
|
||||
UINT8 btsnd_hcic_ble_cs_remove_config(UINT16 conn_handle, UINT8 config_id);
|
||||
BOOLEAN btsnd_hcic_ble_cs_remove_config(UINT16 conn_handle, UINT8 config_id);
|
||||
UINT8 btsnd_hcic_ble_cs_set_channel_classification(UINT8 *channel_class);
|
||||
UINT8 btsnd_hcic_ble_cs_set_procedure_params(UINT16 conn_handle, UINT8 config_id, UINT16 max_procedure_len,
|
||||
UINT16 min_procedure_interval, UINT16 max_procedure_interval,
|
||||
@@ -1412,7 +1412,7 @@ UINT8 btsnd_hcic_ble_cs_set_procedure_params(UINT16 conn_handle, UINT8 config_id
|
||||
UINT32 max_subevent_len, UINT8 tone_ant_config_selection,
|
||||
UINT8 phy, UINT8 tx_power_delta, UINT8 preferred_peer_antenna,
|
||||
UINT8 SNR_control_initiator, UINT8 SNR_control_reflector);
|
||||
UINT8 btsnd_hcic_ble_cs_procedure_enable(UINT16 conn_handle, UINT8 config_id, UINT8 enable);
|
||||
BOOLEAN btsnd_hcic_ble_cs_procedure_enable(UINT16 conn_handle, UINT8 config_id, UINT8 enable);
|
||||
#endif // (BT_BLE_FEAT_CHANNEL_SOUNDING == TRUE)
|
||||
|
||||
#if (BT_BLE_FEAT_CS_SECURITY_REQUIREMENTS == TRUE)
|
||||
|
||||
@@ -277,6 +277,15 @@ typedef void (tL2CA_ECHO_DATA_CB) (BD_ADDR, UINT16, UINT8 *);
|
||||
*/
|
||||
typedef void (tL2CA_CONGESTION_STATUS_CB) (UINT16, BOOLEAN);
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
/* LE CoC reconfiguration indication. Parameters are:
|
||||
** Local CID
|
||||
** Result (0 = L2CAP_LE_RECONFIG_OK)
|
||||
** TRUE if peer initiated the reconfiguration
|
||||
*/
|
||||
typedef void (tL2CA_LE_RECONFIG_IND_CB) (UINT16, UINT16, BOOLEAN);
|
||||
#endif
|
||||
|
||||
/* Callback prototype for number of packets completed events.
|
||||
** This callback notifies the application when Number of Completed Packets
|
||||
** event has been received.
|
||||
@@ -312,6 +321,9 @@ typedef struct {
|
||||
tL2CA_DATA_IND_CB *pL2CA_DataInd_Cb;
|
||||
tL2CA_CONGESTION_STATUS_CB *pL2CA_CongestionStatus_Cb;
|
||||
tL2CA_TX_COMPLETE_CB *pL2CA_TxComplete_Cb;
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
tL2CA_LE_RECONFIG_IND_CB *pL2CA_LeReconfigInd_Cb;
|
||||
#endif
|
||||
|
||||
} tL2CAP_APPL_INFO;
|
||||
|
||||
@@ -558,6 +570,12 @@ extern UINT16 L2CA_ConnectLECocReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG
|
||||
extern BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result,
|
||||
UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg);
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
extern UINT8 L2CA_ConnectLEEcocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg,
|
||||
UINT8 num_chan, UINT16 *p_lcids);
|
||||
extern BOOLEAN L2CA_LEEcocReconfig(UINT16 lcids[], UINT8 num, UINT16 new_mtu, UINT16 new_mps);
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function L2CA_GetPeerLECocConfig
|
||||
@@ -569,6 +587,12 @@ extern BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, U
|
||||
*******************************************************************************/
|
||||
extern BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg);
|
||||
|
||||
extern UINT8 L2CA_LECocDataWrite (UINT16 lcid, BT_HDR *p_data);
|
||||
extern BOOLEAN L2CA_LECocIsCongested (UINT16 lcid);
|
||||
extern BOOLEAN L2CA_LECocGiveCredits (UINT16 lcid, UINT16 credits);
|
||||
extern BOOLEAN L2CA_LECocSetAutoCredit (UINT16 lcid, BOOLEAN enable);
|
||||
extern BOOLEAN L2CA_LECocDisconnect (UINT16 lcid);
|
||||
|
||||
#endif // (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ 0x14
|
||||
#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES 0x15
|
||||
#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT 0x16
|
||||
#define L2CAP_CMD_BLE_ENHANCED_CONN_REQ 0x17
|
||||
#define L2CAP_CMD_BLE_ENHANCED_CONN_RES 0x18
|
||||
#define L2CAP_CMD_BLE_CREDIT_RECONFIG_REQ 0x19
|
||||
#define L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP 0x1A
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +81,10 @@
|
||||
#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN 10 /* LE_PSM, SCID, MTU, MPS, Init Credit */
|
||||
#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN 10 /* DCID, MTU, MPS, Init credit, Result */
|
||||
#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN 4 /* CID, Credit */
|
||||
#define L2CAP_CMD_BLE_ENHANCED_CONN_REQ_BASE_LEN 8 /* LE_PSM, MTU, MPS, Init Credit */
|
||||
#define L2CAP_CMD_BLE_ENHANCED_CONN_RES_BASE_LEN 8 /* MTU, MPS, Init credit, Result */
|
||||
#define L2CAP_CMD_BLE_CREDIT_RECONFIG_REQ_BASE_LEN 4 /* MTU, MPS */
|
||||
#define L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP_LEN 2 /* Result */
|
||||
|
||||
|
||||
|
||||
@@ -288,7 +296,7 @@
|
||||
/* SAR bits in the control word
|
||||
*/
|
||||
#define L2CAP_FCR_UNSEG_SDU 0x0000 /* Control word to begin with for unsegmented PDU*/
|
||||
#define L2CAP_FCR_START_SDU 0x4000 /* ...for Starting PDU of a semented SDU */
|
||||
#define L2CAP_FCR_START_SDU 0x4000 /* ...for Starting PDU of a segmented SDU */
|
||||
#define L2CAP_FCR_END_SDU 0x8000 /* ...for ending PDU of a segmented SDU */
|
||||
#define L2CAP_FCR_CONT_SDU 0xc000 /* ...for continuation PDU of a segmented SDU */
|
||||
|
||||
@@ -333,4 +341,10 @@
|
||||
#define L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS 0x0B
|
||||
#define L2CAP_LE_RESULT_INVALID_PARAMETERS 0x0C
|
||||
|
||||
#define L2CAP_LE_RECONFIG_OK 0
|
||||
#define L2CAP_LE_RECONFIG_REDUCTION_MTU_NOT_ALLOWED 1
|
||||
#define L2CAP_LE_RECONFIG_REDUCTION_MPS_NOT_ALLOWED 2
|
||||
#define L2CAP_LE_RECONFIG_INVALID_DCID 3
|
||||
#define L2CAP_LE_RECONFIG_UNACCEPTED_PARAM 4
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
#define L2CAP_LE_MIN_MTU 23
|
||||
#define L2CAP_LE_MIN_MPS 23
|
||||
#define L2CAP_LE_MAX_MPS 65533
|
||||
#define L2CAP_LE_CLAMP_MPS(m) \
|
||||
((UINT16)(((m) < L2CAP_LE_MIN_MPS) ? L2CAP_LE_MIN_MPS : \
|
||||
(((m) > L2CAP_LE_MAX_MPS) ? L2CAP_LE_MAX_MPS : (m))))
|
||||
/* Enhanced Credit Based Flow Control minimums (Core Spec Vol 3 Part A 4.25). */
|
||||
#define L2CAP_LE_ECFC_MIN_MTU 64
|
||||
#define L2CAP_LE_ECFC_MIN_MPS 64
|
||||
#define L2CAP_LE_MIN_CREDIT 0
|
||||
#define L2CAP_LE_MAX_CREDIT 65535
|
||||
#define L2CAP_LE_DEFAULT_MTU 512
|
||||
@@ -285,8 +291,10 @@ typedef struct
|
||||
typedef struct t_l2c_ccb {
|
||||
BOOLEAN in_use; /* TRUE when in use, FALSE when not */
|
||||
tL2C_CHNL_STATE chnl_state; /* Channel state */
|
||||
tL2CAP_LE_CFG_INFO local_conn_cfg; /* Our config for ble conn oriented channel */
|
||||
tL2CAP_LE_CFG_INFO peer_conn_cfg; /* Peer device config ble conn oriented channel */
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
tL2CAP_LE_CFG_INFO local_conn_cfg; /* LE CoC local channel config */
|
||||
tL2CAP_LE_CFG_INFO peer_conn_cfg; /* LE CoC peer channel config */
|
||||
#endif
|
||||
|
||||
struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */
|
||||
struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */
|
||||
@@ -347,6 +355,23 @@ typedef struct t_l2c_ccb {
|
||||
UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */
|
||||
#endif
|
||||
UINT16 tx_data_len;
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
BOOLEAN le_coc_active;
|
||||
BOOLEAN le_ecfc_channel;
|
||||
BOOLEAN le_coc_no_auto_credit;
|
||||
UINT16 le_coc_rx_avail;
|
||||
UINT16 le_coc_rx_credits_pending;
|
||||
UINT16 le_coc_rx_manual_owed; /* manual mode: K-frame credits consumed, awaiting recv_ready return */
|
||||
BT_HDR *le_coc_rx_sdu;
|
||||
UINT16 le_coc_rx_sdu_total;
|
||||
UINT16 le_coc_rx_sdu_rcvd;
|
||||
BOOLEAN le_coc_rx_have_len;
|
||||
BT_HDR *le_coc_tx_sdu;
|
||||
UINT16 le_coc_tx_offset;
|
||||
BOOLEAN le_coc_tx_len_sent;
|
||||
BOOLEAN le_coc_xmit_busy; /* try_xmit re-entrancy guard */
|
||||
BOOLEAN le_coc_xmit_rerun; /* re-entered: outer loop must re-run */
|
||||
#endif
|
||||
} tL2C_CCB;
|
||||
|
||||
/***********************************************************************
|
||||
@@ -449,7 +474,9 @@ typedef struct t_l2c_linkcb {
|
||||
tBLE_ADDR_TYPE open_addr_type; /* be set by open API */
|
||||
tBLE_ADDR_TYPE ble_addr_type;
|
||||
UINT16 tx_data_len; /* tx data length used in data length extension */
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
fixed_queue_t *le_sec_pending_q; /* LE coc channels waiting for security check completion */
|
||||
#endif
|
||||
UINT8 sec_act;
|
||||
#define L2C_BLE_CONN_UPDATE_DISABLE 0x1 /* disable update connection parameters */
|
||||
#define L2C_BLE_NEW_CONN_PARAM 0x2 /* new connection parameter to be set */
|
||||
@@ -720,6 +747,7 @@ extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm);
|
||||
extern void l2cu_release_rcb (tL2C_RCB *p_rcb);
|
||||
extern tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm);
|
||||
extern tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm);
|
||||
extern tL2C_RCB *l2cu_find_ble_rcb_by_real_psm (UINT16 real_psm);
|
||||
|
||||
#if (L2CAP_COC_INCLUDED == TRUE)
|
||||
extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
|
||||
@@ -814,6 +842,8 @@ extern void l2c_fcr_free_timer (tL2C_CCB *p_ccb);
|
||||
*/
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
extern BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb);
|
||||
extern void l2cble_remove_pending_direct_conn (tL2C_LCB *p_lcb);
|
||||
extern void l2cble_cleanup_alloc_ccb_failed_conn (tL2C_LCB *p_lcb);
|
||||
extern void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
|
||||
extern void l2cble_conn_comp (UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type,
|
||||
UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout);
|
||||
@@ -828,7 +858,84 @@ extern void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb);
|
||||
extern void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result);
|
||||
extern void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb);
|
||||
extern void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value);
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
/* Defined in l2c_ble.c under (SMP_INCLUDED && BLE_L2CAP_COC_INCLUDED); the LE
|
||||
* CoC/ECFC security check has no meaning without SMP, so callers guard their
|
||||
* use with #if (SMP_INCLUDED == TRUE) and fall back to an immediate success. */
|
||||
extern BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data);
|
||||
extern void l2ble_sec_flush_pending_req(tL2C_LCB *p_lcb, void *p_ref_data);
|
||||
#endif
|
||||
|
||||
extern BOOLEAN l2c_ble_le_coc_is_chan(tL2C_CCB *p_ccb);
|
||||
/* Map a BTM security failure (tBTM_STATUS) to the matching LE CoC/ECFC L2CAP
|
||||
* result code (0x0005-0x0008) so the peer learns the real reason (authorization
|
||||
* / encryption) instead of always seeing "insufficient authentication". */
|
||||
extern UINT16 l2c_ble_coc_sec_status_to_result(BD_ADDR bd_addr, tBTM_STATUS status);
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
extern void l2c_ble_le_coc_connect_req(tL2C_CCB *p_ccb);
|
||||
extern void l2c_ble_le_coc_handle_credit_conn_res(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
/* Fail a pending base LE CoC (0x14) client request whose sig id was CMD_REJECTed.
|
||||
* Returns TRUE if a matching pending CCB was found and torn down. */
|
||||
extern BOOLEAN l2c_ble_le_coc_abort_conn_req(tL2C_LCB *p_lcb, UINT8 id, UINT16 result);
|
||||
#endif
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED == TRUE)
|
||||
extern void l2c_ble_le_coc_connect_rsp(tL2C_CCB *p_ccb, UINT16 result);
|
||||
extern void l2c_ble_le_coc_handle_credit_conn_req(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
#endif
|
||||
extern void l2c_ble_le_coc_on_link_up(tL2C_LCB *p_lcb);
|
||||
extern void l2c_ble_le_coc_open_channel(tL2C_CCB *p_ccb, UINT16 result);
|
||||
extern void l2c_ble_le_coc_cleanup_ccb(tL2C_CCB *p_ccb);
|
||||
extern void l2c_ble_le_coc_apply_reconfig(tL2C_CCB *p_ccb, UINT16 new_mtu, UINT16 new_mps);
|
||||
extern void l2c_ble_le_coc_handle_flow_ctrl_credit(tL2C_LCB *p_lcb, UINT8 *p, UINT16 cmd_len);
|
||||
extern void l2c_ble_le_coc_handle_disc_req(tL2C_CCB *p_ccb, tL2C_LCB *p_lcb, UINT8 id, UINT16 lcid, UINT16 rcid);
|
||||
extern void l2c_ble_le_coc_handle_disc_rsp(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
extern void l2c_ble_le_coc_data_ind(tL2C_CCB *p_ccb, BT_HDR *p_msg);
|
||||
extern UINT8 l2c_ble_le_coc_data_write(UINT16 lcid, BT_HDR *p_data);
|
||||
extern BOOLEAN l2c_ble_le_coc_is_congested(UINT16 lcid);
|
||||
extern BOOLEAN l2c_ble_le_coc_give_credits(UINT16 lcid, UINT16 credits);
|
||||
extern BOOLEAN l2c_ble_le_coc_set_auto_credit(UINT16 lcid, BOOLEAN enable);
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
extern void l2c_ble_le_coc_notify_reconfig(tL2C_CCB *p_ccb, UINT16 status, BOOLEAN peer_initiated);
|
||||
#endif
|
||||
extern BOOLEAN l2c_ble_le_coc_disconnect(UINT16 lcid);
|
||||
/* Per-CCB signalling response timeout (BTU_TTYPE_L2CAP_CHNL on p_ccb->timer_entry):
|
||||
* fires when a peer never answers a pending connect/reconfigure request. */
|
||||
extern void l2c_ble_le_coc_channel_timeout(tL2C_CCB *p_ccb);
|
||||
extern void l2c_ble_le_coc_start_rsp_timer(tL2C_CCB *p_ccb, UINT16 timeout_sec);
|
||||
extern void l2c_ble_le_coc_stop_rsp_timer(tL2C_CCB *p_ccb);
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED == TRUE)
|
||||
extern void l2c_ble_ecfc_connect_rsp(tL2C_CCB *p_ccb, UINT16 result);
|
||||
extern void l2c_ble_ecfc_handle_conn_req(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
#endif
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
extern void l2c_ble_ecfc_handle_conn_res(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
extern void l2c_ble_ecfc_abort_cl_txn(tL2C_LCB *p_lcb, UINT8 sig_id, UINT16 result);
|
||||
/* Abort the ECFC client connect transaction that owns p_ccb (0x18 timed out). */
|
||||
extern BOOLEAN l2c_ble_ecfc_on_conn_timeout(tL2C_CCB *p_ccb);
|
||||
#endif
|
||||
/* Reconfiguration is available regardless of the client/server flag. */
|
||||
extern void l2c_ble_ecfc_abort_reconfig_txn(tL2C_LCB *p_lcb, UINT8 sig_id);
|
||||
/* Abort the ECFC reconfigure transaction that owns p_ccb (0x1A timed out). */
|
||||
extern BOOLEAN l2c_ble_ecfc_on_reconfig_timeout(tL2C_CCB *p_ccb);
|
||||
extern void l2c_ble_ecfc_handle_reconfig_req(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
extern void l2c_ble_ecfc_handle_reconfig_res(tL2C_LCB *p_lcb, UINT8 *p, UINT8 id, UINT16 cmd_len);
|
||||
extern void l2c_ble_ecfc_on_ccb_release(tL2C_CCB *p_ccb);
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
extern void l2c_ble_ecfc_on_link_up(tL2C_LCB *p_lcb);
|
||||
#endif
|
||||
extern BOOLEAN l2cu_send_peer_ble_enhanced_credit_conn_req(tL2C_LCB *p_lcb, UINT8 sig_id, UINT16 psm,
|
||||
UINT16 mtu, UINT16 mps, UINT16 credits, UINT8 num_chan, UINT16 *p_scids);
|
||||
extern void l2cu_send_peer_ble_enhanced_credit_conn_res(tL2C_LCB *p_lcb, UINT8 rem_id,
|
||||
UINT16 mtu, UINT16 mps, UINT16 credits, UINT16 result, UINT8 num_chan, UINT16 *p_dcids);
|
||||
extern void l2cu_reject_ble_enhanced_connection(tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result, UINT8 num_scids);
|
||||
extern BOOLEAN l2cu_send_peer_ble_credit_reconfig_req(tL2C_LCB *p_lcb, UINT8 sig_id,
|
||||
UINT16 mtu, UINT16 mps, UINT8 num_chan, UINT16 *p_dcids);
|
||||
extern void l2cu_send_peer_ble_credit_reconfig_rsp(tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result);
|
||||
#endif /* BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE */
|
||||
#endif /* BLE_L2CAP_COC_INCLUDED == TRUE */
|
||||
|
||||
|
||||
#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "stack/btm_api.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "gatt_int.h"
|
||||
#include "device/controller.h"
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
/*******************************************************************************
|
||||
**
|
||||
@@ -1439,6 +1440,12 @@ void L2CA_DeregisterLECoc(UINT16 psm)
|
||||
*******************************************************************************/
|
||||
UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg)
|
||||
{
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED != TRUE)
|
||||
UNUSED(psm);
|
||||
UNUSED(p_bd_addr);
|
||||
UNUSED(p_cfg);
|
||||
return 0;
|
||||
#else
|
||||
L2CAP_TRACE_API("%s PSM: 0x%04x BDA: %02x:%02x:%02x:%02x:%02x:%02x", __func__, psm,
|
||||
p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]);
|
||||
|
||||
@@ -1449,6 +1456,17 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bail out before allocating an LCB if the controller has no BLE support:
|
||||
* l2cu_create_conn()'s !supports_ble() path returns FALSE WITHOUT releasing
|
||||
* the LCB (it must not change its ownership contract), so allocating here and
|
||||
* relying on that path would leak the LCB. Pre-check at the API entry as the
|
||||
* function header of l2cu_create_conn recommends. */
|
||||
if (!controller_get_interface()->supports_ble())
|
||||
{
|
||||
L2CAP_TRACE_WARNING("%s controller has no BLE support", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fail if the PSM is not registered */
|
||||
tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm);
|
||||
if (p_rcb == NULL)
|
||||
@@ -1458,17 +1476,22 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
}
|
||||
|
||||
/* First, see if we already have a le link to the remote */
|
||||
BOOLEAN lcb_allocated = FALSE;
|
||||
tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE);
|
||||
if (p_lcb == NULL)
|
||||
{
|
||||
/* No link. Get an LCB and start link establishment */
|
||||
p_lcb = l2cu_allocate_lcb(p_bd_addr, FALSE, BT_TRANSPORT_LE);
|
||||
if ((p_lcb == NULL)
|
||||
/* currently use BR/EDR for ERTM mode l2cap connection */
|
||||
|| (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) )
|
||||
{
|
||||
L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: 0x%p",
|
||||
if (p_lcb == NULL) {
|
||||
L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: NULL",
|
||||
__func__, psm);
|
||||
return 0;
|
||||
}
|
||||
lcb_allocated = TRUE;
|
||||
if (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) {
|
||||
L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: %p",
|
||||
__func__, psm, p_lcb);
|
||||
l2cu_release_lcb(p_lcb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1478,11 +1501,21 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
if (p_ccb == NULL)
|
||||
{
|
||||
L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm);
|
||||
if (lcb_allocated) {
|
||||
l2cble_cleanup_alloc_ccb_failed_conn(p_lcb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save registration info */
|
||||
p_ccb->p_rcb = p_rcb;
|
||||
p_ccb->le_coc_active = TRUE;
|
||||
/* A pooled CCB reused from a released non-CoC channel keeps its stale
|
||||
* remote_cid (l2cu_allocate_ccb does not clear it, and l2cu_release_ccb only
|
||||
* runs cleanup_ccb for le_coc_active CCBs). Clear it now so the DCID dedup
|
||||
* check in l2c_ble_le_coc_handle_credit_conn_res cannot false-match this
|
||||
* channel-in-setup before its real remote_cid is assigned. */
|
||||
p_ccb->remote_cid = 0;
|
||||
|
||||
/* Save the configuration */
|
||||
if (p_cfg) {
|
||||
@@ -1495,7 +1528,7 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE)
|
||||
{
|
||||
L2CAP_TRACE_DEBUG("%s LE Link is up", __func__);
|
||||
l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);
|
||||
l2c_ble_le_coc_connect_req(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1517,6 +1550,7 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
|
||||
/* Return the local CID as our handle */
|
||||
return p_ccb->local_cid;
|
||||
#endif /* BLE_L2CAP_COC_CLIENT_INCLUDED */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -1533,6 +1567,16 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result,
|
||||
UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg)
|
||||
{
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED != TRUE)
|
||||
UNUSED(p_bd_addr);
|
||||
UNUSED(id);
|
||||
UNUSED(lcid);
|
||||
UNUSED(result);
|
||||
UNUSED(status);
|
||||
UNUSED(p_cfg);
|
||||
return FALSE;
|
||||
#else
|
||||
UNUSED(status);
|
||||
L2CAP_TRACE_API("%s CID: 0x%04x Result: %d Status: %d BDA: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
__func__, lcid, result, status,
|
||||
p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]);
|
||||
@@ -1566,18 +1610,77 @@ BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 r
|
||||
memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO));
|
||||
}
|
||||
|
||||
if (result == L2CAP_CONN_OK)
|
||||
l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL);
|
||||
else
|
||||
{
|
||||
tL2C_CONN_INFO conn_info;
|
||||
memcpy(conn_info.bd_addr, p_bd_addr, BD_ADDR_LEN);
|
||||
conn_info.l2cap_result = result;
|
||||
conn_info.l2cap_status = status;
|
||||
l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info);
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
if (p_ccb->le_ecfc_channel) {
|
||||
/* Forward the caller's specific result so a security reject
|
||||
* (0x0005-0x0008) reaches the peer intact (Core Spec v6.2 Vol 3 Part A
|
||||
* 10.2 mandates the exact "insufficient authentication/encryption" code).
|
||||
* l2c_ble_ecfc_connect_rsp records it for the aggregate 0x18 response. */
|
||||
l2c_ble_ecfc_connect_rsp(p_ccb, result);
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Legacy single-channel LE CoC: forward the caller's specific result so the
|
||||
* peer sees the real reject reason (mapped to a valid LE result code). */
|
||||
l2c_ble_le_coc_connect_rsp(p_ccb, result);
|
||||
|
||||
return TRUE;
|
||||
#endif /* BLE_L2CAP_COC_SERVER_INCLUDED */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function L2CA_LECocDataWrite
|
||||
**
|
||||
** Description Write an SDU on an LE CoC channel.
|
||||
**
|
||||
** Returns L2CAP_DW_SUCCESS, L2CAP_DW_CONGESTED, or L2CAP_DW_FAILED
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT8 L2CA_LECocDataWrite(UINT16 lcid, BT_HDR *p_data)
|
||||
{
|
||||
L2CAP_TRACE_API("L2CA_LECocDataWrite() CID: 0x%04x", lcid);
|
||||
return l2c_ble_le_coc_data_write(lcid, p_data);
|
||||
}
|
||||
|
||||
BOOLEAN L2CA_LECocIsCongested(UINT16 lcid)
|
||||
{
|
||||
return l2c_ble_le_coc_is_congested(lcid);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function L2CA_LECocGiveCredits
|
||||
**
|
||||
** Description Return RX credits to peer after processing an SDU.
|
||||
**
|
||||
** Returns TRUE if credits were sent
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN L2CA_LECocGiveCredits(UINT16 lcid, UINT16 credits)
|
||||
{
|
||||
L2CAP_TRACE_API("L2CA_LECocGiveCredits() CID: 0x%04x credits: %u", lcid, credits);
|
||||
return l2c_ble_le_coc_give_credits(lcid, credits);
|
||||
}
|
||||
|
||||
BOOLEAN L2CA_LECocSetAutoCredit(UINT16 lcid, BOOLEAN enable)
|
||||
{
|
||||
L2CAP_TRACE_API("L2CA_LECocSetAutoCredit() CID: 0x%04x enable=%u", lcid, enable);
|
||||
return l2c_ble_le_coc_set_auto_credit(lcid, enable);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Description Disconnect an LE CoC channel.
|
||||
**
|
||||
** Returns TRUE if disconnect request was sent
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN L2CA_LECocDisconnect(UINT16 lcid)
|
||||
{
|
||||
L2CAP_TRACE_API("L2CA_LECocDisconnect() CID: 0x%04x", lcid);
|
||||
return l2c_ble_le_coc_disconnect(lcid);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -312,6 +312,9 @@ void l2cble_notify_le_connection (BD_ADDR bda)
|
||||
/* update l2cap link status and send callback */
|
||||
p_lcb->link_state = LST_CONNECTED;
|
||||
l2cu_process_fixed_chnl_resp (p_lcb);
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
l2c_ble_le_coc_on_link_up(p_lcb);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +496,9 @@ void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE typ
|
||||
if (!HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(controller_get_interface()->get_features_ble()->as_array)) {
|
||||
p_lcb->link_state = LST_CONNECTED;
|
||||
l2cu_process_fixed_chnl_resp (p_lcb);
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
l2c_ble_le_coc_on_link_up(p_lcb);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* when adv and initiating are both active, cancel the direct connection */
|
||||
@@ -738,8 +744,55 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
return;
|
||||
}
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
if (cmd_code >= L2CAP_CMD_BLE_ENHANCED_CONN_REQ &&
|
||||
cmd_code <= L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP) {
|
||||
L2CAP_TRACE_DEBUG("LE_ECFC sig rx cmd=0x%02x id=%u len=%u link_st=%u role=%u",
|
||||
cmd_code, id, cmd_len, p_lcb->link_state, p_lcb->link_role);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (cmd_code) {
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
case L2CAP_CMD_REJECT: {
|
||||
UINT16 rej_reason = 0;
|
||||
|
||||
if (cmd_len < 2) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
STREAM_TO_UINT16(rej_reason, p);
|
||||
L2CAP_TRACE_DEBUG("LE_ECFC rx CMD_REJECT sig_id=%u reason=%u", id, rej_reason);
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
/* Peer explicitly rejected the request: "no/unsupported PSM" is the
|
||||
* closest generic reason to report to the application. A CMD_REJECT may
|
||||
* answer either an ECFC (0x18) or a base LE CoC (0x14) client request, so
|
||||
* try both aborts; each only acts on its own matching pending state. */
|
||||
l2c_ble_ecfc_abort_cl_txn(p_lcb, id, L2CAP_CONN_NO_PSM);
|
||||
l2c_ble_le_coc_abort_conn_req(p_lcb, id, L2CAP_CONN_NO_PSM);
|
||||
#endif
|
||||
/* Reconfiguration is compiled in regardless of the client/server flag,
|
||||
* so a CMD_REJECT may be answering a pending reconfigure request. Abort
|
||||
* it here too, otherwise its txn slot leaks (never freed). */
|
||||
l2c_ble_ecfc_abort_reconfig_txn(p_lcb, id);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED != TRUE)
|
||||
case L2CAP_CMD_REJECT:
|
||||
if (cmd_len < 2) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
L2CAP_TRACE_DEBUG("LE rx CMD_REJECT sig_id=%u", id);
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
/* A CMD_REJECT may be answering a pending base LE CoC (0x14) client
|
||||
* request; fail it now instead of waiting out the connect RTX timer. */
|
||||
l2c_ble_le_coc_abort_conn_req(p_lcb, id, L2CAP_CONN_NO_PSM);
|
||||
#endif
|
||||
p += 2;
|
||||
break;
|
||||
#endif
|
||||
case L2CAP_CMD_ECHO_RSP:
|
||||
case L2CAP_CMD_INFO_RSP:
|
||||
if (cmd_len < 2) {
|
||||
@@ -816,8 +869,12 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ: {
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED == TRUE)
|
||||
l2c_ble_le_coc_handle_credit_conn_req(p_lcb, p, id, cmd_len);
|
||||
#elif (BLE_L2CAP_COC_INCLUDED != TRUE)
|
||||
if (cmd_len < 10) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS);
|
||||
return;
|
||||
}
|
||||
tL2C_CCB *p_ccb = NULL;
|
||||
@@ -834,6 +891,11 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
STREAM_TO_UINT16(credits, p);
|
||||
L2CAP_TRACE_DEBUG("%s spsm %x, scid %x", __func__, spsm, scid);
|
||||
|
||||
if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MPS || mps > L2CAP_LE_MAX_MPS) {
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_INVALID_PARAMETERS);
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, scid);
|
||||
if (p_ccb) {
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED);
|
||||
@@ -855,17 +917,35 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
p_ccb->remote_id = id;
|
||||
p_ccb->p_rcb = p_rcb;
|
||||
p_ccb->remote_cid = scid;
|
||||
p_ccb->local_conn_cfg.mtu = mtu;
|
||||
p_ccb->local_conn_cfg.mps = controller_get_interface()->get_acl_data_size_ble();
|
||||
p_ccb->local_conn_cfg.credits = credits;
|
||||
/* Peer request fields describe peer receive capability */
|
||||
p_ccb->peer_conn_cfg.mtu = mtu;
|
||||
p_ccb->peer_conn_cfg.mps = mps;
|
||||
p_ccb->peer_conn_cfg.credits = credits;
|
||||
/* Response must advertise our receive capability, not peer's */
|
||||
p_ccb->local_conn_cfg.mtu = L2CAP_LE_DEFAULT_MTU;
|
||||
p_ccb->local_conn_cfg.mps = controller_get_interface()->get_acl_data_size_ble();
|
||||
p_ccb->local_conn_cfg.credits = L2CAP_LE_DEFAULT_CREDIT;
|
||||
|
||||
l2cu_send_peer_ble_credit_based_conn_res(p_ccb, L2CAP_LE_RESULT_CONN_OK);
|
||||
#else
|
||||
if (cmd_len < 10) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS);
|
||||
return;
|
||||
}
|
||||
l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_NO_RESOURCES);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
case L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES:
|
||||
l2c_ble_le_coc_handle_credit_conn_res(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
#endif
|
||||
case L2CAP_CMD_BLE_FLOW_CTRL_CREDIT: {
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
l2c_ble_le_coc_handle_flow_ctrl_credit(p_lcb, p, cmd_len);
|
||||
#else
|
||||
if (cmd_len < L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - flow ctrl credit too short: %d", cmd_len);
|
||||
return;
|
||||
@@ -891,6 +971,7 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
p_ccb->peer_conn_cfg.credits, lcid);
|
||||
l2c_link_check_send_pkts(p_ccb->p_lcb, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_DISC_REQ: {
|
||||
@@ -905,6 +986,12 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
STREAM_TO_UINT16(rcid, p);
|
||||
|
||||
p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
if (p_ccb && p_ccb->le_coc_active) {
|
||||
l2c_ble_le_coc_handle_disc_req(p_ccb, p_lcb, id, lcid, rcid);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (p_ccb) {
|
||||
p_ccb->remote_id = id;
|
||||
l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid);
|
||||
@@ -914,6 +1001,57 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
case L2CAP_CMD_DISC_RSP:
|
||||
l2c_ble_le_coc_handle_disc_rsp(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
#endif
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
#if (BLE_L2CAP_COC_SERVER_INCLUDED == TRUE)
|
||||
case L2CAP_CMD_BLE_ENHANCED_CONN_REQ:
|
||||
l2c_ble_ecfc_handle_conn_req(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
#else
|
||||
case L2CAP_CMD_BLE_ENHANCED_CONN_REQ: {
|
||||
/* ECFC compiled without a server role (e.g. GATTS disabled): we still
|
||||
* understand the ECFC command set (RECONFIG_REQ/RSP are handled below),
|
||||
* so reply with a proper all-refused ECFC connection response instead of
|
||||
* a CMD_REJECT "not understood". Mirrors the 0x14 #else path above. */
|
||||
if (cmd_len >= L2CAP_CMD_BLE_ENHANCED_CONN_REQ_BASE_LEN + sizeof(UINT16)) {
|
||||
UINT16 n_scids = (UINT16)((cmd_len - L2CAP_CMD_BLE_ENHANCED_CONN_REQ_BASE_LEN) / sizeof(UINT16));
|
||||
/* The reject must carry one DCID per requested SCID (Core Spec v6.2
|
||||
* Vol 3 Part A 4.26: 1:1 positional mapping); do NOT clamp to the
|
||||
* local channel budget as that desyncs the DCID count. Cap at 255
|
||||
* only to fit the UINT8 API argument. Mirror the server path
|
||||
* (l2c_ble_ecfc_handle_conn_req): >5 SCIDs is malformed
|
||||
* (INVALID_PARAMETERS), otherwise a plain resource refusal. */
|
||||
UINT8 reject_scids = (n_scids > 255) ? 255 : (UINT8)n_scids;
|
||||
UINT16 reason = (n_scids > 5) ? L2CAP_LE_RESULT_INVALID_PARAMETERS
|
||||
: L2CAP_LE_RESULT_NO_RESOURCES;
|
||||
l2cu_reject_ble_enhanced_connection(p_lcb, id, reason, reject_scids);
|
||||
} else {
|
||||
/* Too short to parse the SCID list, but the peer still expects a
|
||||
* response; mirror the server path (l2c_ble_ecfc_handle_conn_req) and
|
||||
* reject with n_scids=1 so the peer does not hang until its signalling
|
||||
* timer expires. */
|
||||
L2CAP_TRACE_WARNING("L2CAP - LE - short ECFC conn req: %d", cmd_len);
|
||||
l2cu_reject_ble_enhanced_connection(p_lcb, id, L2CAP_LE_RESULT_INVALID_PARAMETERS, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if (BLE_L2CAP_COC_CLIENT_INCLUDED == TRUE)
|
||||
case L2CAP_CMD_BLE_ENHANCED_CONN_RES:
|
||||
l2c_ble_ecfc_handle_conn_res(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
#endif
|
||||
case L2CAP_CMD_BLE_CREDIT_RECONFIG_REQ:
|
||||
l2c_ble_ecfc_handle_reconfig_req(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
case L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP:
|
||||
l2c_ble_ecfc_handle_reconfig_res(p_lcb, p, id, cmd_len);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - unknown cmd code: %d", cmd_code);
|
||||
l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
|
||||
@@ -921,6 +1059,17 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
}
|
||||
}
|
||||
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
static void l2cble_abort_direct_conn_init(tL2C_LCB *p_lcb)
|
||||
{
|
||||
btu_stop_timer(&p_lcb->timer_entry);
|
||||
l2cb.is_ble_connecting = FALSE;
|
||||
memset(l2cb.ble_connecting_bda, 0, BD_ADDR_LEN);
|
||||
btm_ble_set_conn_st(BLE_CONN_IDLE);
|
||||
p_lcb->link_state = LST_DISCONNECTED;
|
||||
}
|
||||
#endif // (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2cble_init_direct_conn
|
||||
@@ -929,6 +1078,9 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
**
|
||||
** Returns TRUE connection initiated, FALSE otherwise.
|
||||
**
|
||||
** Note On failure the LCB is not released; the caller must call
|
||||
** l2cu_release_lcb (see l2cu_create_conn contract in l2c_link.c).
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
{
|
||||
@@ -952,6 +1104,10 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
/* There can be only one BLE connection request outstanding at a time */
|
||||
if (p_dev_rec == NULL) {
|
||||
L2CAP_TRACE_WARNING ("unknown device, can not initiate connection");
|
||||
/* The caller allocated this LCB and expects this function to release it
|
||||
* on failure (as the other error paths do); free it to avoid leaking the
|
||||
* LCB and its queues / num_ble_links_active count. */
|
||||
l2cu_release_lcb (p_lcb);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
@@ -1012,7 +1168,6 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
|
||||
#if (BLE_TOPOLOGY_CHECK == TRUE)
|
||||
if (!btm_ble_topology_check(BTM_BLE_STATE_INIT)) {
|
||||
l2cu_release_lcb (p_lcb);
|
||||
L2CAP_TRACE_ERROR("initiate direct connection fail, topology limitation");
|
||||
return FALSE;
|
||||
}
|
||||
@@ -1077,7 +1232,6 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
p_dev_rec->conn_params.min_ce_len : BLE_CE_LEN_MIN), /* UINT16 min_ce_len */
|
||||
(UINT16) ((p_dev_rec->conn_params.max_ce_len != BTM_BLE_CONN_PARAM_UNDEF) ?
|
||||
p_dev_rec->conn_params.max_ce_len : BLE_CE_LEN_MIN) /* UINT16 max_ce_len */)) {
|
||||
l2cu_release_lcb (p_lcb);
|
||||
L2CAP_TRACE_ERROR("initiate direct connection fail, no resources");
|
||||
return (FALSE);
|
||||
} else {
|
||||
@@ -1135,10 +1289,7 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
#if (BT_BLE_FEAT_PAWR_EN == TRUE)
|
||||
if (p_lcb->is_pawr_synced) {
|
||||
if(!btsnd_hcic_ble_create_ext_conn_v2(&aux_conn)) {
|
||||
l2cb.is_ble_connecting = FALSE;
|
||||
memset(l2cb.ble_connecting_bda, 0, BD_ADDR_LEN);
|
||||
btm_ble_set_conn_st (BLE_CONN_IDLE);
|
||||
l2cu_release_lcb (p_lcb);
|
||||
l2cble_abort_direct_conn_init(p_lcb);
|
||||
L2CAP_TRACE_ERROR("initiate pawr sync connection failed, no resources");
|
||||
return (FALSE);
|
||||
}
|
||||
@@ -1146,16 +1297,12 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
|
||||
#endif // (BT_BLE_FEAT_PAWR_EN == TRUE)
|
||||
{
|
||||
if(!btsnd_hcic_ble_create_ext_conn(&aux_conn)) {
|
||||
l2cb.is_ble_connecting = FALSE;
|
||||
memset(l2cb.ble_connecting_bda, 0, BD_ADDR_LEN);
|
||||
btm_ble_set_conn_st (BLE_CONN_IDLE);
|
||||
l2cu_release_lcb (p_lcb);
|
||||
l2cble_abort_direct_conn_init(p_lcb);
|
||||
L2CAP_TRACE_ERROR("initiate Aux connection failed, no resources");
|
||||
return (FALSE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
l2cu_release_lcb (p_lcb);
|
||||
L2CAP_TRACE_ERROR("BLE 5.0 not support!\n");
|
||||
return (FALSE);
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
@@ -1194,6 +1341,50 @@ BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb)
|
||||
return rt;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2cble_cleanup_alloc_ccb_failed_conn
|
||||
**
|
||||
** Description Clean up after LE CoC setup fails to allocate a CCB. If a
|
||||
** direct HCI connection is in progress, cancel it and update
|
||||
** BTM state; otherwise drop a queued direct-connect request.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void l2cble_cleanup_alloc_ccb_failed_conn (tL2C_LCB *p_lcb)
|
||||
{
|
||||
if (p_lcb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_lcb->link_state == LST_CONNECTING) {
|
||||
if (!L2CA_CancelBleConnectReq(p_lcb->remote_bd_addr)) {
|
||||
L2CAP_TRACE_ERROR("%s: cancel direct connect failed", __func__);
|
||||
l2cu_release_lcb(p_lcb);
|
||||
memset(l2cb.ble_connecting_bda, 0, BD_ADDR_LEN);
|
||||
btm_ble_set_conn_st(BLE_CONN_IDLE);
|
||||
}
|
||||
} else {
|
||||
l2cble_remove_pending_direct_conn(p_lcb);
|
||||
l2cu_release_lcb(p_lcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2cble_remove_pending_direct_conn
|
||||
**
|
||||
** Description Drop a queued direct-connection attempt for this LCB.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void l2cble_remove_pending_direct_conn (tL2C_LCB *p_lcb)
|
||||
{
|
||||
btm_ble_remove_direct_conn_req(p_lcb);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2c_link_processs_ble_num_bufs
|
||||
@@ -1675,7 +1866,43 @@ void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb)
|
||||
return;
|
||||
}
|
||||
|
||||
#if (SMP_INCLUDED == TRUE)
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2c_ble_coc_sec_status_to_result
|
||||
**
|
||||
** Description Translate a BTM security failure into the LE CoC/ECFC L2CAP
|
||||
** result code that best matches it, so a rejected peer learns
|
||||
** the real reason instead of always "insufficient
|
||||
** authentication" (Core Spec v6.2 Vol 3 Part A 4.26/10.2 make
|
||||
** 0x0005-0x0008 mandatory per failure type).
|
||||
**
|
||||
** Returns One of L2CAP_LE_RESULT_INSUFFICIENT_* (0x0005-0x0008)
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 l2c_ble_coc_sec_status_to_result(BD_ADDR bd_addr, tBTM_STATUS status)
|
||||
{
|
||||
UINT8 sec_flags = 0;
|
||||
|
||||
if (status == BTM_NOT_AUTHORIZED) {
|
||||
return L2CAP_LE_RESULT_INSUFFICIENT_AUTHORIZATION; /* 0x0006 */
|
||||
}
|
||||
|
||||
/* If the link is not encrypted, tell the peer to encrypt (0x0008) rather
|
||||
* than re-authenticate; only fall back to insufficient authentication
|
||||
* (0x0005) when encryption is present but the required level was not met.
|
||||
* Key-size (0x0007) needs the actual key length, which the flags API does
|
||||
* not expose, so it is intentionally not distinguished here. */
|
||||
if (BTM_GetSecurityFlagsByTransport(bd_addr, &sec_flags, BT_TRANSPORT_LE) &&
|
||||
!(sec_flags & BTM_SEC_FLAG_ENCRYPTED)) {
|
||||
return L2CAP_LE_RESULT_INSUFFICIENT_ENCRY; /* 0x0008 */
|
||||
}
|
||||
|
||||
return L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION; /* 0x0005 */
|
||||
}
|
||||
#endif /* BLE_L2CAP_COC_INCLUDED == TRUE */
|
||||
|
||||
#if (SMP_INCLUDED == TRUE) && (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2cble_sec_comp
|
||||
@@ -1762,6 +1989,53 @@ void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data,
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2ble_sec_flush_pending_req
|
||||
**
|
||||
** Description Drop any queued LE security requests whose p_ref_data matches
|
||||
** |p_ref_data| (typically a CCB being released). Without this,
|
||||
** l2cble_sec_comp() would later invoke the stored callback with
|
||||
** a dangling or reused pointer once SMP completes.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void l2ble_sec_flush_pending_req(tL2C_LCB *p_lcb, void *p_ref_data)
|
||||
{
|
||||
if (p_lcb == NULL || p_lcb->le_sec_pending_q == NULL || p_ref_data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Removing mutates the underlying list, so re-scan from the head after each
|
||||
* hit until no queued request references p_ref_data anymore. */
|
||||
for (;;) {
|
||||
list_t *list = fixed_queue_get_list(p_lcb->le_sec_pending_q);
|
||||
tL2CAP_SEC_DATA *match = NULL;
|
||||
list_node_t *node;
|
||||
|
||||
for (node = list_begin(list); node != list_end(list); node = list_next(node)) {
|
||||
tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA *)list_node(node);
|
||||
if (p_buf != NULL && p_buf->p_ref_data == p_ref_data) {
|
||||
match = p_buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match == NULL) {
|
||||
break;
|
||||
}
|
||||
/* Only free once the node is actually detached. If removal fails (item
|
||||
* gone / could not acquire the dequeue semaphore), freeing it here would
|
||||
* leave a dangling node in the list, so the next scan would dereference
|
||||
* freed memory (use-after-free) and could loop forever. Abort instead. */
|
||||
if (fixed_queue_try_remove_from_queue(p_lcb->le_sec_pending_q, match) != NULL) {
|
||||
osi_free(match);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2ble_sec_access_req
|
||||
@@ -1810,7 +2084,7 @@ BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator,
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif /* #if (SMP_INCLUDED == TRUE) */
|
||||
#endif /* (SMP_INCLUDED == TRUE) && (BLE_L2CAP_COC_INCLUDED == TRUE) */
|
||||
#endif /* (BLE_INCLUDED == TRUE) */
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -478,6 +478,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
|
||||
while (!list_is_empty(p_lcb->link_xmit_data_q)) {
|
||||
p_buf = list_front(p_lcb->link_xmit_data_q);
|
||||
list_remove(p_lcb->link_xmit_data_q, p_buf);
|
||||
p_buf->event = 0;
|
||||
osi_free(p_buf);
|
||||
}
|
||||
} else
|
||||
@@ -1629,6 +1630,11 @@ void l2c_link_segments_xmitted (BT_HDR *p_msg)
|
||||
/* Find the LCB based on the handle */
|
||||
if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - rcvd segment complete, unknown handle: %d\n", handle);
|
||||
/* The partial segment being bounced back here was already removed from
|
||||
* link_xmit_data_q before it was handed to the controller, so it is not
|
||||
* freed by l2cu_release_lcb()/disc_comp when the link goes away. This
|
||||
* function is its sole owner, so it must be freed here to avoid a leak. */
|
||||
p_msg->event = 0;
|
||||
osi_free (p_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,6 +311,13 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
|
||||
if (p_ccb == NULL) {
|
||||
osi_free (p_msg);
|
||||
} else {
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
/* LE CoC data plane only; BR/EDR dynamic channels use l2c_csm / l2c_fcr below */
|
||||
if (p_lcb->transport == BT_TRANSPORT_LE && l2c_ble_le_coc_is_chan(p_ccb)) {
|
||||
l2c_ble_le_coc_data_ind(p_ccb, p_msg);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
|
||||
}
|
||||
@@ -1147,11 +1154,41 @@ void l2c_process_timeout (TIMER_LIST_ENT *p_tle)
|
||||
* re-issue the connection attempt now. */
|
||||
l2c_link_create_conn_retry ((tL2C_LCB *)p_tle->param);
|
||||
break;
|
||||
#endif ///CLASSIC_BT_INCLUDED == TRUE
|
||||
|
||||
case BTU_TTYPE_L2CAP_CHNL:
|
||||
l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
|
||||
case BTU_TTYPE_L2CAP_CHNL: {
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
tL2C_CCB *p_ccb = (tL2C_CCB *)p_tle->param;
|
||||
/* LE CoC/ECFC channels do not use the classic state machine; a per-CCB
|
||||
* BTU_TTYPE_L2CAP_CHNL timer is their connect/reconfigure response
|
||||
* timeout. Route it to the CoC handler. */
|
||||
if (p_ccb != NULL && p_ccb->le_coc_active) {
|
||||
l2c_ble_le_coc_channel_timeout(p_ccb);
|
||||
break;
|
||||
}
|
||||
/* Keep the NULL handling consistent with the CoC check above: the classic
|
||||
* state machine dereferences p_ccb unconditionally, so bail out here
|
||||
* instead of passing a NULL CCB down to l2c_csm_execute. */
|
||||
if (p_ccb == NULL) {
|
||||
L2CAP_TRACE_WARNING("L2CAP channel timeout with NULL CCB");
|
||||
break;
|
||||
}
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
l2c_csm_execute (p_ccb, L2CEVT_TIMEOUT, NULL);
|
||||
#else
|
||||
/* p_ccb may be unused when BT_STACK_NO_LOG strips the trace macro. */
|
||||
L2CAP_TRACE_WARNING("Unhandled L2CAP channel timeout for CCB %p", p_ccb);
|
||||
UNUSED(p_ccb);
|
||||
#endif
|
||||
#elif (CLASSIC_BT_INCLUDED == TRUE)
|
||||
l2c_csm_execute ((tL2C_CCB *)p_tle->param, L2CEVT_TIMEOUT, NULL);
|
||||
#else
|
||||
L2CAP_TRACE_WARNING("Unhandled L2CAP channel timeout");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#if (CLASSIC_BT_INCLUDED == TRUE)
|
||||
case BTU_TTYPE_L2CAP_FCR_ACK:
|
||||
l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_ACK_TIMEOUT, NULL);
|
||||
break;
|
||||
|
||||
@@ -108,7 +108,9 @@ tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPOR
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
p_lcb->transport = transport;
|
||||
p_lcb->tx_data_len = controller_get_interface()->get_ble_default_data_packet_length();
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
p_lcb->le_sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX);
|
||||
#endif
|
||||
|
||||
if (transport == BT_TRANSPORT_LE) {
|
||||
l2cb.num_ble_links_active++;
|
||||
@@ -164,6 +166,16 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
|
||||
{
|
||||
tL2C_CCB *p_ccb;
|
||||
|
||||
/* Make double-release harmless. Several failure paths (e.g.
|
||||
* l2cble_init_direct_conn) release the LCB and return FALSE, after which the
|
||||
* API-level caller (e.g. L2CA_ConnectFixedChnl) releases it again. Without
|
||||
* this guard the second call would wrongly decrement num_ble_links_active
|
||||
* and re-run l2cu_process_fixed_disc_cback on an already freed LCB. A valid
|
||||
* LCB always has in_use == TRUE (set in l2cu_allocate_lcb). */
|
||||
if (p_lcb == NULL || !p_lcb->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
L2CAP_TRACE_DEBUG("%s handle=%u bda="MACSTR"",
|
||||
__func__, p_lcb->handle, MAC2STR(p_lcb->remote_bd_addr));
|
||||
|
||||
@@ -253,6 +265,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
|
||||
while (!list_is_empty(p_lcb->link_xmit_data_q)) {
|
||||
BT_HDR *p_buf = list_front(p_lcb->link_xmit_data_q);
|
||||
list_remove(p_lcb->link_xmit_data_q, p_buf);
|
||||
p_buf->event = 0;
|
||||
osi_free(p_buf);
|
||||
}
|
||||
list_free(p_lcb->link_xmit_data_q);
|
||||
@@ -294,7 +307,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
|
||||
(*p_cb) (L2CAP_PING_RESULT_NO_LINK);
|
||||
}
|
||||
|
||||
#if (BLE_INCLUDED == TRUE)
|
||||
#if (BLE_INCLUDED == TRUE) && (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
/* Check and release all the LE COC connections waiting for security */
|
||||
if (p_lcb->le_sec_pending_q)
|
||||
{
|
||||
@@ -1721,8 +1734,18 @@ void l2cu_release_ccb (tL2C_CCB *p_ccb)
|
||||
if (!p_ccb->in_use) {
|
||||
return;
|
||||
}
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
if (p_lcb != NULL && p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
l2c_ble_ecfc_on_ccb_release(p_ccb);
|
||||
}
|
||||
#endif
|
||||
#if (BLE_L2CAP_COC_INCLUDED == TRUE)
|
||||
if (p_lcb != NULL && p_lcb->transport == BT_TRANSPORT_LE && p_ccb->le_coc_active) {
|
||||
l2c_ble_le_coc_cleanup_ccb(p_ccb);
|
||||
}
|
||||
#endif
|
||||
#if BLE_INCLUDED == TRUE
|
||||
if (p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
if (p_lcb != NULL && p_lcb->transport == BT_TRANSPORT_LE) {
|
||||
/* Take samephore to avoid race condition */
|
||||
l2ble_update_att_acl_pkt_num(L2CA_BUFF_FREE, NULL);
|
||||
}
|
||||
@@ -1995,6 +2018,32 @@ tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm)
|
||||
/* If here, no match found */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function l2cu_find_ble_rcb_by_real_psm
|
||||
**
|
||||
** Description Look through the BLE Registration Control Blocks to see if
|
||||
** anyone registered to handle the application PSM in question
|
||||
**
|
||||
** Returns Pointer to the BLE RCB or NULL if not found
|
||||
**
|
||||
*******************************************************************************/
|
||||
tL2C_RCB *l2cu_find_ble_rcb_by_real_psm (UINT16 real_psm)
|
||||
{
|
||||
tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0];
|
||||
UINT16 xx;
|
||||
|
||||
for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++)
|
||||
{
|
||||
if ((p_rcb->in_use) && (p_rcb->real_psm == real_psm)) {
|
||||
return (p_rcb);
|
||||
}
|
||||
}
|
||||
|
||||
/* If here, no match found */
|
||||
return (NULL);
|
||||
}
|
||||
#endif ///BLE_INCLUDED == TRUE
|
||||
|
||||
#if (L2CAP_COC_INCLUDED == TRUE)
|
||||
@@ -2306,6 +2355,32 @@ void l2cu_device_reset (void)
|
||||
**
|
||||
** Returns TRUE if successful, FALSE if gki get buffer fails.
|
||||
**
|
||||
** LCB OWNERSHIP ON FAILURE - READ BEFORE "FIXING" A LEAK HERE:
|
||||
** The release contract of this function is deliberately NOT uniform, and the
|
||||
** callers rely on the current behaviour. Do NOT add an unconditional
|
||||
** l2cu_release_lcb(p_lcb) around the FALSE returns below - it causes a
|
||||
** use-after-free + double free (see l2c_link_hci_disc_comp).
|
||||
**
|
||||
** Per-path behaviour on a FALSE return:
|
||||
** - BLE connect path (l2cble_create_conn -> l2cble_init_direct_conn) and the
|
||||
** classic l2cu_create_conn_after_switch RELEASE p_lcb internally on their
|
||||
** own failures. Callers must therefore NOT release again on those paths.
|
||||
** - The "!supports_ble()" and the trailing "return false" paths do NOT
|
||||
** release p_lcb (kept as-is on purpose).
|
||||
**
|
||||
** Caller expectations (all currently satisfied by the above):
|
||||
** - l2c_link_hci_disc_comp() keeps using p_lcb after a FALSE return and
|
||||
** releases it itself at the end via lcb_is_free (see the explicit
|
||||
** "must not release the LCB on failure" note there). Releasing internally
|
||||
** would UAF/double-free this hot disconnect+reconnect path.
|
||||
** - L2CA_ConnectFixedChnl() releases p_lcb itself on FALSE.
|
||||
** - The LE CoC/ECFC callers (L2CA_ConnectLECocReq / L2CA_ConnectLEEcocReq)
|
||||
** do NOT release on FALSE; they rely on the BLE path having released. The
|
||||
** only genuine leak is the (practically unreachable) !supports_ble() path
|
||||
** for those callers - if that must be closed, do it at the CoC API entry
|
||||
** (pre-check supports_ble and release the freshly-allocated LCB there),
|
||||
** not by changing the contract of this function.
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport)
|
||||
{
|
||||
@@ -2327,6 +2402,9 @@ BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport)
|
||||
|
||||
if (transport == BT_TRANSPORT_LE) {
|
||||
if (!controller_get_interface()->supports_ble()) {
|
||||
/* Intentionally does NOT release p_lcb (see the ownership note in the
|
||||
* function header). Practically unreachable for LE callers; close the
|
||||
* CoC leak at the API entry, not here. */
|
||||
return FALSE;
|
||||
}
|
||||
if(addr_type > BLE_ADDR_TYPE_MAX) {
|
||||
@@ -2384,6 +2462,9 @@ BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport)
|
||||
|
||||
return (l2cu_create_conn_after_switch (p_lcb));
|
||||
#endif // (CLASSIC_BT_INCLUDED == TRUE)
|
||||
/* Fallthrough only in a BLE-only build reached with a non-LE transport
|
||||
* (effectively dead). Intentionally does NOT release p_lcb - see the
|
||||
* ownership note in the function header. */
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3270,6 +3351,128 @@ void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb)
|
||||
l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
|
||||
}
|
||||
|
||||
#if (BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE)
|
||||
BOOLEAN l2cu_send_peer_ble_enhanced_credit_conn_req(tL2C_LCB *p_lcb, UINT8 sig_id, UINT16 psm,
|
||||
UINT16 mtu, UINT16 mps, UINT16 credits, UINT8 num_chan, UINT16 *p_scids)
|
||||
{
|
||||
BT_HDR *p_buf;
|
||||
UINT8 *p;
|
||||
UINT16 len = L2CAP_CMD_BLE_ENHANCED_CONN_REQ_BASE_LEN + num_chan * sizeof(UINT16);
|
||||
|
||||
if (p_lcb == NULL || p_scids == NULL || num_chan == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_BLE_ENHANCED_CONN_REQ, sig_id)) == NULL) {
|
||||
L2CAP_TRACE_WARNING("LE_ECFC tx 0x17 build_header failed sig_id=%u", sig_id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
|
||||
L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
|
||||
|
||||
UINT16_TO_STREAM(p, psm);
|
||||
UINT16_TO_STREAM(p, mtu);
|
||||
UINT16_TO_STREAM(p, mps);
|
||||
UINT16_TO_STREAM(p, credits);
|
||||
for (UINT8 i = 0; i < num_chan; i++) {
|
||||
UINT16_TO_STREAM(p, p_scids[i]);
|
||||
}
|
||||
|
||||
l2c_link_check_send_pkts(p_lcb, NULL, p_buf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void l2cu_send_peer_ble_enhanced_credit_conn_res(tL2C_LCB *p_lcb, UINT8 rem_id,
|
||||
UINT16 mtu, UINT16 mps, UINT16 credits, UINT16 result, UINT8 num_chan, UINT16 *p_dcids)
|
||||
{
|
||||
BT_HDR *p_buf;
|
||||
UINT8 *p;
|
||||
UINT16 len = L2CAP_CMD_BLE_ENHANCED_CONN_RES_BASE_LEN + num_chan * sizeof(UINT16);
|
||||
|
||||
if (p_lcb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_BLE_ENHANCED_CONN_RES, rem_id)) == NULL) {
|
||||
L2CAP_TRACE_WARNING("LE_ECFC tx 0x18 build_header failed rem_id=%u", rem_id);
|
||||
return;
|
||||
}
|
||||
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
|
||||
L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
|
||||
|
||||
UINT16_TO_STREAM(p, mtu);
|
||||
UINT16_TO_STREAM(p, mps);
|
||||
UINT16_TO_STREAM(p, credits);
|
||||
UINT16_TO_STREAM(p, result);
|
||||
for (UINT8 i = 0; i < num_chan; i++) {
|
||||
UINT16 dcid = (p_dcids != NULL) ? p_dcids[i] : 0;
|
||||
UINT16_TO_STREAM(p, dcid);
|
||||
}
|
||||
|
||||
l2c_link_check_send_pkts(p_lcb, NULL, p_buf);
|
||||
}
|
||||
|
||||
void l2cu_reject_ble_enhanced_connection(tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result, UINT8 num_scids)
|
||||
{
|
||||
if (num_scids == 0) {
|
||||
num_scids = 1;
|
||||
}
|
||||
l2cu_send_peer_ble_enhanced_credit_conn_res(p_lcb, rem_id, 0, 0, 0, result, num_scids, NULL);
|
||||
}
|
||||
|
||||
BOOLEAN l2cu_send_peer_ble_credit_reconfig_req(tL2C_LCB *p_lcb, UINT8 sig_id,
|
||||
UINT16 mtu, UINT16 mps, UINT8 num_chan, UINT16 *p_dcids)
|
||||
{
|
||||
BT_HDR *p_buf;
|
||||
UINT8 *p;
|
||||
UINT16 len = L2CAP_CMD_BLE_CREDIT_RECONFIG_REQ_BASE_LEN + num_chan * sizeof(UINT16);
|
||||
|
||||
if (p_lcb == NULL || p_dcids == NULL || num_chan == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_BLE_CREDIT_RECONFIG_REQ, sig_id)) == NULL) {
|
||||
L2CAP_TRACE_WARNING("LE_ECFC tx 0x19 build_header failed sig_id=%u", sig_id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
|
||||
L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
|
||||
|
||||
UINT16_TO_STREAM(p, mtu);
|
||||
UINT16_TO_STREAM(p, mps);
|
||||
for (UINT8 i = 0; i < num_chan; i++) {
|
||||
UINT16_TO_STREAM(p, p_dcids[i]);
|
||||
}
|
||||
|
||||
l2c_link_check_send_pkts(p_lcb, NULL, p_buf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void l2cu_send_peer_ble_credit_reconfig_rsp(tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result)
|
||||
{
|
||||
BT_HDR *p_buf;
|
||||
UINT8 *p;
|
||||
|
||||
if (p_lcb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP_LEN,
|
||||
L2CAP_CMD_BLE_CREDIT_RECONFIG_RSP, rem_id)) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
|
||||
L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
|
||||
|
||||
UINT16_TO_STREAM(p, result);
|
||||
l2c_link_check_send_pkts(p_lcb, NULL, p_buf);
|
||||
}
|
||||
#endif /* BLE_L2CAP_ENHANCED_COC_INCLUDED == TRUE */
|
||||
|
||||
#endif /* BLE_INCLUDED == TRUE */
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include "device/interop.h"
|
||||
#include "common/bt_target.h"
|
||||
#include "btm_int.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "btm_ble_pseudo.h"
|
||||
#endif
|
||||
#include "stack/l2c_api.h"
|
||||
#include "smp_int.h"
|
||||
#if (SMP_CRYPTO_MBEDTLS == TRUE)
|
||||
@@ -522,8 +525,17 @@ void smp_proc_sec_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
*******************************************************************************/
|
||||
void smp_proc_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
{
|
||||
SMP_TRACE_DEBUG("%s", __func__);
|
||||
p_cb->status = *(UINT8 *)p_data;
|
||||
UINT8 reason = *(UINT8 *)p_data;
|
||||
|
||||
SMP_TRACE_DEBUG("%s reason=0x%02x", __func__, reason);
|
||||
/* A peer may send a reserved or out-of-range reason code; normalize it so
|
||||
* upper layers always receive a defined pairing failure status. */
|
||||
if (reason == SMP_SUCCESS || reason > SMP_MAX_FAIL_RSN_PER_SPEC) {
|
||||
SMP_TRACE_WARNING("%s invalid pairing fail reason 0x%02x", __func__, reason);
|
||||
reason = SMP_PAIR_FAIL_UNKNOWN;
|
||||
}
|
||||
p_cb->status = reason;
|
||||
p_cb->failure = reason;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -1186,6 +1198,28 @@ void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
|
||||
}
|
||||
#endif ///BLE_INCLUDED == TRUE
|
||||
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
/* Dual-identity bond isolation: the link may have been keyed earlier from a
|
||||
* transient RPA. Now that the peer's stable Identity Address is known,
|
||||
* re-derive the pseudo from (local, Identity) and re-key the link so the
|
||||
* stored bond is reproducible across the peer's future RPA rotations. Keep
|
||||
* smp_cb.pairing_bda consistent so the in-flight pairing continues. */
|
||||
{
|
||||
tACL_CONN *p_acl = btm_bda_to_acl(p_cb->pairing_bda, BT_TRANSPORT_LE);
|
||||
BLE_PSEUDO_DBG("smp PID: pairing_bda=" BLE_PSEUDO_BDA_FMT " acl=%p id_addr=" BLE_PSEUDO_BDA_FMT,
|
||||
BLE_PSEUDO_BDA(p_cb->pairing_bda), p_acl, BLE_PSEUDO_BDA(pid_key.static_addr));
|
||||
if (p_acl != NULL) {
|
||||
BD_ADDR new_pseudo;
|
||||
if (btm_ble_pseudo_apply_identity(p_acl->hci_handle, pid_key.static_addr,
|
||||
pid_key.addr_type, new_pseudo)) {
|
||||
memcpy(p_cb->pairing_bda, new_pseudo, BD_ADDR_LEN);
|
||||
BLE_PSEUDO_DBG("smp PID: pairing_bda updated -> " BLE_PSEUDO_BDA_FMT,
|
||||
BLE_PSEUDO_BDA(p_cb->pairing_bda));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
smp_key_distribution_by_transport(p_cb, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -443,6 +443,17 @@ void SMP_OobDataReply(BD_ADDR bd_addr, tSMP_STATUS res, UINT8 len, UINT8 *p_data
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reject an OOB reply that does not match the device currently pairing. */
|
||||
if (memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) != 0) {
|
||||
SMP_TRACE_ERROR("%s() - Wrong BD Addr", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (btm_find_dev(bd_addr) == NULL) {
|
||||
SMP_TRACE_ERROR("%s() - no dev CB", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res != SMP_SUCCESS || len == 0 || !p_data) {
|
||||
SMP_TRACE_ERROR("%s pairing failed, res=0x%x len=%u p_data=%p",
|
||||
__func__, res, len, p_data);
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include "smp_int.h"
|
||||
#include "device/controller.h"
|
||||
#include "btm_int.h"
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
#include "btm_ble_pseudo.h"
|
||||
#endif
|
||||
#include "common/bte_appl.h"
|
||||
|
||||
#define SMP_PAIRING_REQ_SIZE 7
|
||||
@@ -1530,6 +1533,10 @@ void smp_collect_local_ble_address(UINT8 *le_addr, tSMP_CB *p_cb)
|
||||
BTM_ReadConnectionAddr( p_cb->pairing_bda, bda, &addr_type);
|
||||
BDADDR_TO_STREAM(p, bda);
|
||||
UINT8_TO_STREAM(p, addr_type);
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BLE_PSEUDO_DBG("smp local addr for f5/f6 = " BLE_PSEUDO_BDA_FMT " type %u (pairing_bda " BLE_PSEUDO_BDA_FMT ")",
|
||||
BLE_PSEUDO_BDA(bda), addr_type, BLE_PSEUDO_BDA(p_cb->pairing_bda));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -1557,6 +1564,10 @@ void smp_collect_peer_ble_address(UINT8 *le_addr, tSMP_CB *p_cb)
|
||||
|
||||
BDADDR_TO_STREAM(p, bda);
|
||||
UINT8_TO_STREAM(p, addr_type);
|
||||
#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE)
|
||||
BLE_PSEUDO_DBG("smp peer addr for f5/f6 = " BLE_PSEUDO_BDA_FMT " type %u (pairing_bda " BLE_PSEUDO_BDA_FMT ")",
|
||||
BLE_PSEUDO_BDA(bda), addr_type, BLE_PSEUDO_BDA(p_cb->pairing_bda));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -53,6 +53,9 @@ BLE_DOCS = [
|
||||
'migration-guides/release-5.x/5.0/bluetooth-low-energy.rst',
|
||||
]
|
||||
|
||||
BLE_DUAL_IDENTITY_DOCS = [
|
||||
'api-guides/ble/bluedroid-dual-identity-host-dev.rst',
|
||||
]
|
||||
|
||||
BLE_MESH_DOCS = [
|
||||
'api-guides/esp-ble-mesh/ble-mesh-index.rst',
|
||||
@@ -334,6 +337,7 @@ ESP32P4_DOCS = [
|
||||
conditional_include_dict = {
|
||||
'SOC_BT_SUPPORTED': BT_DOCS,
|
||||
'SOC_BLE_SUPPORTED': BLE_DOCS,
|
||||
'SOC_BLE_50_SUPPORTED': BLE_DUAL_IDENTITY_DOCS,
|
||||
'SOC_BLE_MESH_SUPPORTED': BLE_MESH_DOCS,
|
||||
'SOC_BLUFI_SUPPORTED': BLUFI_DOCS,
|
||||
'SOC_WIFI_SUPPORTED': WIFI_DOCS,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Bluedroid Host Support for Dual Local Identities
|
||||
================================================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
When a single peer phone connects to an ESP32 peripheral through two different **local identities** (for example, a Public address and a fixed Static Random address from two extended advertising sets), the default Bluedroid Host treats both links as the same peer. Bonds, LTK, and NVS sections can overwrite each other.
|
||||
|
||||
Enable :ref:`BT_BLE_PERIPH_PSEUDO_ADDR_BOND <CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND>` to derive a Host-internal **pseudo address** ``f(local_identity, peer)`` per link. The application sees two different ``remote_bda`` values for the same phone, while SMP and the controller still use the real peer identity on air.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
See :example:`ble50_dual_identity_server <bluetooth/bluedroid/ble_50/ble50_dual_identity_server>` for a Bluetooth LE 5.0 peripheral that advertises two identities concurrently, pairs with both, and keeps **isolated bonds per (local, peer) pair**.
|
||||
|
||||
Application Notes
|
||||
-----------------
|
||||
|
||||
- Use **conn_id** as the link key in GATTS calls; do not use ``remote_bda`` to tell links apart.
|
||||
- ``remote_bda`` in GAP/GATTS events is the **pseudo address** when this feature is enabled.
|
||||
- Call ``esp_ble_gap_get_conn_identity()`` while connected to recover the real peer and local identity.
|
||||
- Use ``esp_ble_gap_remove_bond_for_identity()`` to delete one identity's bond without affecting the other.
|
||||
- For controller operations (whitelist, directed advertising), use the **real peer** address, never the pseudo.
|
||||
@@ -15,6 +15,7 @@ Overview
|
||||
ble-qualification
|
||||
Low Power Mode Introduction <../low-power-mode/low-power-mode-ble>
|
||||
ble-multiconnection-guide
|
||||
:SOC_BLE_50_SUPPORTED: bluedroid-dual-identity-host-dev
|
||||
|
||||
***************
|
||||
Get Started
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Bluedroid 双本地身份 Host 开发说明
|
||||
=====================================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
简介
|
||||
----
|
||||
|
||||
当同一部手机通过两个不同的**本地身份**(例如来自两个扩展广播集的 Public 地址与固定的 Static Random 地址)连接到 ESP32 外围设备时,默认 Bluedroid Host 会将两条链路视为同一对端。Bond、LTK 与 NVS 区段可能相互覆盖。
|
||||
|
||||
启用 :ref:`BT_BLE_PERIPH_PSEUDO_ADDR_BOND <CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND>`\ 后,Host 会为每条链路派生内部 **伪地址 (pseudo address)** ``f(local_identity, peer)``。应用层对同一手机会看到两个不同的 ``remote_bda``,而 SMP 与控制器仍使用空口真实对端身份。
|
||||
|
||||
示例
|
||||
----
|
||||
|
||||
请参阅 :example:`ble50_dual_identity_server <bluetooth/bluedroid/ble_50/ble50_dual_identity_server>`\ :该 Bluetooth LE 5.0 外围设备同时广播两个身份、分别配对,并为每个 **(local, peer)** 对保留**独立的 bond**。
|
||||
|
||||
应用要点
|
||||
--------
|
||||
|
||||
- 在 GATTS 调用中以 **conn_id** 作为链路键;不要用 ``remote_bda`` 区分链路。
|
||||
- 启用本特性后,GAP/GATTS 事件中的 ``remote_bda`` 为 **pseudo 地址**。
|
||||
- 连接态下调用 ``esp_ble_gap_get_conn_identity()`` 可恢复真实对端与本地身份。
|
||||
- 使用 ``esp_ble_gap_remove_bond_for_identity()`` 只删除一路身份的 bond,不影响另一路。
|
||||
- 控制器相关操作(白名单、定向广播)须使用**真实对端**地址,切勿使用伪地址。
|
||||
@@ -15,6 +15,7 @@
|
||||
ble-qualification
|
||||
低功耗模式介绍 <../low-power-mode/low-power-mode-ble>
|
||||
ble-multiconnection-guide
|
||||
:SOC_BLE_50_SUPPORTED: bluedroid-dual-identity-host-dev
|
||||
|
||||
**********
|
||||
快速入门
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(ble50_dual_identity_server)
|
||||
@@ -0,0 +1,77 @@
|
||||
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- |
|
||||
|
||||
# BLE 5.0 Dual Local-Identity Security Server
|
||||
|
||||
This example demonstrates **two local identities advertising and being connected/encrypted at the same time** on a BLE 5.0 peripheral, with **isolated bonds per identity**.
|
||||
|
||||
It relies on the Host feature `CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND`: the Bluedroid Host derives a per-link **pseudo address** from `f(local_identity, peer)`, so that one phone connecting through two different local identities is treated as two independent peers (separate device record, LTK and NVS bond section).
|
||||
|
||||
## What it does
|
||||
|
||||
* Advertises **two connectable extended advertising sets** simultaneously:
|
||||
* Adv set 0 — **Public** address (identity A, name `ESP_DUAL_PUBLIC_A`)
|
||||
* Adv set 1 — **fixed Static Random** address (identity B, name `ESP_DUAL_RANDOM_B`)
|
||||
* Runs a minimal GATT server with one characteristic that requires an **encrypted** link to read/write.
|
||||
* On each connection it starts pairing (`SC + MITM + BOND`, static passkey `123456`).
|
||||
* On `AUTH_CMPL` it prints the link's **real peer** and **local identity** via `esp_ble_gap_get_conn_identity()`, and lists bonded devices.
|
||||
|
||||
## Key points for the application
|
||||
|
||||
* The `remote_bda` reported in `ESP_GATTS_CONNECT_EVT` is a **Host pseudo address**, not the phone's real MAC. The **same phone shows up as two different addresses**, one per identity.
|
||||
* **Always use `conn_id` as the link key.** Do not use `remote_bda` to tell links apart.
|
||||
* Use `esp_ble_gap_get_conn_identity(pseudo, &id)` to recover the real peer MAC and the local identity.
|
||||
* Use `esp_ble_gap_remove_bond_for_identity(local, local_type, peer, peer_type)` to delete a single identity's bond without affecting the other.
|
||||
* The Static Random address used for identity B is **hard-coded and fixed** (`s_identity_b_addr`) so the per-identity bond bucket survives reboots. If you randomize it per boot, reconnection cannot match the stored bond.
|
||||
|
||||
### Which address to pass to GAP/GATTS APIs
|
||||
|
||||
* **Link/bond operations** (`esp_ble_set_encryption`, `esp_ble_gap_disconnect`, `esp_ble_gap_security_rsp`, `esp_ble_confirm_reply`, `esp_ble_passkey_reply`, `esp_ble_gap_update_conn_params`, `esp_ble_gap_read_rssi`, `esp_ble_remove_bond_device`): pass back **exactly the `remote_bda` the event gave you** (the pseudo). Never build the real MAC yourself.
|
||||
* **Telling links apart / GATTS data** (`esp_ble_gatts_send_indicate`, `send_response`, `close`, …): use **`conn_id`**, not the address.
|
||||
* **Controller-level operations** (`esp_ble_gap_update_whitelist`, resolving list, directed advertising, `esp_ble_gatts_open`): use the **real peer Identity/RPA**, never the pseudo (the pseudo never goes on air).
|
||||
* **Need the real MAC**: call `esp_ble_gap_get_real_peer_addr()` / `esp_ble_gap_get_conn_identity()`.
|
||||
|
||||
See the Pseudo design guide **§8.2.2** for the full per-API table.
|
||||
|
||||
## How to test
|
||||
|
||||
1. `idf.py set-target esp32c3` (or s3/c6/h2), then `idf.py flash monitor`.
|
||||
2. On a phone, scan: you will see **two devices** — `ESP_DUAL_PUBLIC_A` and `ESP_DUAL_RANDOM_B`.
|
||||
3. Connect and pair to `..._A` (passkey `123456`). Read/write the characteristic — succeeds because the link is encrypted.
|
||||
4. While still connected to A, connect and pair to `..._B` from the **same phone**.
|
||||
5. Observe in the log that both links have the **same real peer** but **different pseudo** addresses and **different local identities**, and that `Bonded devices` shows **two** independent entries.
|
||||
6. Disconnect/reconnect either identity — it re-encrypts from its own stored LTK independently.
|
||||
7. Remove one bond (e.g. forget `..._B` on the phone, or call `esp_ble_gap_remove_bond_for_identity` for B) — the other identity's bond and encrypted reconnect are unaffected.
|
||||
|
||||
## Example log (abridged)
|
||||
|
||||
```
|
||||
I (xxx) DUAL_ID: Pseudo-address dual-identity bond isolation ENABLED
|
||||
I (xxx) DUAL_ID: Advertising as two identities: Public (A) + Static Random c1:22:33:44:55:66 (B)
|
||||
[PSEUDO] conn_complete[sync]: keyed handle=0x0 real=<phone> -> pseudo=<C4..A>
|
||||
I (xxx) DUAL_ID: CONNECT conn_id 0, remote(pseudo) c4:..:a
|
||||
I (xxx) DUAL_ID: AUTH_CMPL identity: real peer <phone>, local <public>
|
||||
[PSEUDO] conn_complete[sync]: keyed handle=0x1 real=<phone> -> pseudo=<FB..B>
|
||||
I (xxx) DUAL_ID: CONNECT conn_id 1, remote(pseudo) fb:..:b
|
||||
I (xxx) DUAL_ID: AUTH_CMPL identity: real peer <phone>, local c1:22:33:44:55:66
|
||||
I (xxx) DUAL_ID: Bonded devices: 2 (each entry is a (local,peer) identity = one pseudo)
|
||||
```
|
||||
|
||||
The `[PSEUDO] ...` lines come from the Host feature's debug logging, emitted through the standard BTM trace at debug level. They make it easy to follow the dual-identity flow during bring-up; raise the Bluetooth log level (or lower the BTM trace level) to suppress them.
|
||||
|
||||
## Notes
|
||||
|
||||
* Requires a BLE 5.0 capable chip (Extended Advertising). ESP32 (original) does not support it.
|
||||
* If `CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND` is disabled, the example still builds and runs but the two identities of the same phone will **share one bond** and overwrite each other — a startup warning is printed.
|
||||
|
||||
## Verified scenarios (ESP32-C3, Android phone using RPA)
|
||||
|
||||
* Connect Public_A and Static-Random_B from the same phone, pair/bond each → `Bonded devices: 2` (two isolated pseudo bonds).
|
||||
* Delete both bonds on the phone, reconnect A and B → fresh re-pairing succeeds for both, still two isolated bonds.
|
||||
* Disconnect all, reconnect A → **re-encrypts directly from the stored LTK (no re-pairing)**; the two identities' bonds never interfere.
|
||||
|
||||
The Host-side feature internals and the fixes behind these scenarios (peer-RPA Identity derivation, SC `active_remote_addr` using the real on-air RPA, disabling same-identity NVS de-dup, duplicate device-record cleanup, BTA `device_list` removal by handle) are documented in the Pseudo design guide, section **§24 (implementation notes)**: `docs/zh_CN/api-guides/ble/bluedroid-periph-pseudo-addr-dev-guide-v0.10.md`.
|
||||
|
||||
## Known limitation
|
||||
|
||||
The two bonds share the same peer IRK, so adding that IRK to the controller resolving list a second time is rejected (`Add resolving list error 18`). This does **not** affect encrypted reconnection here (RPA resolution is done in the Host), but it matters if you rely on the controller resolving list, white list, or directed advertising.
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "ble50_dual_identity_server.c"
|
||||
PRIV_REQUIRES bt nvs_flash
|
||||
INCLUDE_DIRS ".")
|
||||
+488
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* BLE 5.0 dual local-identity GATT server.
|
||||
*
|
||||
* The device advertises TWO connectable extended advertising sets at the same
|
||||
* time, each using a different local identity:
|
||||
*
|
||||
* - Adv set 0 : Public address -> identity A
|
||||
* - Adv set 1 : fixed Static Random addr -> identity B
|
||||
*
|
||||
* A single phone can connect to BOTH identities simultaneously. With
|
||||
* CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND enabled, the Host derives a distinct
|
||||
* pseudo address per (local_identity, peer) pair, so the two links get
|
||||
* independent device records, LTKs and NVS bond sections. Deleting one
|
||||
* identity's bond never affects the other.
|
||||
*
|
||||
* IMPORTANT for the application:
|
||||
* - remote_bda reported here is the Host PSEUDO (the same phone shows up as
|
||||
* two different addresses, one per identity). Use conn_id as the link key.
|
||||
* - Call esp_ble_gap_get_conn_identity(pseudo, &id) to recover the real peer
|
||||
* MAC and the local identity that the link was established with.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "ble50_dual_identity_server.h"
|
||||
|
||||
#define TAG "DUAL_ID"
|
||||
|
||||
#define ESP_APP_ID 0x55
|
||||
#define SVC_INST_ID 0
|
||||
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40
|
||||
#define MAX_CONN 3
|
||||
#define NOTIFY_ENABLE 0x0001
|
||||
|
||||
/* Two simultaneously-advertising connectable extended advertising sets. */
|
||||
#define ADV_HANDLE_PUBLIC 0 /* identity A : Public */
|
||||
#define ADV_HANDLE_RANDOM 1 /* identity B : Static Random */
|
||||
#define NUM_ADV_SET 2
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* A FIXED static random address for identity B. It MUST be persistent across
|
||||
* reboots (top two bits = 0b11) so the per-identity bond bucket matches on
|
||||
* reconnect. Do NOT generate it randomly at every boot. */
|
||||
static esp_bd_addr_t s_identity_b_addr = {0xC1, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
static uint16_t s_handle_table[HRS_IDX_NB];
|
||||
|
||||
/* Track which advertising sets are currently on-air so that on a disconnect we
|
||||
* only restart the set(s) that had been consumed by a connection. */
|
||||
static bool s_adv_on_air[NUM_ADV_SET];
|
||||
|
||||
/* ---- advertising data: one human-readable name per identity ---- */
|
||||
static uint8_t adv_data_public[] = {
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x12, ESP_BLE_AD_TYPE_NAME_CMPL,
|
||||
'E', 'S', 'P', '_', 'D', 'U', 'A', 'L', '_', 'P', 'U', 'B', 'L', 'I', 'C', '_', 'A',
|
||||
};
|
||||
|
||||
static uint8_t adv_data_random[] = {
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x12, ESP_BLE_AD_TYPE_NAME_CMPL,
|
||||
'E', 'S', 'P', '_', 'D', 'U', 'A', 'L', '_', 'R', 'A', 'N', 'D', 'O', 'M', '_', 'B',
|
||||
};
|
||||
|
||||
static esp_ble_gap_ext_adv_t s_ext_adv[NUM_ADV_SET] = {
|
||||
[0] = {ADV_HANDLE_PUBLIC, 0, 0},
|
||||
[1] = {ADV_HANDLE_RANDOM, 0, 0},
|
||||
};
|
||||
|
||||
static esp_ble_gap_ext_adv_params_t s_adv_params_public = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_2M,
|
||||
.sid = 0,
|
||||
.scan_req_notif = false,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
};
|
||||
|
||||
static esp_ble_gap_ext_adv_params_t s_adv_params_random = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_2M,
|
||||
.sid = 1,
|
||||
.scan_req_notif = false,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
};
|
||||
|
||||
/* ---- minimal GATT database (one read/write/notify characteristic) ---- */
|
||||
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
|
||||
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
|
||||
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
|
||||
static const uint16_t GATTS_SERVICE_UUID_TEST = 0x00FF;
|
||||
static const uint16_t GATTS_CHAR_UUID_TEST_A = 0xFF01;
|
||||
static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
|
||||
static const uint8_t ccc[2] = {0x00, 0x00};
|
||||
static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};
|
||||
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
|
||||
|
||||
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] = {
|
||||
[IDX_SVC] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
|
||||
sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},
|
||||
|
||||
[IDX_CHAR_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
|
||||
CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},
|
||||
|
||||
/* Require encryption to read/write so the link must be paired/encrypted. */
|
||||
[IDX_CHAR_VAL_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A,
|
||||
ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED,
|
||||
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
|
||||
|
||||
[IDX_CHAR_CFG_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid,
|
||||
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
sizeof(uint16_t), sizeof(ccc), (uint8_t *)ccc}},
|
||||
};
|
||||
|
||||
static esp_gatt_if_t s_gatts_if = ESP_GATT_IF_NONE;
|
||||
|
||||
/* Per-link notify subscription (CCC). Indexed by conn_id. */
|
||||
static bool s_notify_enabled[MAX_CONN];
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static const char *auth_req_to_str(esp_ble_auth_req_t auth_req)
|
||||
{
|
||||
switch (auth_req) {
|
||||
case ESP_LE_AUTH_NO_BOND: return "NO_BOND";
|
||||
case ESP_LE_AUTH_BOND: return "BOND";
|
||||
case ESP_LE_AUTH_REQ_MITM: return "MITM";
|
||||
case ESP_LE_AUTH_REQ_BOND_MITM: return "BOND_MITM";
|
||||
case ESP_LE_AUTH_REQ_SC_ONLY: return "SC_ONLY";
|
||||
case ESP_LE_AUTH_REQ_SC_BOND: return "SC_BOND";
|
||||
case ESP_LE_AUTH_REQ_SC_MITM: return "SC_MITM";
|
||||
case ESP_LE_AUTH_REQ_SC_MITM_BOND: return "SC_MITM_BOND";
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static void show_bonded_devices(void)
|
||||
{
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
if (dev_num <= 0) {
|
||||
ESP_LOGI(TAG, "Bonded devices: 0");
|
||||
return;
|
||||
}
|
||||
esp_ble_bond_dev_t *list = malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
esp_ble_get_bond_device_list(&dev_num, list);
|
||||
ESP_LOGI(TAG, "Bonded devices: %d (each entry is a (local,peer) identity = one pseudo)", dev_num);
|
||||
for (int i = 0; i < dev_num; i++) {
|
||||
ESP_LOGI(TAG, " [%d] pseudo "ESP_BD_ADDR_STR"", i, ESP_BD_ADDR_HEX(list[i].bd_addr));
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
#if CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
static void log_conn_identity(const char *tag, esp_bd_addr_t pseudo)
|
||||
{
|
||||
esp_ble_conn_identity_t id;
|
||||
if (esp_ble_gap_get_conn_identity(pseudo, &id) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "%s identity: real peer "ESP_BD_ADDR_STR" (type %u), local "ESP_BD_ADDR_STR" (type %u)",
|
||||
tag,
|
||||
ESP_BD_ADDR_HEX(id.peer_addr), id.peer_addr_type,
|
||||
ESP_BD_ADDR_HEX(id.local_addr), id.local_addr_type);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s identity: pseudo not finalized yet (link may still be on real peer)", tag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void request_ext_adv_start(uint8_t inst)
|
||||
{
|
||||
if (inst >= NUM_ADV_SET || s_adv_on_air[inst]) {
|
||||
return;
|
||||
}
|
||||
esp_err_t ret = esp_ble_gap_ext_adv_start(1, &s_ext_adv[inst]);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ext_adv_start enqueue failed for instance %u: %s", inst, esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
static void start_idle_adv_sets(void)
|
||||
{
|
||||
for (int i = 0; i < NUM_ADV_SET; i++) {
|
||||
request_ext_adv_start(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Set random addr (identity B) done, status %d", param->ext_adv_set_rand_addr.status);
|
||||
if (param->ext_adv_set_rand_addr.status == ESP_BT_STATUS_SUCCESS) {
|
||||
esp_ble_gap_config_ext_adv_data_raw(ADV_HANDLE_RANDOM, sizeof(adv_data_random), adv_data_random);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Identity B random addr failed, skip adv data/start");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Ext adv params set, instance %u, status %d",
|
||||
param->ext_adv_set_params.instance, param->ext_adv_set_params.status);
|
||||
if (param->ext_adv_set_params.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Ext adv params failed for instance %u, skip downstream setup",
|
||||
param->ext_adv_set_params.instance);
|
||||
break;
|
||||
}
|
||||
if (param->ext_adv_set_params.instance == ADV_HANDLE_PUBLIC) {
|
||||
esp_ble_gap_config_ext_adv_data_raw(ADV_HANDLE_PUBLIC, sizeof(adv_data_public), adv_data_public);
|
||||
esp_ble_gap_ext_adv_set_params(ADV_HANDLE_RANDOM, &s_adv_params_random);
|
||||
} else if (param->ext_adv_set_params.instance == ADV_HANDLE_RANDOM) {
|
||||
esp_ble_gap_ext_adv_set_rand_addr(ADV_HANDLE_RANDOM, s_identity_b_addr);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Ext adv data set, status %d, instance %u",
|
||||
param->ext_adv_data_set.status, param->ext_adv_data_set.instance);
|
||||
if (param->ext_adv_data_set.status == ESP_BT_STATUS_SUCCESS) {
|
||||
request_ext_adv_start(param->ext_adv_data_set.instance);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Ext adv start, status %d, instance_num %u",
|
||||
param->ext_adv_start.status, param->ext_adv_start.instance_num);
|
||||
for (uint8_t j = 0; j < param->ext_adv_start.instance_num; j++) {
|
||||
uint8_t inst = param->ext_adv_start.instance[j];
|
||||
if (inst >= NUM_ADV_SET) {
|
||||
continue;
|
||||
}
|
||||
if (param->ext_adv_start.status == ESP_BT_STATUS_SUCCESS) {
|
||||
s_adv_on_air[inst] = true;
|
||||
} else {
|
||||
s_adv_on_air[inst] = false;
|
||||
ESP_LOGE(TAG, "Ext adv start failed for instance %u", inst);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_TERMINATED_EVT:
|
||||
ESP_LOGI(TAG, "Adv terminated: instance %u status 0x%x conn_idx %u",
|
||||
param->adv_terminate.adv_instance, param->adv_terminate.status,
|
||||
param->adv_terminate.conn_idx);
|
||||
if (param->adv_terminate.adv_instance < NUM_ADV_SET) {
|
||||
/* This set produced a connection -> it stopped advertising. */
|
||||
s_adv_on_air[param->adv_terminate.adv_instance] = false;
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||
esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||
ESP_LOGI(TAG, "Passkey notify: %06" PRIu32, param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
case ESP_GAP_BLE_KEY_EVT:
|
||||
ESP_LOGI(TAG, "Key exchanged on link "ESP_BD_ADDR_STR" type %d",
|
||||
ESP_BD_ADDR_HEX(param->ble_security.ble_key.bd_addr),
|
||||
param->ble_security.ble_key.key_type);
|
||||
break;
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
|
||||
esp_bd_addr_t pseudo;
|
||||
memcpy(pseudo, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
|
||||
if (param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGI(TAG, "Pairing OK on link "ESP_BD_ADDR_STR", auth %s",
|
||||
ESP_BD_ADDR_HEX(pseudo),
|
||||
auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
|
||||
#if CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
log_conn_identity("AUTH_CMPL", pseudo);
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Pairing FAILED on link "ESP_BD_ADDR_STR", reason 0x%x",
|
||||
ESP_BD_ADDR_HEX(pseudo), param->ble_security.auth_cmpl.fail_reason);
|
||||
}
|
||||
show_bonded_devices();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Bond removed, status %d, pseudo "ESP_BD_ADDR_STR"",
|
||||
param->remove_bond_dev_cmpl.status,
|
||||
ESP_BD_ADDR_HEX(param->remove_bond_dev_cmpl.bd_addr));
|
||||
break;
|
||||
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||
ESP_LOGI(TAG, "Conn params update, link "ESP_BD_ADDR_STR", status %d, "
|
||||
"conn_int %u, latency %u, timeout %u",
|
||||
ESP_BD_ADDR_HEX(param->update_conn_params.bda),
|
||||
param->update_conn_params.status,
|
||||
param->update_conn_params.conn_int,
|
||||
param->update_conn_params.latency,
|
||||
param->update_conn_params.timeout);
|
||||
break;
|
||||
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Data length update, status %d, rx %u, tx %u",
|
||||
param->pkt_data_length_cmpl.status,
|
||||
param->pkt_data_length_cmpl.params.rx_len,
|
||||
param->pkt_data_length_cmpl.params.tx_len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GATTS_REG_EVT:
|
||||
if (param->reg.status == ESP_GATT_OK) {
|
||||
s_gatts_if = gatts_if;
|
||||
esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_CREAT_ATTR_TAB_EVT:
|
||||
if (param->add_attr_tab.status == ESP_GATT_OK && param->add_attr_tab.num_handle == HRS_IDX_NB) {
|
||||
memcpy(s_handle_table, param->add_attr_tab.handles, sizeof(s_handle_table));
|
||||
esp_ble_gatts_start_service(s_handle_table[IDX_SVC]);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Create attr table failed");
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_CONNECT_EVT:
|
||||
ESP_LOGI(TAG, "CONNECT conn_id %u, remote(pseudo) "ESP_BD_ADDR_STR"",
|
||||
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
|
||||
ESP_LOGI(TAG, " -> use conn_id %u as the link key (remote_bda may be a Host pseudo)",
|
||||
param->connect.conn_id);
|
||||
/* Trigger pairing / encryption for this link. */
|
||||
esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
|
||||
break;
|
||||
case ESP_GATTS_WRITE_EVT:
|
||||
if (param->write.is_prep) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "WRITE conn_id %u handle %u len %d",
|
||||
param->write.conn_id, param->write.handle, param->write.len);
|
||||
if (param->write.handle == s_handle_table[IDX_CHAR_CFG_A] && param->write.len == 2) {
|
||||
uint16_t descr_value = (param->write.value[1] << 8) | param->write.value[0];
|
||||
if (param->write.conn_id < MAX_CONN) {
|
||||
if (descr_value == NOTIFY_ENABLE) {
|
||||
s_notify_enabled[param->write.conn_id] = true;
|
||||
ESP_LOGI(TAG, "Notify enabled on conn_id %u", param->write.conn_id);
|
||||
} else if (descr_value == 0x0000) {
|
||||
s_notify_enabled[param->write.conn_id] = false;
|
||||
ESP_LOGI(TAG, "Notify disabled on conn_id %u", param->write.conn_id);
|
||||
}
|
||||
}
|
||||
} else if (param->write.handle == s_handle_table[IDX_CHAR_VAL_A]) {
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->write.value, param->write.len);
|
||||
if (param->write.conn_id < MAX_CONN && s_notify_enabled[param->write.conn_id] &&
|
||||
param->write.len > 0) {
|
||||
esp_err_t ret = esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id,
|
||||
s_handle_table[IDX_CHAR_VAL_A],
|
||||
param->write.len, param->write.value, false);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Notify echo failed on conn_id %u: %s",
|
||||
param->write.conn_id, esp_err_to_name(ret));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Notify echo sent on conn_id %u, len %d",
|
||||
param->write.conn_id, param->write.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_READ_EVT:
|
||||
ESP_LOGI(TAG, "READ conn_id %u (link is encrypted)", param->read.conn_id);
|
||||
break;
|
||||
case ESP_GATTS_MTU_EVT:
|
||||
ESP_LOGI(TAG, "MTU exchange, conn_id %u, mtu %u",
|
||||
param->mtu.conn_id, param->mtu.mtu);
|
||||
break;
|
||||
case ESP_GATTS_CONF_EVT:
|
||||
ESP_LOGI(TAG, "Notify/indicate confirm, conn_id %u, status %d, handle %u",
|
||||
param->conf.conn_id, param->conf.status, param->conf.handle);
|
||||
break;
|
||||
case ESP_GATTS_DISCONNECT_EVT:
|
||||
ESP_LOGI(TAG, "DISCONNECT conn_id %u, remote(pseudo) "ESP_BD_ADDR_STR", reason 0x%x",
|
||||
param->disconnect.conn_id, ESP_BD_ADDR_HEX(param->disconnect.remote_bda),
|
||||
param->disconnect.reason);
|
||||
if (param->disconnect.conn_id < MAX_CONN) {
|
||||
s_notify_enabled[param->disconnect.conn_id] = false;
|
||||
}
|
||||
/* Re-advertise only the set(s) that are not currently on-air. */
|
||||
start_idle_adv_sets();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_advertising(void)
|
||||
{
|
||||
/* Kick off the event-driven setup chain:
|
||||
* PUBLIC params -> PUBLIC data -> RANDOM params -> RANDOM rand_addr -> RANDOM data.
|
||||
* Each step is gated on the previous COMPLETE event status. */
|
||||
esp_err_t ret = esp_ble_gap_ext_adv_set_params(ADV_HANDLE_PUBLIC, &s_adv_params_public);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ext_adv_set_params(PUBLIC) enqueue failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
|
||||
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
|
||||
|
||||
esp_bluedroid_config_t cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_bluedroid_init_with_cfg(&cfg));
|
||||
ESP_ERROR_CHECK(esp_bluedroid_enable());
|
||||
|
||||
ESP_ERROR_CHECK(esp_ble_gatts_register_callback(gatts_event_handler));
|
||||
ESP_ERROR_CHECK(esp_ble_gap_register_callback(gap_event_handler));
|
||||
ESP_ERROR_CHECK(esp_ble_gatts_app_register(ESP_APP_ID));
|
||||
|
||||
/* Security: bond + MITM + Secure Connections, static passkey. */
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
|
||||
uint8_t key_size = 16;
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint32_t passkey = 123456;
|
||||
uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE;
|
||||
uint8_t oob_support = ESP_BLE_OOB_DISABLE;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
|
||||
|
||||
#if !CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND
|
||||
ESP_LOGW(TAG, "CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND is DISABLED:");
|
||||
ESP_LOGW(TAG, " two identities of the same phone will share one bond and overwrite each other.");
|
||||
ESP_LOGW(TAG, " Enable it to get isolated bonds per local identity.");
|
||||
#else
|
||||
ESP_LOGI(TAG, "Pseudo-address dual-identity bond isolation ENABLED");
|
||||
#endif
|
||||
|
||||
setup_advertising();
|
||||
ESP_LOGI(TAG, "Advertising as two identities: Public (A) + Static Random "ESP_BD_ADDR_STR" (B)",
|
||||
ESP_BD_ADDR_HEX(s_identity_b_addr));
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef BLE50_DUAL_IDENTITY_SERVER_H
|
||||
#define BLE50_DUAL_IDENTITY_SERVER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Attributes State Machine */
|
||||
enum {
|
||||
IDX_SVC,
|
||||
IDX_CHAR_A,
|
||||
IDX_CHAR_VAL_A,
|
||||
IDX_CHAR_CFG_A,
|
||||
|
||||
HRS_IDX_NB,
|
||||
};
|
||||
|
||||
#endif // BLE50_DUAL_IDENTITY_SERVER_H
|
||||
@@ -0,0 +1,11 @@
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n
|
||||
CONFIG_BT_BLE_SMP_ENABLE=y
|
||||
CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND=y
|
||||
CONFIG_BT_ACL_CONNECTIONS=2
|
||||
CONFIG_BT_GATTS_ENABLE=y
|
||||
# CONFIG_BT_GATTC_ENABLE is not set
|
||||
# CONFIG_BT_BLE_50_DTM_TEST_EN is not set
|
||||
# CONFIG_BT_BLE_50_PERIODIC_ADV_EN is not set
|
||||
# CONFIG_BT_BLE_50_EXTEND_SCAN_EN is not set
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
CONFIG_BT_ENABLED=y
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_BT_ENABLED=y
|
||||
Reference in New Issue
Block a user