mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user