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
This commit is contained in:
@@ -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