mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(ble/bluedroid): Support bluedroid LE COC and EATT features
(cherry picked from commit 83f0831c53)
Co-authored-by: zhiweijian <zhiweijian@espressif.com>
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
|
||||
@@ -273,6 +276,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)
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
@@ -117,6 +117,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
|
||||
|
||||
|
||||
@@ -317,6 +317,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"
|
||||
|
||||
@@ -1629,6 +1629,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
|
||||
@@ -3226,3 +3230,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;
|
||||
|
||||
/**
|
||||
@@ -5167,6 +5180,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
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__ */
|
||||
|
||||
@@ -643,6 +643,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
|
||||
|
||||
@@ -1419,7 +1419,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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -387,9 +390,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 +424,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 +504,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 +586,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 +663,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 +722,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 +737,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"
|
||||
@@ -636,6 +639,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 +654,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 +704,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 +1231,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;
|
||||
|
||||
@@ -1775,12 +1808,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 +1836,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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -251,6 +254,9 @@ void gatt_notify_enc_cmpl(BD_ADDR bd_addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
gatt_eatt_on_encrypted(bd_addr);
|
||||
#endif
|
||||
} else {
|
||||
GATT_TRACE_DEBUG("notify GATT for encryption completion of unknown device");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -722,6 +725,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);
|
||||
@@ -797,11 +807,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;
|
||||
}
|
||||
@@ -891,7 +901,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;
|
||||
}
|
||||
@@ -1000,7 +1010,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 +1078,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,21 +1205,51 @@ 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 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);
|
||||
|
||||
rsp_code = gatt_cmd_to_rsp_code(rsp_code);
|
||||
p_clcb = NULL;
|
||||
#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) {
|
||||
rsp_code = gatt_cmd_to_rsp_code(cmd_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (p_clcb == NULL
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
&& eatt_bearer_lcid == 0
|
||||
#endif
|
||||
) {
|
||||
p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_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);
|
||||
|
||||
#if (BLE_EATT_CLIENT_INCLUDED == TRUE)
|
||||
/* 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 && eatt_bearer_lcid != 0) {
|
||||
gatt_eatt_mark_busy(p_tcb->peer_bda, eatt_bearer_lcid, cmd_code, clcb_idx);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
} else {
|
||||
btu_stop_timer (&p_clcb->rsp_timer_ent);
|
||||
@@ -1210,8 +1258,8 @@ void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
}
|
||||
/* 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);
|
||||
@@ -1272,7 +1320,12 @@ void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +179,11 @@ void gatt_free(void)
|
||||
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;
|
||||
tGATT_TCB *p_tcb = NULL;
|
||||
for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) {
|
||||
@@ -986,7 +998,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 +1010,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 +1026,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -442,13 +446,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 +487,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 +514,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 +526,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);
|
||||
}
|
||||
|
||||
@@ -875,7 +924,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 +1102,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 +1168,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 +1179,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;
|
||||
|
||||
@@ -1182,6 +1231,14 @@ static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data)
|
||||
BT_HDR *p_buf;
|
||||
UINT16 conn_id;
|
||||
|
||||
#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) {
|
||||
gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, GATT_REQ_MTU, 0, FALSE);
|
||||
@@ -1241,7 +1298,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 +1336,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;
|
||||
|
||||
@@ -1614,7 +1676,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 +1701,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,
|
||||
@@ -1958,8 +2020,8 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code,
|
||||
|
||||
/* 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 &&
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1301,6 +1304,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 {
|
||||
@@ -1336,6 +1353,16 @@ void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle)
|
||||
p_tcb->ind_count = 0;
|
||||
}
|
||||
|
||||
#if (BLE_EATT_INCLUDED == TRUE)
|
||||
if (p_tcb != NULL) {
|
||||
/* 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(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL);
|
||||
}
|
||||
#endif // (GATTC_INCLUDED == TRUE)
|
||||
|
||||
@@ -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);
|
||||
@@ -753,7 +792,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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -828,7 +856,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)
|
||||
@@ -1483,6 +1501,13 @@ UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p
|
||||
|
||||
/* 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 +1520,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 +1542,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 +1559,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 +1602,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;
|
||||
@@ -863,9 +920,25 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
p_ccb->peer_conn_cfg.credits = credits;
|
||||
|
||||
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 +964,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 +979,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 +994,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);
|
||||
@@ -952,6 +1083,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);
|
||||
}
|
||||
|
||||
@@ -1675,7 +1810,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 +1933,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 +2028,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 */
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user