mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00: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:
@@ -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