summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--btif/src/btif_l2cap.c94
-rw-r--r--include/bt_target.h26
-rw-r--r--include/bt_testapp.h10
-rw-r--r--stack/Android.mk3
-rw-r--r--stack/avct/avct_l2c.c5
-rw-r--r--stack/avdt/avdt_l2c.c5
-rw-r--r--stack/btm/btm_ble.c18
-rw-r--r--stack/btm/btm_ble_addr.c5
-rw-r--r--stack/btm/btm_int.h11
-rw-r--r--stack/btm/btm_sec.c324
-rw-r--r--stack/btu/btu_task.c5
-rw-r--r--stack/gatt/gatt_main.c5
-rw-r--r--stack/hid/hidh_conn.c5
-rw-r--r--stack/include/btm_api.h38
-rw-r--r--stack/include/btm_ble_api.h14
-rw-r--r--stack/include/l2c_api.h105
-rw-r--r--stack/include/l2cdefs.h33
-rw-r--r--stack/l2cap/l2c_api.c45
-rw-r--r--stack/l2cap/l2c_ble.c560
-rw-r--r--stack/l2cap/l2c_csm.c9
-rw-r--r--stack/l2cap/l2c_fcr.c173
-rw-r--r--stack/l2cap/l2c_int.h80
-rw-r--r--stack/l2cap/l2c_le_csm.c619
-rw-r--r--stack/l2cap/l2c_link.c114
-rw-r--r--stack/l2cap/l2c_main.c68
-rw-r--r--stack/l2cap/l2c_ucd.c43
-rw-r--r--stack/l2cap/l2c_utils.c452
-rw-r--r--stack/mcap/mca_api.c5
-rw-r--r--stack/mcap/mca_l2c.c5
-rw-r--r--test/l2test_ertm/l2test_ertm.c5
30 files changed, 2861 insertions, 23 deletions
diff --git a/btif/src/btif_l2cap.c b/btif/src/btif_l2cap.c
index eb4900691..87a313499 100644
--- a/btif/src/btif_l2cap.c
+++ b/btif/src/btif_l2cap.c
@@ -80,6 +80,14 @@ static BOOLEAN L2cap_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8
static BOOLEAN L2cap_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr);
static UINT8 L2cap_GetChnlFcrMode (UINT16 lcid);
static UINT16 L2cap_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+static bt_status_t L2cap_LE_Register (UINT16 le_psm, BOOLEAN ConnType, UINT16 SecLevel, UINT8 enc_key_size);
+static bt_status_t L2cap_LE_DeRegister (UINT16 psm);
+static UINT16 L2cap_LE_Connect(BD_ADDR address, tL2CAP_LE_CONN_INFO *conn_info);
+static BOOLEAN L2cap_LE_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, tL2CAP_LE_CONN_INFO *conn_info);
+static BOOLEAN L2cap_LE_FlowControl (UINT16 lcid, UINT16 credits);
+static void L2cap_LE_freebuf(BT_HDR *p_buf);
+#endif
static const btl2cap_interface_t btl2capInterface = {
sizeof(btl2cap_interface_t),
@@ -118,6 +126,14 @@ static const btl2cap_interface_t btl2capInterface = {
L2cap_GetChnlFcrMode,
L2cap_SendFixedChnlData,
NULL, // cleanup,
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ L2cap_LE_Register,
+ L2cap_LE_DeRegister,
+ L2cap_LE_Connect,
+ L2cap_LE_ConnectRsp,
+ L2cap_LE_FlowControl,
+ L2cap_LE_freebuf,
+#endif
};
const btl2cap_interface_t *btif_l2cap_get_interface(void)
@@ -177,6 +193,84 @@ static bt_status_t L2cap_Register (UINT16 psm, BOOLEAN ConnType, UINT16 SecLevel
return BT_STATUS_SUCCESS;
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function L2cap_LE_Register
+**
+** Description This function is called during the task startup
+** to register interface functions with L2CAP.
+**
+*******************************************************************************/
+static bt_status_t L2cap_LE_Register (UINT16 le_psm, BOOLEAN ConnType, UINT16 SecLevel,
+ UINT8 enc_key_size)
+{
+
+ BTIF_TRACE_DEBUG("LE-L2CAP: %s le_psm=%d, SecLevel=%d ", __FUNCTION__, le_psm, SecLevel);
+ if (!BTM_SetBleSecurityLevel (ConnType, "l2c_le_test", BTM_SEC_SERVICE_ATT,
+ SecLevel, le_psm))
+ {
+ BTIF_TRACE_ERROR("LE-L2CAP: BTM_SetSecurityLevel failed");
+ return BT_STATUS_FAIL;
+ }
+
+ if (!BTM_SetBleEncKeySize ("l2c_le_test", enc_key_size, le_psm))
+ {
+ BTIF_TRACE_ERROR("LE-L2CAP: BTM_SetBleEncKeySize failed");
+ return BT_STATUS_FAIL;
+ }
+
+ g_Psm = L2CA_LE_Register (le_psm, pl2test_l2c_appl);
+
+ if(0 == g_Psm) {
+ BTIF_TRACE_ERROR("LE-L2CAP: L2cap_LE_Register failed");
+ return BT_STATUS_FAIL;
+ }
+ return BT_STATUS_SUCCESS;
+}
+
+static UINT16 L2cap_LE_Connect(BD_ADDR address, tL2CAP_LE_CONN_INFO *conn_info)
+{
+ BTIF_TRACE_DEBUG("LE-L2CAP: %s:: %0x %0x %0x %0x %0x %0x", __FUNCTION__,
+ address[0], address[1], address[2],address[3],address[4],address[5]);
+
+ if (0 == (g_lcid = L2CA_LE_CreditBasedConn_Req (address, conn_info))) {
+ BTIF_TRACE_ERROR("LE-L2CAP: L2CA_LE_CreditBasedConn_Req failed for le_psm %d",
+ conn_info->le_psm);
+ }
+ return g_lcid;
+}
+
+static BOOLEAN L2cap_LE_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid,
+ tL2CAP_LE_CONN_INFO *conn_info)
+{
+ if (!L2CA_LE_CreditBasedConn_Rsp (p_bd_addr, id, lcid, conn_info)) {
+ BTIF_TRACE_ERROR("LE-L2CAP: L2CA_LE_CreditBasedConn_Rsp failed");
+ return BT_STATUS_FAIL;
+ }
+ return BT_STATUS_SUCCESS;
+}
+
+static BOOLEAN L2cap_LE_FlowControl (UINT16 lcid, UINT16 credits)
+{
+ if (!L2CA_LE_SetFlowControlCredits (lcid, credits)) {
+ BTIF_TRACE_ERROR("LE-L2CAP: L2CA_LE_SetFlowControlCredits failed");
+ return BT_STATUS_FAIL;
+ }
+ return BT_STATUS_SUCCESS;
+}
+static void L2cap_LE_freebuf (BT_HDR *p_buf)
+{
+ GKI_freebuf(p_buf);
+}
+
+static bt_status_t L2cap_LE_DeRegister (UINT16 psm)
+{
+ L2CA_LE_Deregister(psm);
+ return BT_STATUS_SUCCESS;
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
static bt_status_t L2cap_DeRegister (UINT16 psm)
{
L2CA_Deregister(psm);
diff --git a/include/bt_target.h b/include/bt_target.h
index a4cbc8218..ffbcc0efa 100644
--- a/include/bt_target.h
+++ b/include/bt_target.h
@@ -808,6 +808,32 @@
#define BLE_INCLUDED TRUE
#endif
+/*LE_L2CAP_CODE*/
+/*LE Credit Based Flow Control Mode*/
+#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
+#ifndef LE_L2CAP_CFC_INCLUDED
+#define LE_L2CAP_CFC_INCLUDED TRUE
+#define HCI_LE_ACL_POOL_ID GKI_POOL_ID_3
+
+/* The maximum number of simultaneous channels that L2CAP can support. */
+#ifndef MAX_L2CAP_CHANNELS
+#define MAX_L2CAP_CHANNELS 20
+#else
+#undef MAX_L2CAP_CHANNELS
+#define MAX_L2CAP_CHANNELS 20
+#endif
+
+/* The maximum number of simultaneous applications that can register with L2CAP. */
+#ifndef MAX_L2CAP_CLIENTS
+#define MAX_L2CAP_CLIENTS MAX_L2CAP_CHANNELS - 1
+#else
+#undef MAX_L2CAP_CLIENTS
+#define MAX_L2CAP_CLIENTS MAX_L2CAP_CHANNELS - 1
+#endif
+
+#endif /*LE_L2CAP_CFC_INCLUDED */
+#endif /* BLE_INCLUDED */
+
#ifndef BLE_ANDROID_CONTROLLER_SCAN_FILTER
#define BLE_ANDROID_CONTROLLER_SCAN_FILTER TRUE
#endif
diff --git a/include/bt_testapp.h b/include/bt_testapp.h
index 37e39ca35..f3a805ad6 100644
--- a/include/bt_testapp.h
+++ b/include/bt_testapp.h
@@ -121,6 +121,16 @@ typedef struct {
UINT8 (*GetChnlFcrMode)(UINT16 lcid);
UINT16 (*SendFixedChnlData)(UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf);
void (*Cleanup)(void);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ bt_status_t (*RegisterLePsm) (UINT16 le_psm, BOOLEAN ConnType, UINT16 SecLevel,
+ UINT8 enc_key_size);
+ bt_status_t (*LeDeregister)(UINT16 psm);
+ UINT16 (*LeConnect) (BD_ADDR address, tL2CAP_LE_CONN_INFO *conn_info);
+ BOOLEAN (*LeConnectRsp) (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid,
+ tL2CAP_LE_CONN_INFO *conn_info);
+ BOOLEAN (*LeFlowControl) (UINT16 lcid, UINT16 credits);
+ void (*LeFreeBuf)(BT_HDR *p_buf);
+#endif
} btl2cap_interface_t;
typedef struct
diff --git a/stack/Android.mk b/stack/Android.mk
index 4c5c3e7c8..adf2fb1b2 100644
--- a/stack/Android.mk
+++ b/stack/Android.mk
@@ -160,7 +160,8 @@ LOCAL_SRC_FILES:= \
./gap/gap_api.c \
./gap/gap_ble.c \
./gap/gap_conn.c \
- ./gap/gap_utils.c
+ ./gap/gap_utils.c \
+ ./l2cap/l2c_le_csm.c
LOCAL_MODULE := libbt-brcm_stack
LOCAL_MODULE_TAGS := optional
diff --git a/stack/avct/avct_l2c.c b/stack/avct/avct_l2c.c
index 0e36bbb2f..e383db22f 100644
--- a/stack/avct/avct_l2c.c
+++ b/stack/avct/avct_l2c.c
@@ -78,6 +78,11 @@ const tL2CAP_APPL_INFO avct_l2c_appl = {
avct_l2c_data_ind_cback,
avct_l2c_congestion_ind_cback,
NULL /* tL2CA_TX_COMPLETE_CB */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
};
#if (AVCT_BROWSE_INCLUDED == TRUE)
diff --git a/stack/avdt/avdt_l2c.c b/stack/avdt/avdt_l2c.c
index f487bcfbc..dde8a6846 100644
--- a/stack/avdt/avdt_l2c.c
+++ b/stack/avdt/avdt_l2c.c
@@ -58,6 +58,11 @@ const tL2CAP_APPL_INFO avdt_l2c_appl = {
avdt_l2c_data_ind_cback,
avdt_l2c_congestion_ind_cback,
NULL /* tL2CA_TX_COMPLETE_CB */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
};
/*******************************************************************************
diff --git a/stack/btm/btm_ble.c b/stack/btm/btm_ble.c
index eb9e86461..05166b90e 100644
--- a/stack/btm/btm_ble.c
+++ b/stack/btm/btm_ble.c
@@ -49,6 +49,7 @@ extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable);
extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda);
#endif
extern void gatt_notify_enc_cmpl(BD_ADDR bd_addr);
+
/*******************************************************************************/
/* External Function to be called by other modules */
/*******************************************************************************/
@@ -1893,6 +1894,23 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced)
btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE;
btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE);
#endif
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ handle = HCID_GET_HANDLE (handle);
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+ /* Link is not connected. For all channels, send the event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ if(p_lcb)
+ {
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: %s Notifying connection Failed", __FUNCTION__);
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, (void*)&status);
+ }
+ p_lcb->link_state = LST_DISCONNECTED;
+ }
+#endif
}
}
diff --git a/stack/btm/btm_ble_addr.c b/stack/btm/btm_ble_addr.c
index 96fb050f7..86606d331 100644
--- a/stack/btm/btm_ble_addr.c
+++ b/stack/btm/btm_ble_addr.c
@@ -352,8 +352,6 @@ static BOOLEAN btm_ble_match_random_bda(UINT16 rec_index)
rand[1] = p_mgnt_cb->random_bda[1];
rand[2] = p_mgnt_cb->random_bda[0];
- BTM_TRACE_EVENT("%s rec_index = %d", __func__, rec_index);
-
if (rec_index < BTM_SEC_MAX_DEVICE_RECORDS)
{
tSMP_ENC output;
@@ -369,6 +367,9 @@ static BOOLEAN btm_ble_match_random_bda(UINT16 rec_index)
/* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN,
&rand[0], 3, &output);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ BTM_TRACE_EVENT("btm_ble_match_random_bda matched rec_index = %d", rec_index);
+#endif
return btm_ble_proc_resolve_x(&output);
}
else
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index f8e0ca7f9..d4abf2380 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -440,6 +440,10 @@ typedef struct
UINT8 orig_service_name[BTM_SEC_SERVICE_NAME_LEN + 1];
UINT8 term_service_name[BTM_SEC_SERVICE_NAME_LEN + 1];
#endif
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ UINT8 enc_key_size; /* BLE encryption key size */
+ tBT_TRANSPORT transport;
+#endif
} tBTM_SEC_SERV_REC;
#if BLE_INCLUDED == TRUE
@@ -1111,7 +1115,14 @@ extern void btm_read_local_oob_complete (UINT8 *p);
extern void btm_acl_resubmit_page (void);
extern void btm_acl_reset_paging (void);
extern void btm_acl_paging (BT_HDR *p, BD_ADDR dest);
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+UINT8 btm_sec_clr_service_by_psm (UINT16 psm, tBT_TRANSPORT transport);
+int btm_sec_l2cap_le_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle,
+ CONNECTION_TYPE conn_type, tBTM_SEC_CALLBACK *p_callback, void *p_ref_data);
+#else
extern UINT8 btm_sec_clr_service_by_psm (UINT16 psm);
+#endif
extern void btm_sec_clr_temp_auth_service (BD_ADDR bda);
#ifdef __cplusplus
diff --git a/stack/btm/btm_sec.c b/stack/btm/btm_sec.c
index 9fe113e58..a6b45076e 100644
--- a/stack/btm/btm_sec.c
+++ b/stack/btm/btm_sec.c
@@ -55,8 +55,17 @@ BOOLEAN (APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR bd_addr);
/********************************************************************************
** L O C A L F U N C T I O N P R O T O T Y P E S *
*********************************************************************************/
-static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (BOOLEAN is_originator, UINT16 psm);
+static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ );
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur,
+ tBT_TRANSPORT transport);
+#else
static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur);
+#endif
static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm,
UINT32 mx_proto_id,
UINT32 mx_chan_id);
@@ -90,9 +99,15 @@ static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UIN
UINT8 btm_sec_start_role_switch (tBTM_SEC_DEV_REC *p_dev_rec);
tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id,
+ UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
+ UINT32 mx_chan_id, tBT_TRANSPORT transport);
+#else
static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id,
UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
UINT32 mx_chan_id);
+#endif
static BOOLEAN btm_dev_authenticated(tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_dev_encrypted(tBTM_SEC_DEV_REC *p_dev_rec);
@@ -487,13 +502,102 @@ BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service
conn_type = CONN_ORIENT_TERM;
return(btm_sec_set_security_level (conn_type, p_name, service_id,
- sec_level, psm, mx_proto_id, mx_chan_id));
+ sec_level, psm, mx_proto_id, mx_chan_id
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , BT_TRANSPORT_BR_EDR
+#endif
+ ));
#else
return(btm_sec_set_security_level (is_originator, p_name, service_id,
- sec_level, psm, mx_proto_id, mx_chan_id));
+ sec_level, psm, mx_proto_id, mx_chan_id
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , BT_TRANSPORT_BR_EDR
+#endif
+ ));
#endif
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+
+/*******************************************************************************
+**
+** Function BTM_SetBleSecurityLevel
+**
+** Description Register service security level with Security Manager
+**
+** Parameters: is_originator - TRUE if originating the connection, FALSE if not
+** p_name - Name of the service relevant only if
+** authorization will show this name to user. ignored
+** if BTM_SEC_SERVICE_NAME_LEN is 0.
+** service_id - service ID for the service passed to authorization callback
+** sec_level - bit mask of the security features
+** psm - L2CAP PSM
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN BTM_SetBleSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id,
+ UINT16 sec_level, UINT16 psm)
+{
+ BTM_TRACE_API("LE-L2CAP: %s sec_level %d",__FUNCTION__, sec_level);
+ return(btm_sec_set_security_level (is_originator, p_name, service_id,
+ sec_level, psm, 0, 0, BT_TRANSPORT_LE));
+}
+
+/*******************************************************************************
+**
+** Function BTM_SetBleEncKeySize
+**
+** Description Register the required LE encryption key size with Security Manager
+**
+** Parameters: p_name - Name of the service relevant only if
+** authorization will show this name to user. ignored
+** if BTM_SEC_SERVICE_NAME_LEN is 0.
+** mx_proto_id - Protocol ID for the service passed
+** enc_key_size- required LE encryption key size
+** le_psm - LE L2CAP PSM
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN BTM_SetBleEncKeySize (char *p_name, UINT8 enc_key_size, UINT16 le_psm)
+{
+ tBTM_SEC_SERV_REC *p_srec;
+ UINT16 index;
+ BOOLEAN record_found = FALSE;
+
+ p_srec = &btm_cb.sec_serv_rec[0];
+
+
+ for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++)
+ {
+ /* Check if there is already a record for this service */
+ if (p_srec->security_flags & BTM_SEC_IN_USE)
+ {
+#if BTM_SEC_SERVICE_NAME_LEN > 0
+ if (p_srec->psm == le_psm &&
+ BT_TRANSPORT_LE == p_srec->transport &&
+ (!strncmp (p_name, (char *) p_srec->orig_service_name,
+ BTM_SEC_SERVICE_NAME_LEN) ||
+ !strncmp (p_name, (char *) p_srec->term_service_name,
+ BTM_SEC_SERVICE_NAME_LEN)))
+#else
+ if (p_srec->psm == psm &&
+ BT_TRANSPORT_LE == p_srec->transport &&
+ mx_proto_id == p_srec->mx_proto_id)
+#endif
+ {
+ p_srec->enc_key_size = enc_key_size;
+ record_found = TRUE;
+ break;
+ }
+ }
+ }
+ return record_found;
+}
+
+#endif
+
/*******************************************************************************
**
** Function btm_sec_set_security_level
@@ -515,7 +619,11 @@ BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service
*******************************************************************************/
static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id,
UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
- UINT32 mx_chan_id)
+ UINT32 mx_chan_id
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+)
{
tBTM_SEC_SERV_REC *p_srec;
UINT16 index;
@@ -557,6 +665,9 @@ static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_na
{
#if BTM_SEC_SERVICE_NAME_LEN > 0
if (p_srec->psm == psm &&
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_srec->transport == transport &&
+#endif
p_srec->mx_proto_id == mx_proto_id &&
service_id == p_srec->service_id &&
(!strncmp (p_name, (char *) p_srec->orig_service_name,
@@ -599,6 +710,9 @@ static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_na
p_srec->psm = psm;
p_srec->service_id = service_id;
p_srec->mx_proto_id = mx_proto_id;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_srec->transport = transport;
+#endif
if (is_originator)
{
@@ -753,6 +867,9 @@ UINT8 BTM_SecClrService (UINT8 service_id)
{
/* Delete services with specified name (if in use and not SDP) */
if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm != BT_PSM_SDP) &&
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ (BT_TRANSPORT_BR_EDR == p_srec->transport) &&
+#endif
(!service_id || (service_id == p_srec->service_id)))
{
BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d", i, service_id);
@@ -783,7 +900,11 @@ UINT8 BTM_SecClrService (UINT8 service_id)
** Returns Number of records that were freed.
**
*******************************************************************************/
-UINT8 btm_sec_clr_service_by_psm (UINT16 psm)
+UINT8 btm_sec_clr_service_by_psm (UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ )
{
tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0];
UINT8 num_freed = 0;
@@ -792,7 +913,11 @@ UINT8 btm_sec_clr_service_by_psm (UINT16 psm)
for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++)
{
/* Delete services with specified name (if in use and not SDP) */
- if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm) )
+ if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm)
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ && (transport == p_srec->transport)
+#endif
+ )
{
BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d ", i, p_srec->service_id);
p_srec->security_flags = 0;
@@ -2097,6 +2222,159 @@ static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_origi
}
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+ **
+ ** Function btm_sec_l2cap_le_access_req
+ **
+ ** Description This function is called by the LE L2CAP to grant permission to
+ ** establish L2CAP connection to or from the peer device.
+ **
+ ** Parameters: bd_addr - Address of the peer device
+ ** psm - L2CAP PSM
+ ** is_originator - TRUE if protocol above L2CAP originates
+ ** connection
+ ** p_callback - Pointer to callback function called if
+ ** this function returns PENDING after required
+ ** procedures are complete. MUST NOT BE NULL.
+ **
+ ** Returns tBTM_STATUS
+ **
+ *******************************************************************************/
+
+int btm_sec_l2cap_le_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle,
+ CONNECTION_TYPE conn_type, tBTM_SEC_CALLBACK *p_callback, void *p_ref_data)
+{
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ tBTM_SEC_SERV_REC *p_serv_rec;
+ UINT16 security_required;
+ BOOLEAN is_originator = conn_type;
+ tBT_TRANSPORT transport = BT_TRANSPORT_LE;
+ tBTM_BLE_SEC_ACT btm_ble_sec_act;
+
+ BTM_TRACE_DEBUG ("LE-L2CAP: %s is_originator:%d", __FUNCTION__, is_originator);
+
+ /* Find or get oldest record */
+ p_dev_rec = btm_find_or_alloc_dev (bd_addr);
+
+
+ p_dev_rec->ble_hci_handle = handle;
+ /* Find the service record for the PSM */
+ p_serv_rec = btm_sec_find_first_serv (conn_type, psm, transport);
+
+ /* If there is no application registered with this PSM do not allow connection */
+ if (!p_serv_rec)
+ {
+ BTM_TRACE_WARNING ("LE-L2CAP: PSM:%d no application registerd", psm);
+ if (p_callback)
+ (*p_callback) (bd_addr, transport, p_ref_data, BTM_BLE_NO_PSM);
+
+ return(BTM_BLE_NO_PSM);
+ }
+
+ security_required = p_serv_rec->security_flags;
+
+ BTM_TRACE_WARNING ("LE-L2CAP: Security Manager: l2cap_le_access_req PSM:%d Handle:%d"\
+ "State:%d Flags:0x%x Required:0x%x Service ID:%d", \
+ psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, security_required,
+ p_serv_rec->service_id);
+
+ if (is_originator)
+ {
+ if (((security_required & BTM_SEC_OUT_AUTHENTICATE ) && !(p_dev_rec->sec_flags &
+ (BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_LE_AUTHENTICATED))) ||
+ ((security_required & BTM_SEC_OUT_ENCRYPT) && !(p_dev_rec->sec_flags &
+ BTM_SEC_LE_ENCRYPTED)))
+ {
+ btm_ble_sec_act = BTM_BLE_SEC_NONE;
+ tACL_CONN *p = btm_bda_to_acl(bd_addr, transport);
+
+ if(p == NULL)
+ {
+ if (p_callback)
+ (*p_callback) (bd_addr, transport, p_ref_data, BTM_UNKNOWN_ADDR);
+ return BTM_UNKNOWN_ADDR;
+ }
+ if( (security_required & BTM_SEC_OUT_AUTHENTICATE) || /* authenticated encryption */
+ ((security_required & BTM_SEC_OUT_AUTHENTICATE) &&
+ (security_required & BTM_SEC_OUT_ENCRYPT)) )
+ {
+ /* check if the device is not authenticated */
+ if(!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED))
+ {
+ btm_ble_sec_act = BTM_BLE_SEC_ENCRYPT_MITM;
+ }
+ /* check if the link is not encrypted */
+ else if(!(p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED))
+ {
+ btm_ble_sec_act = BTM_BLE_SEC_ENCRYPT;
+ }
+ }
+ else if(security_required & BTM_SEC_OUT_ENCRYPT) /* unauthenticated encryption */
+ {
+ /* check if we know the link key of remote device */
+ if(!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN))
+ {
+ btm_ble_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM;
+ }
+ /* check if the link is not encrypted */
+ else if( !(p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED))
+ {
+ btm_ble_sec_act = BTM_BLE_SEC_ENCRYPT;
+ }
+ }
+ if(btm_ble_sec_act == BTM_BLE_SEC_NONE)
+ {
+ BTM_TRACE_WARNING ("LE-L2CAP: Security Manager: Succeded ");
+ if (p_callback)
+ (*p_callback) (bd_addr, transport, p_ref_data, BTM_BLE_SUCCESS);
+ return BTM_BLE_SUCCESS;
+ }
+ else
+ {
+ /* Save pointer to service record */
+ p_dev_rec->p_cur_service = p_serv_rec;
+ p_dev_rec->p_ref_data = p_ref_data;
+ p_dev_rec->p_callback = p_callback;
+ BTM_TRACE_WARNING ("LE-L2CAP: Security Manager: starting security");
+ return btm_ble_set_encryption(bd_addr, &btm_ble_sec_act, p->link_role);
+ }
+ }
+ else
+ {
+ BTM_TRACE_WARNING ("LE-L2CAP: Security Manager: No security set ");
+ if (p_callback)
+ (*p_callback) (bd_addr, transport, p_ref_data, BTM_BLE_SUCCESS);
+ return BTM_BLE_SUCCESS;
+ }
+ }
+ else
+ {
+ if ((security_required & BTM_SEC_IN_AUTHENTICATE ) && !(p_dev_rec->sec_flags &
+ (BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_LE_AUTHENTICATED)))
+ {
+ BTM_TRACE_ERROR( "LE-L2CAP: GATT_INSUF_AUTHENTICATION");
+ return L2CAP_LE_CONN_INSUFFI_AUTHENTICATION;
+ }
+ else if((security_required & BTM_SEC_IN_ENCRYPT) && !(p_dev_rec->sec_flags &
+ BTM_SEC_LE_ENCRYPTED))
+ {
+ BTM_TRACE_ERROR( "LE-L2CAP: L2CAP_LE_CONN_INSUFFI_ENCRYPT");
+ return L2CAP_LE_CONN_INSUFFI_ENCRYPT;
+ }
+ else if((security_required & BTM_SEC_IN_ENCRYPT) && (p_dev_rec->sec_flags &
+ BTM_SEC_LE_ENCRYPTED) && p_dev_rec->enc_key_size <
+ p_serv_rec->enc_key_size)
+ {
+ BTM_TRACE_ERROR( "LE-L2CAP: L2CAP_LE_CONN_INSUFFI_ENCRYPT_KEY_SIZE");
+ return L2CAP_LE_CONN_INSUFFI_ENCRYPT_KEY_SIZE;
+ }
+ return(BTM_BLE_SUCCESS);
+ }
+}
+
+#endif
+
/*******************************************************************************
**
** Function btm_sec_l2cap_access_req
@@ -2158,7 +2436,11 @@ tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle
p_dev_rec->hci_handle = handle;
/* Find the service record for the PSM */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_serv_rec = btm_sec_find_first_serv (conn_type, psm, BT_TRANSPORT_BR_EDR);
+#else
p_serv_rec = btm_sec_find_first_serv (conn_type, psm);
+#endif
/* If there is no application registered with this PSM do not allow connection */
if (!p_serv_rec)
@@ -2379,11 +2661,20 @@ tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle
/* If there are multiple service records used through the same PSM */
/* leave security decision for the multiplexor on the top */
#if (L2CAP_UCD_INCLUDED == TRUE)
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (((btm_sec_find_next_serv (p_serv_rec, BT_TRANSPORT_BR_EDR)) != NULL)
+ &&(!( conn_type & CONNECTION_TYPE_CONNLESS_MASK ))) /* if not UCD */
+#else
if (((btm_sec_find_next_serv (p_serv_rec)) != NULL)
&&(!( conn_type & CONNECTION_TYPE_CONNLESS_MASK ))) /* if not UCD */
+#endif
+#else
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((btm_sec_find_next_serv (p_serv_rec, BT_TRANSPORT_BR_EDR)) != NULL)
#else
if ((btm_sec_find_next_serv (p_serv_rec)) != NULL)
#endif
+#endif
{
BTM_TRACE_DEBUG ("no next_serv sm4:0x%x, chk:%d", p_dev_rec->sm4, chk_acp_auth_done);
if (!BTM_SEC_IS_SM4(p_dev_rec->sm4))
@@ -5706,7 +5997,11 @@ BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[])
** Returns Pointer to the record or NULL
**
*******************************************************************************/
-static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm)
+static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ )
{
tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0];
int i;
@@ -5732,7 +6027,11 @@ static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UI
/* otherwise, just find the first record with the specified PSM */
for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++)
{
- if ( (p_serv_rec->security_flags & BTM_SEC_IN_USE) && (p_serv_rec->psm == psm) )
+ if ( (p_serv_rec->security_flags & BTM_SEC_IN_USE) &&
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ (p_serv_rec->transport == transport) &&
+#endif
+ (p_serv_rec->psm == psm) )
return(p_serv_rec);
}
return(NULL);
@@ -5749,7 +6048,11 @@ static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UI
** Returns Pointer to the record or NULL
**
*******************************************************************************/
-static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur)
+static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ )
{
tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0];
int i;
@@ -5757,6 +6060,9 @@ static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur)
for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++)
{
if ((p_serv_rec->security_flags & BTM_SEC_IN_USE)
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ && (p_serv_rec->transport == transport)
+#endif
&& (p_serv_rec->psm == p_cur->psm) )
{
if (p_cur != p_serv_rec)
diff --git a/stack/btu/btu_task.c b/stack/btu/btu_task.c
index e2b899efd..40132ac87 100644
--- a/stack/btu/btu_task.c
+++ b/stack/btu/btu_task.c
@@ -324,6 +324,11 @@ void btu_task_shut_down(UNUSED_ATTR void *context) {
bta_sys_free();
btu_free_core();
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ l2c_cleanup();
+#endif
+
}
/*******************************************************************************
diff --git a/stack/gatt/gatt_main.c b/stack/gatt/gatt_main.c
index d7811364d..168859c72 100644
--- a/stack/gatt/gatt_main.c
+++ b/stack/gatt/gatt_main.c
@@ -73,6 +73,11 @@ static const tL2CAP_APPL_INFO dyn_info =
gatt_l2cif_data_ind_cback,
gatt_l2cif_congest_cback,
NULL
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
} ;
#if GATT_DYNAMIC_MEMORY == FALSE
diff --git a/stack/hid/hidh_conn.c b/stack/hid/hidh_conn.c
index 9ec64591b..3e9a5c423 100644
--- a/stack/hid/hidh_conn.c
+++ b/stack/hid/hidh_conn.c
@@ -74,6 +74,11 @@ static const tL2CAP_APPL_INFO hst_reg_info =
hidh_l2cif_data_ind,
hidh_l2cif_cong_ind,
NULL /* tL2CA_TX_COMPLETE_CB */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
};
/*******************************************************************************
diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h
index c92730f04..dc1b26cc1 100644
--- a/stack/include/btm_api.h
+++ b/stack/include/btm_api.h
@@ -3285,6 +3285,44 @@ extern void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_pai
*******************************************************************************/
extern void BTM_SetSecureConnectionsOnly (BOOLEAN secure_connections_only_mode);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function BTM_SetBleSecurityLevel
+**
+** Description Register LE service security level with Security Manager
+**
+** Parameters: is_originator - TRUE if originating the connection, FALSE if not
+** p_name - Name of the service relevant only if
+** authorization will show this name to user. ignored
+** if BTM_SEC_SERVICE_NAME_LEN is 0.
+** service_id - service ID for the service passed to authorization callback
+** sec_level - bit mask of the security features
+** psm - LE L2CAP PSM
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN BTM_SetBleSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id,
+ UINT16 sec_level, UINT16 psm);
+
+/*******************************************************************************
+**
+** Function BTM_SetBleEncKeySize
+**
+** Description Register the required LE encryption key size with Security Manager
+**
+** Parameters: p_name - Name of the service relevant only
+** enc_key_size- required LE encryption key size
+** le_psm - LE L2CAP PSM
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN BTM_SetBleEncKeySize (char *p_name, UINT8 enc_key_size, UINT16 le_psm);
+#endif
+
+
/*******************************************************************************
**
** Function BTM_SetSecurityLevel
diff --git a/stack/include/btm_ble_api.h b/stack/include/btm_ble_api.h
index 0a91d5f4a..008efe125 100644
--- a/stack/include/btm_ble_api.h
+++ b/stack/include/btm_ble_api.h
@@ -452,6 +452,20 @@ typedef struct
UINT8 flag;
UINT8 tx_power;
}tBTM_BLE_ADV_DATA;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+enum
+{
+ BTM_BLE_SUCCESS = 0,
+ BTM_BLE_NO_PSM = 2,
+ BTM_BLE_NO_RESOURCES = 4,
+ BTM_BLE_INSUFF_AUTHENTICATION = 5,
+ BTM_BLE_INSUFF_AUTHORIZATION = 6,
+ BTM_BLE_INSUFF_ENCR_KEY_SIZE = 7,
+ BTM_BLE_INSUFF_ENCRYPTION = 8
+};
+
+typedef UINT8 tBTM_BLE_STATUS;
+#endif /* LE_L2CAP_CFC_INCLUDED */
#ifndef BTM_BLE_MULTI_ADV_MAX
#define BTM_BLE_MULTI_ADV_MAX 16 /* controller returned adv_inst_max should be less
diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h
index 837dbc852..f2b648de7 100644
--- a/stack/include/l2c_api.h
+++ b/stack/include/l2c_api.h
@@ -38,7 +38,9 @@
** HCI type(1), len(2), handle(2), L2CAP len(2) and CID(2) => 9
*/
#define L2CAP_MIN_OFFSET 13 /* plus control(2), SDU length(2) */
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define L2CAP_LE_MIN_OFFSET 11 /* plus LE SDU length(2) */
+#endif
/* Minimum offset for broadcast needs another two bytes for the PSM */
#define L2CAP_BCST_MIN_OFFSET 11
@@ -125,6 +127,10 @@ typedef UINT8 tL2CAP_CHNL_DATA_RATE;
#define L2C_INVALID_PSM(psm) (((psm) & 0x0101) != 0x0001)
#define L2C_IS_VALID_PSM(psm) (((psm) & 0x0101) == 0x0001)
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define L2C_LE_INVALID_PSM(psm) !((psm >= 0x0001) && (psm <= 0x00FF))
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/*****************************************************************************
** Type Definitions
*****************************************************************************/
@@ -166,6 +172,20 @@ typedef struct
UINT16 flags; /* bit 0: 0-no continuation, 1-continuation */
} tL2CAP_CFG_INFO;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/* Define a structure to hold the LE COC connection parameters.
+ */
+typedef struct
+{
+ UINT16 result; /* Only used in confirm messages */
+ UINT16 credits; /* used to send the outstanding credits */
+ UINT16 le_psm;
+ UINT16 le_mps;
+ UINT16 le_mtu;
+ UINT16 init_credits; /* initial credits */
+} tL2CAP_LE_CONN_INFO;
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/* L2CAP channel configured field bitmap */
#define L2CAP_CH_CFG_MASK_MTU 0x0001
#define L2CAP_CH_CFG_MASK_QOS 0x0002
@@ -282,6 +302,22 @@ typedef void (tL2CA_NOCP_CB) (BD_ADDR);
*/
typedef void (tL2CA_TX_COMPLETE_CB) (UINT16, UINT16);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/* LE credit based Connection indication callback prototype. Parameters are
+** BD Address of remote
+** Local CID assigned to the connection
+** Identifier that the remote sent
+** LE connection info
+*/
+typedef void (tL2CA_LE_CONNECT_IND_CB) (BD_ADDR, UINT16, UINT8, tL2CAP_LE_CONN_INFO *);
+
+/* LE credit based Connection confirmation callback prototype. Parameters are
+** Local CID
+** LE connection info
+*/
+typedef void (tL2CA_LE_CONNECT_CFM_CB) (UINT16, tL2CAP_LE_CONN_INFO *);
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/* Define the structure that applications use to register with
** L2CAP. This structure includes callback functions. All functions
** MUST be provided, with the exception of the "connect pending"
@@ -300,7 +336,10 @@ typedef struct
tL2CA_DATA_IND_CB *pL2CA_DataInd_Cb;
tL2CA_CONGESTION_STATUS_CB *pL2CA_CongestionStatus_Cb;
tL2CA_TX_COMPLETE_CB *pL2CA_TxComplete_Cb;
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ tL2CA_LE_CONNECT_IND_CB *pL2CA_LE_ConnectInd_Cb;
+ tL2CA_LE_CONNECT_CFM_CB *pL2CA_LE_ConnectCfm_Cb;
+#endif /* LE_L2CAP_CFC_INCLUDED */
} tL2CAP_APPL_INFO;
/* Define the structure that applications use to create or accept
@@ -1134,6 +1173,68 @@ extern UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr);
*******************************************************************************/
extern UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function L2CA_LE_Register
+**
+** Description Other layers call this function to register for LE L2CAP
+** services.
+**
+** Returns PSM to use or zero if error. Typically, the PSM returned
+** is the same as was passed in.
+**
+*******************************************************************************/
+UINT16 L2CA_LE_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info);
+
+/*******************************************************************************
+**
+** Function L2CA_LE_Deregister
+**
+** Description Other layers call this function to deregister for LE L2CAP
+** services.
+**
+** Returns void
+**
+*******************************************************************************/
+extern void L2CA_LE_Deregister (UINT16 psm);
+
+/*******************************************************************************
+**
+** Function L2CA_LE_CreditBasedConn_Req
+**
+** Description This function used to send LE credit based connection request
+**
+** Returns local cid
+**
+*******************************************************************************/
+extern UINT16 L2CA_LE_CreditBasedConn_Req (BD_ADDR rem_bda,
+ tL2CAP_LE_CONN_INFO *conn_info);
+
+/*******************************************************************************
+**
+** Function L2CA_LE_CreditBasedConn_Rsp
+**
+** Description This function used to send LE credit based connection response
+**
+** Returns
+**
+*******************************************************************************/
+extern BOOLEAN L2CA_LE_CreditBasedConn_Rsp (BD_ADDR p_bd_addr, UINT8 identifier,
+ UINT16 lcid, tL2CAP_LE_CONN_INFO *conn_info);
+
+/*******************************************************************************
+**
+** Function L2CA_LE_SetFlowControlCredits
+**
+** Description This function sets the credits for LE incase credits was
+** not set during the LE connection establishment.
+**
+** Returns
+**
+*******************************************************************************/
+BOOLEAN L2CA_LE_SetFlowControlCredits (UINT16 cid, UINT16 credits);
+#endif /* LE_L2CAP_CFC_INCLUDED */
#endif /* (BLE_INCLUDED == TRUE) */
#ifdef __cplusplus
diff --git a/stack/include/l2cdefs.h b/stack/include/l2cdefs.h
index a6e25eb9e..e24ae2720 100644
--- a/stack/include/l2cdefs.h
+++ b/stack/include/l2cdefs.h
@@ -41,6 +41,14 @@
#define L2CAP_CMD_BLE_UPDATE_REQ 0x12
#define L2CAP_CMD_BLE_UPDATE_RSP 0x13
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/* LE L2CAP Signaling Commands */
+#define LE_L2CAP_CMD_CB_CONN_REQ 0x14 /* LE credit based connect request */
+#define LE_L2CAP_CMD_CB_CONN_RSP 0x15 /* LE credit based connect response */
+#define LE_L2CAP_CMD_CB_FLOW_CTRL 0x16 /* LE credit based flow control */
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
+
/* Define some packet and header lengths
*/
@@ -69,6 +77,12 @@
#define L2CAP_CMD_BLE_UPD_REQ_LEN 8 /* Min and max interval, latency, tout */
#define L2CAP_CMD_BLE_UPD_RSP_LEN 2 /* Result */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define LE_L2CAP_CMD_CB_CONN_REQ_LEN 10 /* LE_PSM, Source CID, MTU, MPS, Initial Credits */
+#define LE_L2CAP_CMD_CB_CONN_RSP_LEN 10 /* Dest CID, MTU, MPS, Initial Credits, Result */
+#define LE_L2CAP_CMD_CB_FLOW_CTRL_LEN 4 /* CID, Credits */
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/* Define the packet boundary flags
*/
@@ -97,6 +111,16 @@
#define L2CAP_CONN_NO_LINK 255 /* Add a couple of our own for internal use */
#define L2CAP_CONN_CANCEL 256 /* L2CAP connection cancelled */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define L2CAP_LE_CONN_OK 0x0000 /* Connection successful */
+#define L2CAP_LE_CONN_NO_PSM 0x0002 /* Connection refused - LE_PSM not supported */
+#define L2CAP_LE_CONN_NO_RESOURCES 0x0004 /* Connection refused - no resources available */
+#define L2CAP_LE_CONN_INSUFFI_AUTHENTICATION 0x0005 /* Connection refused - insufficient authentication */
+#define L2CAP_LE_CONN_INSUFFI_AUTHORIZATION 0x0006 /* Connection refused - insufficient authorization */
+#define L2CAP_LE_CONN_INSUFFI_ENCRYPT_KEY_SIZE 0x0007 /* Connection refused - insufficient encryption key size */
+#define L2CAP_LE_CONN_INSUFFI_ENCRYPT 0x0008 /* Connection refused - insufficient encryption */
+/**** 0x0001, 0x0003, 0x0009-0xFFFF are Reserved */
+#endif /* LE_L2CAP_CFC_INCLUDED */
/* Define L2CAP Move Channel Response result codes
*/
@@ -213,6 +237,15 @@
#define L2CAP_DEFAULT_DELAY 0xFFFFFFFF
#define L2CAP_DEFAULT_FCS L2CAP_CFG_FCS_USE
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define L2CAP_LE_DEFAULT_MAX_CREDITS (10) /* L2CAP LE COC default maximum credits */
+#define L2CAP_LE_MIN_MTU (23) /* L2CAP LE COC minimum mtu size */
+#define L2CAP_LE_DEFAULT_MPS (230) /* L2CAP LE COC default mps size */
+#define L2CAP_LE_FLOWCTL_MIN_CREDITS (0x0001)
+#define L2CAP_LE_FLOWCTL_MAX_CREDITS (0xffff)
+#define L2CAP_LE_FLOWCTL_MAX_MPS (0xffff - 2)
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/* Define the L2CAP disconnect result codes
*/
diff --git a/stack/l2cap/l2c_api.c b/stack/l2cap/l2c_api.c
index e01a261e5..a7ffc4ed7 100644
--- a/stack/l2cap/l2c_api.c
+++ b/stack/l2cap/l2c_api.c
@@ -89,7 +89,11 @@ UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
{
for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2)
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
+#endif
break;
}
@@ -97,9 +101,17 @@ UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
}
/* If registration block already there, just overwrite it */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
+#endif
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_allocate_rcb (vpsm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL)
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB available, PSM: 0x%04x vPSM: 0x%04x", psm, vpsm);
return (0);
@@ -133,7 +145,11 @@ void L2CA_Deregister (UINT16 psm)
L2CAP_TRACE_API ("L2CAP - L2CA_Deregister() called for PSM: 0x%04x", psm);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) != NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) != NULL)
+#endif
{
p_lcb = &l2cb.lcb_pool[0];
for (ii = 0; ii < MAX_L2CAP_LINKS; ii++, p_lcb++)
@@ -195,7 +211,11 @@ UINT16 L2CA_AllocatePSM(void)
continue;
/* make sure the newlly allocated psm is not used right now */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
done = TRUE;
}
l2cb.dyn_psm = psm;
@@ -256,7 +276,11 @@ UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_e
return (0);
}
/* Fail if the PSM is not registered */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm);
return (0);
@@ -598,6 +622,15 @@ BOOLEAN L2CA_DisconnectReq (UINT16 cid)
return (FALSE);
}
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) &&
+ (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
return (TRUE);
@@ -627,6 +660,14 @@ BOOLEAN L2CA_DisconnectRsp (UINT16 cid)
return (FALSE);
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) &&
+ (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL);
return (TRUE);
@@ -954,7 +995,11 @@ UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr)
}
/* Fail if the PSM is not registered */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm);
return (0);
diff --git a/stack/l2cap/l2c_ble.c b/stack/l2cap/l2c_ble.c
index a5cfb4d78..db9eae6b4 100644
--- a/stack/l2cap/l2c_ble.c
+++ b/stack/l2cap/l2c_ble.c
@@ -225,6 +225,357 @@ UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport)
return reason;
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function L2CA_LE_Register
+**
+** Description Other layers call this function to register for LE L2CAP
+** services.
+**
+** Returns PSM to use or zero if error. Typically, the PSM returned
+** is the same as was passed in.
+**
+*******************************************************************************/
+UINT16 L2CA_LE_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
+{
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API ("LE-L2CAP: %s called for PSM: 0x%04x", __FUNCTION__, psm);
+
+ /* Verify that the required callback info has been filled */
+
+ if ((!p_cb_info->pL2CA_LE_ConnectInd_Cb)
+ || (!p_cb_info->pL2CA_LE_ConnectCfm_Cb)
+ || (!p_cb_info->pL2CA_DataInd_Cb)
+ || (!p_cb_info->pL2CA_DisconnectInd_Cb)
+ || (!p_cb_info->pL2CA_DisconnectCfm_Cb))
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: no cb registering PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Verify PSM is valid */
+ if (L2C_LE_INVALID_PSM(psm))
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: invalid PSM value, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* If registration block already there, just overwrite it */
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_LE)) == NULL)
+ {
+ if ((p_rcb = l2cu_allocate_rcb (psm, BT_TRANSPORT_LE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no RCB available, PSM: 0x%04x ", psm);
+ return (0);
+ }
+ }
+
+ p_rcb->api = *p_cb_info;
+ p_rcb->real_psm = psm;
+
+ return (psm);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_LE_Deregister
+**
+** Description Other layers call this function to de-register for L2CAP
+** services.
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_LE_Deregister (UINT16 psm)
+{
+ tL2C_RCB *p_rcb;
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+ int ii;
+
+ L2CAP_TRACE_API ("LE-L2CAP: L2CA_LE_Deregister() called for PSM: 0x%04x", psm);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_LE)) != NULL)
+ {
+ p_lcb = &l2cb.lcb_pool[0];
+ for (ii = 0; ii < MAX_L2CAP_LINKS; ii++, p_lcb++)
+ {
+ if (p_lcb->in_use)
+ {
+ if (((p_ccb = p_lcb->ccb_queue.p_first_ccb) == NULL)
+ || (p_lcb->link_state == LST_DISCONNECTING))
+ continue;
+
+ if ((p_ccb->in_use) &&
+ ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) ||
+ (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP)))
+ continue;
+
+ if (p_ccb->p_rcb == p_rcb)
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ }
+ }
+ l2cu_release_rcb (p_rcb);
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: - PSM: 0x%04x not found for deregistration", psm);
+ }
+}
+
+/*******************************************************************************
+**
+** Function LE_L2CA_CreditBasedConn_Req
+**
+** Description This function used to send the LE CB connection request
+**
+** Returns local cid
+**
+*******************************************************************************/
+UINT16 L2CA_LE_CreditBasedConn_Req (BD_ADDR rem_bda, tL2CAP_LE_CONN_INFO *conn_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ UINT16 max_mtu;
+
+ /* Fail if we have not established communications with the controller */
+ if (!conn_info || !BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Device not ready/No conn info");
+ return (0);
+ }
+ /* Fail if the PSM is not registered */
+ if ((p_rcb = l2cu_find_rcb_by_psm (conn_info->le_psm, BT_TRANSPORT_LE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no RCB for PSM: 0x%04x", conn_info->le_psm);
+ return (0);
+ }
+
+ max_mtu = (GKI_get_pool_bufsize (HCI_LE_ACL_POOL_ID) -
+ L2CAP_LE_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET);
+
+ /* mtu check */
+ if((conn_info->le_mtu < L2CAP_LE_MIN_MTU) ||
+ (conn_info->le_mtu > max_mtu))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MTU size");
+ L2CAP_TRACE_WARNING ("LE-L2CAP: mtu should be in the range 23 to %d", max_mtu);
+ return (0);
+ }
+
+ /* mps check */
+ if((conn_info->le_mps < L2CAP_LE_MIN_MTU) ||
+ (conn_info->le_mps > conn_info->le_mtu) ||
+ (conn_info->le_mps > L2CAP_LE_FLOWCTL_MAX_MPS))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MPS size");
+ L2CAP_TRACE_WARNING ("LE-L2CAP: mps should be in the range 23 to %d", conn_info->le_mtu);
+ return (0);
+ }
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ if ( ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, BT_TRANSPORT_LE)) == NULL)
+ || (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) )
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: conn not started for PSM: 0x%04x p_lcb: 0x%08x",
+ conn_info->le_psm, p_lcb);
+ return (0);
+ }
+ }
+
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no CCB for PSM: 0x%04x", conn_info->le_psm);
+ return (0);
+ }
+
+ /* make the channel as LE COC channel */
+ p_ccb->is_le_coc = TRUE;
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ if (conn_info)
+ {
+ p_ccb->le_loc_conn_info.le_psm = conn_info->le_psm;
+ p_ccb->le_loc_conn_info.le_mtu = conn_info->le_mtu;
+ p_ccb->le_loc_conn_info.le_mps = conn_info->le_mps;
+ p_ccb->le_loc_conn_info.init_credits = conn_info->init_credits;
+ p_ccb->le_loc_conn_info.credits = conn_info->init_credits;
+ }
+ /* If link is up, start the L2CAP connection */
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_LE_CONNECT_REQ, NULL);
+ }
+ else if (p_lcb->link_state == LST_DISCONNECTING)
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: link disconnecting: RETRY LATER");
+
+ /* Save ccb to pending list so that it can restarted */
+ p_lcb->p_pending_ccb = p_ccb;
+ }
+
+ L2CAP_TRACE_API ("LE-L2CAP: (psm: 0x%04x) returned CID: 0x%04x", conn_info->le_psm,
+ p_ccb->local_cid);
+
+ /* Return the local CID as our handle */
+ return (p_ccb->local_cid);
+
+}
+/*******************************************************************************
+**
+** Function L2CA_LE_CreditBasedConn_Rsp
+**
+** Description This function enables the LE credit based connection response
+**
+** Returns
+**
+*******************************************************************************/
+BOOLEAN L2CA_LE_CreditBasedConn_Rsp (BD_ADDR p_bd_addr, UINT8 identifier,
+ UINT16 lcid, tL2CAP_LE_CONN_INFO *conn_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ UINT16 max_mtu;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP: %s CID: 0x%04x Result: %d BDA: %08x%04x ",
+ __FUNCTION__, lcid, conn_info->result,
+ (p_bd_addr[0]<<24)+(p_bd_addr[1]<<16)+(p_bd_addr[2]<<8)+p_bd_addr[3],
+ (p_bd_addr[4]<<8)+p_bd_addr[5]);
+
+ if(conn_info == NULL)
+ {
+ return (FALSE);
+ }
+
+ /* First, find the link control block */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_LE)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no LCB for L2CA_LE_CreditBasedConn_Rsp");
+ return (FALSE);
+ }
+
+ /* Now, find the channel control block */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no CCB for L2CA_LE_CreditBasedConn_Rsp");
+ return (FALSE);
+ }
+
+ max_mtu = (GKI_get_pool_bufsize (HCI_LE_ACL_POOL_ID) -
+ L2CAP_LE_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET);
+
+ /* mtu check */
+ if((conn_info->le_mtu < L2CAP_LE_MIN_MTU) ||
+ (conn_info->le_mtu > max_mtu))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MTU size");
+ L2CAP_TRACE_WARNING ("LE-L2CAP: mtu should be in the range 23 to %d", max_mtu);
+ return (0);
+ }
+
+ /* mps check */
+ if((conn_info->le_mps < L2CAP_LE_MIN_MTU) ||
+ (conn_info->le_mps > conn_info->le_mtu) ||
+ (conn_info->le_mps > L2CAP_LE_FLOWCTL_MAX_MPS))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MPS size");
+ L2CAP_TRACE_WARNING ("LE-L2CAP: mps should be in the range 23 to %d",
+ conn_info->le_mtu);
+ return (0);
+ }
+
+ /* The IDs must match */
+ if (p_ccb->remote_id != identifier)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: identifier is not matched p_ccb->remote_id: %d"\
+ "identifier: %d", p_ccb->remote_id, identifier);
+ return (FALSE);
+ }
+
+ if (conn_info)
+ {
+ p_ccb->le_loc_conn_info.le_psm = conn_info->le_psm;
+ p_ccb->le_loc_conn_info.le_mtu = conn_info->le_mtu;
+ p_ccb->le_loc_conn_info.le_mps = conn_info->le_mps;
+ p_ccb->le_loc_conn_info.init_credits = conn_info->init_credits;
+ p_ccb->le_loc_conn_info.credits = conn_info->init_credits;
+ p_ccb->le_loc_conn_info.result = conn_info->result;
+ }
+
+ if (conn_info->result == L2CAP_LE_CONN_OK)
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_LE_CONNECT_RSP, NULL);
+ else
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_LE_CONNECT_RSP_NEG, NULL);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_LE_SetFlowControlCredits
+**
+** Description This function sets the credits for LE incase credits did
+** not set during the LE connection establishment.
+**
+** Returns
+**
+*******************************************************************************/
+BOOLEAN L2CA_LE_SetFlowControlCredits (UINT16 cid, UINT16 credits)
+{
+ tL2C_CCB *p_ccb;
+ L2CAP_TRACE_WARNING ("LE-L2CAP: %s credits: %d CID: 0x%04x", __FUNCTION__,
+ credits, cid);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no CCB found");
+ return (FALSE);
+ }
+
+ if ((BT_TRANSPORT_LE != l2cu_get_chnl_transport(p_ccb)) ||
+ (p_ccb->is_le_coc == FALSE))
+ {
+ L2CAP_TRACE_EVENT ("LE-L2CAP: not a LE COC channel");
+ return (FALSE);
+ }
+
+ /* check for initial credits set to zero */
+ if( (!p_ccb->le_loc_conn_info.init_credits) &&
+ (credits >= L2CAP_LE_FLOWCTL_MIN_CREDITS) &&
+ (p_ccb->chnl_state == CST_OPEN) )
+ {
+ p_ccb->le_loc_conn_info.init_credits = credits;
+ l2c_le_send_credits (p_ccb);
+ return TRUE;
+ }
+ else if (p_ccb->le_loc_conn_info.init_credits)
+ {
+ if (p_ccb->le_loc_conn_info.init_credits > p_ccb->le_loc_conn_info.credits)
+ {
+ l2cu_send_peer_le_credit_based_flow_ctrl(p_ccb,
+ p_ccb->le_loc_conn_info.init_credits - p_ccb->le_loc_conn_info.credits);
+ return (TRUE);
+ }
+ else
+ {
+ return (FALSE);
+ }
+ }
+ else
+ {
+ return (FALSE);
+ }
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
/*******************************************************************************
**
@@ -239,6 +590,9 @@ void l2cble_notify_le_connection (BD_ADDR bda)
{
tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE);
tACL_CONN *p_acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE) ;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ tL2C_CCB *p_ccb;
+#endif
if (p_lcb != NULL && p_acl != NULL && p_lcb->link_state != LST_CONNECTED)
{
@@ -246,6 +600,14 @@ void l2cble_notify_le_connection (BD_ADDR bda)
btm_establish_continue(p_acl);
/* update l2cap link status and send callback */
p_lcb->link_state = LST_CONNECTED;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: %s Notifying connection completed", __FUNCTION__);
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, NULL);
+ }
+#endif
+
l2cu_process_fixed_chnl_resp (p_lcb);
}
}
@@ -379,6 +741,9 @@ void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE typ
UNUSED(conn_interval);
UNUSED(conn_latency);
UNUSED(conn_timeout);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ tL2C_CCB *p_ccb;
+#endif
/* See if we have a link control block for the remote device */
p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE);
@@ -431,6 +796,13 @@ 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;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: %s Notifying connection completed", __FUNCTION__);
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, NULL);
+ }
+#endif
l2cu_process_fixed_chnl_resp (p_lcb);
}
@@ -599,6 +971,16 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
UINT16 cmd_len;
UINT16 min_interval, max_interval, latency, timeout;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ UINT16 lcid, rcid, credits, max_credits;
+ tL2C_LE_CB_CONN_REQ le_cb_conn_req;
+ tL2C_LE_CB_CONN_RSP le_cb_conn_rsp;
+ tL2C_LE_CB_FLOW_CTRL le_cb_flow_ctrl;
+ tL2CAP_LE_CONN_INFO le_con_info;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
p_pkt_end = p + pkt_len;
STREAM_TO_UINT8 (cmd_code, p);
@@ -665,7 +1047,185 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
case L2CAP_CMD_BLE_UPDATE_RSP:
p += 2;
break;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ case LE_L2CAP_CMD_CB_CONN_REQ:
+ STREAM_TO_UINT16 (le_cb_conn_req.le_psm, p);
+ STREAM_TO_UINT16 (le_cb_conn_req.scid, p);
+ STREAM_TO_UINT16 (le_cb_conn_req.mtu, p);
+ STREAM_TO_UINT16 (le_cb_conn_req.mps, p);
+ STREAM_TO_UINT16 (le_cb_conn_req.init_credits, p);
+
+ if(p_lcb->link_state != LST_CONNECTED)
+ {
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_lcb->remote_bd_addr);
+ if(p_dev_rec)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Creating thr ACL for LE COC channel");
+ btm_acl_created (p_lcb->remote_bd_addr, NULL, p_dev_rec->sec_bd_name,
+ p_lcb->handle,p_lcb->link_role, BT_TRANSPORT_LE);
+ l2cble_notify_le_connection(p_lcb->remote_bd_addr);
+ }
+ }
+ /* mtu/mps check */
+ if((le_cb_conn_req.mtu < L2CAP_LE_MIN_MTU) ||
+ (le_cb_conn_req.mps > le_cb_conn_req.mtu) ||
+ (le_cb_conn_req.mps > L2CAP_LE_FLOWCTL_MAX_MPS))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MTU/MPS size");
+ l2cu_le_reject_connection (p_lcb, id, L2CAP_LE_CONN_NO_RESOURCES);
+ break;
+ }
+ if ((p_rcb = l2cu_find_rcb_by_psm (le_cb_conn_req.le_psm, BT_TRANSPORT_LE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: rcvd le credit based conn req for unknown PSM: %d",
+ le_cb_conn_req.le_psm);
+ l2cu_le_reject_connection (p_lcb, id, L2CAP_LE_CONN_NO_PSM);
+ break;
+ }
+ else
+ {
+ if (!p_rcb->api.pL2CA_LE_ConnectInd_Cb)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: rcvd conn req for outgoing-only connection PSM: %d",
+ le_cb_conn_req.le_psm);
+ l2cu_le_reject_connection (p_lcb, id, L2CAP_LE_CONN_NO_PSM);
+ break;
+ }
+ }
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: unable to allocate CCB");
+ l2cu_le_reject_connection (p_lcb, id, L2CAP_LE_CONN_NO_RESOURCES);
+ break;
+ }
+ /* make the channel as LE COC channel */
+ p_ccb->is_le_coc = TRUE;
+ p_ccb->remote_id = id;
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->remote_cid = le_cb_conn_req.scid;
+
+ /* Save all connection params */
+ p_ccb->le_rmt_conn_info.le_psm = le_cb_conn_req.le_psm;
+ p_ccb->le_rmt_conn_info.le_mtu = le_cb_conn_req.mtu;
+ p_ccb->le_rmt_conn_info.le_mps = le_cb_conn_req.mps;
+ p_ccb->le_rmt_conn_info.init_credits = le_cb_conn_req.init_credits;
+ p_ccb->le_rmt_conn_info.credits = le_cb_conn_req.init_credits;
+
+ l2c_le_csm_execute(p_ccb, L2CEVT_L2CAP_LE_CONNECT_REQ, NULL);
+ break;
+
+ case LE_L2CAP_CMD_CB_CONN_RSP: /* Got Credit Based L2CAP Connect Rsp from peer device */
+ STREAM_TO_UINT16 (le_cb_conn_rsp.dcid, p);
+ STREAM_TO_UINT16 (le_cb_conn_rsp.mtu, p);
+ STREAM_TO_UINT16 (le_cb_conn_rsp.mps, p);
+ STREAM_TO_UINT16 (le_cb_conn_rsp.init_credits, p);
+ STREAM_TO_UINT16 (le_cb_conn_rsp.result, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_local_id (p_lcb, id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no CCB for conn rsp, CID: %d ", le_cb_conn_rsp.dcid);
+ break;
+ }
+
+ p_ccb->remote_cid = le_cb_conn_rsp.dcid;
+
+ if (le_cb_conn_rsp.result == L2CAP_LE_CONN_OK)
+ {
+ p_ccb->le_rmt_conn_info.le_mtu = le_cb_conn_rsp.mtu;
+ p_ccb->le_rmt_conn_info.le_mps = le_cb_conn_rsp.mps;
+ p_ccb->le_rmt_conn_info.init_credits = le_cb_conn_rsp.init_credits;
+ p_ccb->le_rmt_conn_info.credits = le_cb_conn_rsp.init_credits;
+ p_ccb->le_rmt_conn_info.result = le_cb_conn_rsp.result;
+
+ /* save psm from local chnl info */
+ p_ccb->le_rmt_conn_info.le_psm = p_ccb->le_loc_conn_info.le_psm;
+
+ l2c_le_csm_execute(p_ccb, L2CEVT_L2CAP_LE_CONNECT_RSP, NULL);
+
+ if((p_ccb->le_rmt_conn_info.le_mtu < L2CAP_LE_MIN_MTU) ||
+ (p_ccb->le_rmt_conn_info.le_mps > p_ccb->le_rmt_conn_info.le_mtu) ||
+ (p_ccb->le_rmt_conn_info.le_mps > L2CAP_LE_FLOWCTL_MAX_MPS))
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Invalid MTU/MPS size, disconnect channel");
+ l2c_le_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ break;
+ }
+ }
+ else
+ {
+ p_ccb->le_rmt_conn_info.result = le_cb_conn_rsp.result;
+ l2c_le_csm_execute(p_ccb, L2CEVT_L2CAP_LE_CONNECT_RSP_NEG, &le_cb_conn_rsp);
+ }
+
+ break;
+
+ case LE_L2CAP_CMD_CB_FLOW_CTRL:
+ STREAM_TO_UINT16 (rcid, p);
+ STREAM_TO_UINT16 (credits, p);
+ L2CAP_TRACE_DEBUG("LE-L2CAP: rcid 0x%4.4x credits 0x%4.4x", rcid, credits);
+
+ if ((p_ccb = l2cu_find_ccb_by_remote_cid (p_lcb, rcid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: no CCB for LE_L2CAP_CMD_CB_FLOW_CTRL, CID: %d ",
+ le_cb_conn_rsp.dcid);
+ break;
+ }
+ max_credits = L2CAP_LE_FLOWCTL_MAX_CREDITS - p_ccb->le_rmt_conn_info.credits;
+
+ if(!credits)
+ {
+ /* ignore the packet if credits are 0 */
+ break;
+ }
+ if (credits > max_credits)
+ {
+ L2CAP_TRACE_ERROR("LE-L2CAP: LE credits overflow");
+ l2c_le_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ break;
+ }
+ p_ccb->le_rmt_conn_info.credits += credits;
+
+ /* Clearing the channel congestion if it is already congested */
+ if (p_ccb->cong_sent)
+ {
+ l2cu_check_channel_congestion (p_ccb);
+ }
+
+ if(p_ccb->le_rmt_conn_info.credits)
+ {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ break;
+ case L2CAP_CMD_DISC_REQ:
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (rcid, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ {
+ if (p_ccb->remote_cid == rcid)
+ {
+ p_ccb->remote_id = id;
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &le_con_info);
+ }
+ }
+ else
+ l2cu_send_peer_disc_rsp (p_lcb, id, lcid, rcid);
+
+ break;
+ case L2CAP_CMD_DISC_RSP:
+ STREAM_TO_UINT16 (rcid, p);
+ STREAM_TO_UINT16 (lcid, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ {
+ if ((p_ccb->remote_cid == rcid) && (p_ccb->local_id == id))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &le_con_info);
+ }
+ }
+ break;
+#endif /* LE_L2CAP_CFC_INCLUDED */
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);
diff --git a/stack/l2cap/l2c_csm.c b/stack/l2cap/l2c_csm.c
index 7a77f36f1..49b4286c8 100644
--- a/stack/l2cap/l2c_csm.c
+++ b/stack/l2cap/l2c_csm.c
@@ -1284,7 +1284,14 @@ static char *l2c_csm_get_event_name (UINT16 event)
void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf)
{
UINT8 *p;
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if( (BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) &&
+ (p_ccb->is_le_coc == TRUE))
+ {
+ p_buf->event = 0;
+ }
+ else
+#endif
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
p_buf->event = 0;
diff --git a/stack/l2cap/l2c_fcr.c b/stack/l2cap/l2c_fcr.c
index 3df5cba7e..3290cd796 100644
--- a/stack/l2cap/l2c_fcr.c
+++ b/stack/l2cap/l2c_fcr.c
@@ -38,6 +38,10 @@
#include "btm_int.h"
#include "btu.h"
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#include <pthread.h>
+#endif
+
/* Flag passed to retransmit_i_frames() when all packets should be retransmitted */
#define L2C_FCR_RETX_ALL_PKTS 0xFF
@@ -93,6 +97,9 @@ static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq);
static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission);
static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf);
static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+static BOOLEAN do_le_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+#endif /* LE_L2CAP_CFC_INCLUDED */
#if (L2CAP_ERTM_STATS == TRUE)
static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked);
@@ -607,6 +614,36 @@ void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit)
}
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function l2c_le_send_credits
+**
+** Description This function defines the le flow control credit based
+** mechanishm.
+** Returns -
+**
+*******************************************************************************/
+void l2c_le_send_credits (tL2C_CCB *p_ccb)
+{
+ UINT16 return_credits;
+
+ /* send credits to the remote only after the amount of credits falls
+ * below half of the initial amount.
+ */
+ if (p_ccb->le_loc_conn_info.credits >= (p_ccb->le_loc_conn_info.init_credits + 1) / 2)
+ return;
+
+ return_credits = p_ccb->le_loc_conn_info.init_credits - p_ccb->le_loc_conn_info.credits;
+
+ L2CAP_TRACE_WARNING("LE-L2CAP: return_credits = %d", return_credits);
+
+ p_ccb->le_loc_conn_info.credits += return_credits;
+
+ l2cu_send_peer_le_credit_based_flow_ctrl(p_ccb, return_credits);
+
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
/*******************************************************************************
**
@@ -1358,6 +1395,142 @@ static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf)
}
}
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function l2c_le_proc_pdu
+**
+** Description This function processes le frames in credit based flow ctrl
+** mode
+** Returns TRUE if all OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_le_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ UINT8 *p;
+ UINT8 i =0;
+
+ if (!p_ccb->le_loc_conn_info.credits)
+ {
+ L2CAP_TRACE_ERROR("LE-L2CAP: No credits to receive LE L2CAP data");
+ l2cu_send_peer_disc_req (p_ccb);
+ return FALSE;
+ }
+
+ L2CAP_TRACE_DEBUG("LE-L2CAP: l2c_le_proc_pdu: le_mps %d p_buf->len %d",
+ p_ccb->le_loc_conn_info.le_mps, p_buf->len);
+
+ if (p_ccb->le_loc_conn_info.le_mps < p_buf->len)
+ {
+ L2CAP_TRACE_ERROR("LE-L2CAP: Too big LE L2CAP PDU");
+ return FALSE;
+ }
+
+ p_ccb->le_loc_conn_info.credits--;
+ L2CAP_TRACE_DEBUG("LE-L2CAP: rx_credits %u -> %u", p_ccb->le_loc_conn_info.credits + 1,
+ p_ccb->le_loc_conn_info.credits);
+ l2c_le_send_credits (p_ccb);
+
+ if (!do_le_reassembly (p_ccb, p_buf))
+ {
+ /* Some sort of LE reassembly error, so flush the queue */
+ if (p_ccb->fcrb.p_rx_sdu != NULL)
+ {
+ GKI_freebuf (p_ccb->fcrb.p_rx_sdu);
+ p_ccb->fcrb.p_rx_sdu = NULL;
+ }
+ /* TODO do we need to disconnect */
+ }
+ return TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function do_le_reassembly
+**
+** Description Process LE frames and reassemble based on SDU length.
+**
+** Returns TRUE if all OK, else FALSE
+**
+*******************************************************************************/
+static BOOLEAN do_le_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ BOOLEAN packet_ok = TRUE;
+ BOOLEAN pkt_segmented = TRUE;
+ UINT8 *p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+
+ /* it's a first packet */
+ if (p_fcrb->p_rx_sdu == NULL)
+ {
+ /* Get the SDU length */
+ STREAM_TO_UINT16 (p_fcrb->rx_sdu_len, p);
+ p_buf->offset += 2;
+ p_buf->len -= 2;
+
+ if (p_fcrb->rx_sdu_len > p_ccb->le_loc_conn_info.le_mtu)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: SDU len: %u larger than MTU: %u",
+ p_fcrb->rx_sdu_len, p_ccb->le_loc_conn_info.le_mtu);
+ packet_ok = FALSE;
+ }
+ else if(p_fcrb->rx_sdu_len == p_buf->len)
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: SDU Not segmented ");
+ pkt_segmented = FALSE;
+ }
+ else if ((p_fcrb->p_rx_sdu = (BT_HDR *)GKI_getpoolbuf (HCI_LE_ACL_POOL_ID)) == NULL)
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: no buffer for SDU start user_rx_pool_id:%d",
+ p_ccb->ertm_info.user_rx_pool_id);
+ packet_ok = FALSE;
+ }
+ else
+ {
+ p_fcrb->p_rx_sdu->offset = L2CAP_PKT_OVERHEAD;
+ p_fcrb->p_rx_sdu->len = 0;
+ }
+ }
+
+ if ((packet_ok) && (pkt_segmented == TRUE))
+ {
+ if ((p_fcrb->p_rx_sdu->len + p_buf->len) > p_fcrb->rx_sdu_len)
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: SDU len exceeded Lengths: %u %u %u",
+ p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ }
+ else
+ {
+ memcpy (((UINT8 *) (p_fcrb->p_rx_sdu + 1)) + p_fcrb->p_rx_sdu->offset +
+ p_fcrb->p_rx_sdu->len, p, p_buf->len);
+
+ p_fcrb->p_rx_sdu->len += p_buf->len;
+
+ GKI_freebuf (p_buf);
+ p_buf = NULL;
+
+ if (p_fcrb->p_rx_sdu->len == p_fcrb->rx_sdu_len)
+ {
+ p_buf = p_fcrb->p_rx_sdu;
+ p_fcrb->p_rx_sdu = NULL;
+ }
+ }
+ }
+
+ if (packet_ok == FALSE)
+ {
+ GKI_freebuf (p_buf);
+ }
+ else if (p_buf != NULL)
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_buf);
+ }
+
+ return (packet_ok);
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
/*******************************************************************************
diff --git a/stack/l2cap/l2c_int.h b/stack/l2cap/l2c_int.h
index cbaca8251..5429e6d7a 100644
--- a/stack/l2cap/l2c_int.h
+++ b/stack/l2cap/l2c_int.h
@@ -32,6 +32,10 @@
#include "l2cdefs.h"
#include "list.h"
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#include <pthread.h>
+#endif
+
#define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */
/* Timeouts. Since L2CAP works off a 1-second list, all are in seconds.
@@ -137,6 +141,14 @@ typedef enum
#define L2CEVT_ACK_TIMEOUT 34 /* RR delay timeout */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#define L2CEVT_L2CAP_LE_CONNECT_REQ 35 /* Peer LE connection request */
+#define L2CEVT_L2CAP_LE_CONNECT_RSP 36 /* Peer LE connection response */
+#define L2CEVT_L2CAP_LE_CONNECT_RSP_NEG 37 /* Peer LE connection response(failed) */
+#define L2CEVT_L2CA_LE_CONNECT_REQ 38 /* Upper layer LE connect request */
+#define L2CEVT_L2CA_LE_CONNECT_RSP 39 /* Upper layer LE connect response */
+#define L2CEVT_L2CA_LE_CONNECT_RSP_NEG 40 /* Upper layer LE connect response(failed)*/
+#endif
/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two
successive ID values, '0' id only or both */
@@ -240,10 +252,40 @@ typedef struct
#if (L2CAP_UCD_INCLUDED == TRUE)
tL2C_UCD_REG ucd;
#endif
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ tBT_TRANSPORT transport;
+#endif
tL2CAP_APPL_INFO api;
} tL2C_RCB;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+
+/* Define LE Credit Based Connect Req */
+typedef struct l2cap_le_cb_conn_req {
+ UINT16 le_psm;
+ UINT16 scid;
+ UINT16 mtu;
+ UINT16 mps;
+ UINT16 init_credits;
+} tL2C_LE_CB_CONN_REQ;
+
+/* Define LE Credit Based Connect Rsp */
+typedef struct l2cap_le_cb_conn_rsp {
+ UINT16 dcid;
+ UINT16 mtu;
+ UINT16 mps;
+ UINT16 init_credits;
+ UINT16 result;
+} tL2C_LE_CB_CONN_RSP;
+
+/* Define LE Credit Based Flow Control */
+typedef struct l2cap_le_cb_flow_ctrl {
+ UINT16 cid;
+ UINT16 credits;
+} tL2C_LE_CB_FLOW_CTRL;
+
+extern pthread_mutex_t lock_mutex_le_credits;
+#endif /* LE_L2CAP_CFC_INCLUDED */
/* Define a channel control block (CCB). There may be many channel control blocks
** between the same two Bluetooth devices (i.e. on the same link).
@@ -314,6 +356,12 @@ typedef struct t_l2c_ccb
UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */
#endif
UINT16 tx_data_len;
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ BOOLEAN is_le_coc;
+ tL2CAP_LE_CONN_INFO le_loc_conn_info;
+ tL2CAP_LE_CONN_INFO le_rmt_conn_info;
+#endif /* LE_L2CAP_CFC_INCLUDED */
} tL2C_CCB;
/***********************************************************************
@@ -603,6 +651,20 @@ extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16
extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id);
#endif
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+extern void l2c_cleanup (void);
+extern tBT_TRANSPORT l2cu_get_chnl_transport (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_le_credit_based_conn_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_le_credit_based_conn_rsp (tL2C_CCB *p_ccb, UINT8 result);
+extern void l2cu_send_peer_le_credit_based_flow_ctrl (tL2C_CCB *p_ccb, UINT16 credits);
+extern tL2C_CCB *l2cu_find_ccb_by_local_id (tL2C_LCB *p_lcb, UINT8 id);
+extern void l2cu_le_reject_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result);
+extern void l2c_le_send_credits (tL2C_CCB *p_ccb);
+extern void l2c_le_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+extern BOOLEAN l2c_le_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+extern BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf);
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr);
extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb);
extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb);
@@ -641,8 +703,16 @@ extern BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data
extern void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
extern void l2cu_send_feature_req (tL2C_CCB *p_ccb);
-extern tL2C_RCB *l2cu_allocate_rcb (UINT16 psm);
-extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm);
+extern tL2C_RCB *l2cu_allocate_rcb (UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ );
+extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ );
extern void l2cu_release_rcb (tL2C_RCB *p_rcb);
extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
@@ -678,6 +748,10 @@ extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs);
extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles);
extern void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status);
extern void l2c_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT trasnport, void *p_ref_data, UINT8 status);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+extern void l2c_le_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT trasnport,
+ void *p_ref_data, UINT8 status);
+#endif
extern void l2c_link_segments_xmitted (BT_HDR *p_msg);
extern void l2c_pin_code_request (BD_ADDR bd_addr);
extern void l2c_link_adjust_chnl_allocation (void);
diff --git a/stack/l2cap/l2c_le_csm.c b/stack/l2cap/l2c_le_csm.c
new file mode 100644
index 000000000..2a21ee33c
--- /dev/null
+++ b/stack/l2cap/l2c_le_csm.c
@@ -0,0 +1,619 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains the LE L2CAP channel SM handlers implementation
+ *
+ ******************************************************************************/
+
+#include "bt_target.h"
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "btm_int.h"
+#include "btu.h"
+#include "hcimsgs.h"
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static void l2c_le_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_le_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data);
+static void l2c_csm_w4_l2cap_le_connect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data);
+static void l2c_csm_w4_l2ca_le_connect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data);
+static void l2c_le_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2cap_le_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data);
+static void l2c_csm_w4_l2ca_le_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data);
+
+/*******************************************************************************
+ **
+ ** Function dump_le_l2cap_conn_info
+ **
+ ** Description This function dumps the LE L2CAP connection info.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void dump_le_l2cap_conn_info(short type, tL2C_CCB *p_ccb)
+{
+ tL2CAP_LE_CONN_INFO *le_cb_conn_req;
+
+ if((BT_TRANSPORT_LE != l2cu_get_chnl_transport(p_ccb)) ||
+ (p_ccb->is_le_coc == FALSE))
+ {
+ L2CAP_TRACE_WARNING("LE-L2CAP: Error: Not a LE COC chnl\n");
+ return;
+ }
+
+ if(!type)
+ {
+ le_cb_conn_req = &p_ccb->le_loc_conn_info;
+ L2CAP_TRACE_WARNING("LE-L2CAP: Local Dev Connection Params\n");
+ }
+ else
+ {
+ le_cb_conn_req = &p_ccb->le_rmt_conn_info;
+ L2CAP_TRACE_WARNING("LE-L2CAP: Remote Dev Connection Params\n");
+ }
+ L2CAP_TRACE_WARNING("LE-L2CAP: LCID:0x%04x RCID:0x%04x psm:%d mtu:%d mps:%d" \
+ "credits:%d", p_ccb->local_cid, p_ccb->remote_cid, le_cb_conn_req->le_psm,
+ le_cb_conn_req->le_mtu, le_cb_conn_req->le_mps, le_cb_conn_req->init_credits);
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_le_csm_execute
+ **
+ ** Description This function executes the LE L2CAP state machine.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+void l2c_le_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ if ((BT_TRANSPORT_LE != l2cu_get_chnl_transport(p_ccb)) || !p_ccb->p_rcb)
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: SM ERROR %s CCB:%p evt:0x%04x p_rcb:%p",
+ __FUNCTION__, p_ccb, event, (p_ccb ? p_ccb->p_rcb: NULL));
+ return;
+ }
+
+ switch (p_ccb->chnl_state)
+ {
+ case CST_CLOSED:
+ l2c_le_csm_closed (p_ccb, event, p_data);
+ break;
+
+ case CST_ORIG_W4_SEC_COMP:
+ l2c_le_csm_orig_w4_sec_comp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_CONNECT_RSP:
+ l2c_csm_w4_l2cap_le_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_CONNECT_RSP:
+ l2c_csm_w4_l2ca_le_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_OPEN:
+ l2c_le_csm_open (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_DISCONNECT_RSP:
+ l2c_csm_w4_l2cap_le_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_DISCONNECT_RSP:
+ l2c_csm_w4_l2ca_le_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_le_csm_closed
+ **
+ ** Description This function handles events when the channel is in
+ ** CLOSED state. This state exists only when the le link is
+ ** being initially established.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_le_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ UINT16 local_cid = p_ccb->local_cid;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_LE_CONNECT_CFM_CB *le_connect_cfm =
+ p_ccb->p_rcb->api.pL2CA_LE_ConnectCfm_Cb;
+ tL2CA_LE_CONNECT_IND_CB *le_connect_ind =
+ p_ccb->p_rcb->api.pL2CA_LE_ConnectInd_Cb;
+ UINT32 status;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: CLOSED evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */
+ /* Disconnect unless the connection is already exists */
+ if ( *((UINT8 *)(p_data)) != HCI_ERR_CONNECTION_EXISTS)
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: Connection Faied: CID:0x%04x",
+ p_ccb->local_cid);
+ p_ccb->le_loc_conn_info.result = L2CAP_LE_CONN_NO_RESOURCES;
+ (*le_connect_cfm)(local_cid, &p_ccb->le_loc_conn_info);
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ case L2CEVT_L2CA_LE_CONNECT_REQ: /* API connect request */
+ dump_le_l2cap_conn_info(0, p_ccb);
+ if (btm_sec_l2cap_le_access_req (p_ccb->p_lcb->remote_bd_addr,
+ p_ccb->p_rcb->psm, p_ccb->p_lcb->handle, TRUE,
+ &l2c_le_link_sec_comp, p_ccb) == BTM_CMD_STARTED)
+ {
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ }
+ break;
+
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;
+ l2cu_send_peer_le_credit_based_conn_req (p_ccb);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL,
+ L2CAP_CHNL_CONNECT_TOUT);
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ L2CAP_TRACE_ERROR ("LE-L2CAP: Security Failed : status:%d CID:0x%04x",
+ *((UINT8 *)(p_data)) , p_ccb->local_cid);
+
+ p_ccb->le_loc_conn_info.result = *((UINT8 *)(p_data));
+ (*le_connect_cfm)(local_cid, &p_ccb->le_loc_conn_info);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CAP_LE_CONNECT_REQ: /* Peer connect request */
+ dump_le_l2cap_conn_info(1, p_ccb);
+ /* check for le-l2cap access request */
+ status = btm_sec_l2cap_le_access_req (p_ccb->p_lcb->remote_bd_addr,
+ p_ccb->p_rcb->psm, p_ccb->p_lcb->handle, FALSE,
+ NULL, p_ccb);
+
+ if(status == BTM_BLE_SUCCESS)
+ {
+ p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL,
+ L2CAP_CHNL_CONNECT_TOUT);
+
+ (*le_connect_ind) (p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid,
+ p_ccb->remote_id, &p_ccb->le_rmt_conn_info);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: Security Failed : status:%d CID:0x%04x",
+ p_ccb->local_cid, status);
+ l2cu_send_peer_le_credit_based_conn_rsp (p_ccb, status);
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Timeout CID: 0x%04x ", p_ccb->local_cid);
+ p_ccb->le_loc_conn_info.result = L2CAP_CONN_TIMEOUT;
+ (*le_connect_cfm)(local_cid, &p_ccb->le_loc_conn_info);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ GKI_freebuf (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: CLOSED unhandled evt:%d", event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_le_csm_orig_w4_sec_comp
+ **
+ ** Description This function handles events when the le channel is in
+ ** CST_ORIG_W4_SEC_COMP state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_le_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_LE_CONNECT_CFM_CB *le_connect_cfm =
+ p_ccb->p_rcb->api.pL2CA_LE_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: ORIG_W4_SEC_COMP evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;
+ l2cu_send_peer_le_credit_based_conn_req (p_ccb);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL,
+ L2CAP_CHNL_CONNECT_TOUT);
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ L2CAP_TRACE_ERROR ("LE-L2CAP: Security Failed : status:%d CID:0x%04x",
+ *((UINT8 *)(p_data)), p_ccb->local_cid);
+
+ p_ccb->le_loc_conn_info.result = *((UINT8 *)(p_data));
+ (*le_connect_cfm)(local_cid, &p_ccb->le_loc_conn_info);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_freebuf (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: ORIG_W4_SEC_COMP unhandled evt:%d",
+ event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_csm_w4_l2cap_le_connect_rsp
+ **
+ ** Description This function handles events when the le channel is in
+ ** CST_W4_L2CAP_CONNECT_RSP state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_csm_w4_l2cap_le_connect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_LE_CONNECT_CFM_CB *le_connect_cfm =
+ p_ccb->p_rcb->api.pL2CA_LE_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CAP_LE_CON_RSP evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ p_ccb->chnl_state = CST_CLOSED;
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CAP_LE_CONNECT_RSP: /* Got peer LE CB onnect confirm */
+ p_ccb->chnl_state = CST_OPEN;
+ dump_le_l2cap_conn_info(1, p_ccb);
+ l2c_link_adjust_chnl_allocation ();
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ (*le_connect_cfm)(local_cid, &p_ccb->le_rmt_conn_info);
+ break;
+ case L2CEVT_L2CAP_LE_CONNECT_RSP_NEG: /* Peer rejected connection */
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ (*le_connect_cfm)(local_cid, &p_ccb->le_rmt_conn_info);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_WARNING ("LE-L2CAP: Timeout CID: 0x%04x ", p_ccb->local_cid);
+ p_ccb->le_loc_conn_info.result = L2CAP_CONN_TIMEOUT;
+ (*le_connect_cfm)(local_cid, &p_ccb->le_loc_conn_info);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_freebuf (p_data);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CAP_LE_CON_RSP unhandled evt:%d",
+ event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_csm_w4_l2ca_le_connect_rsp
+ **
+ ** Description This function handles events when the le channel is in
+ ** CST_W4_L2CA_CONNECT_RSP state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_csm_w4_l2ca_le_connect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CA_LE_CON_RSP evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ p_ccb->chnl_state = CST_CLOSED;
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_LE_CONNECT_RSP:
+ l2cu_send_peer_le_credit_based_conn_rsp (p_ccb, L2CAP_LE_CONN_OK);
+ p_ccb->chnl_state = CST_OPEN;
+ dump_le_l2cap_conn_info(0, p_ccb);
+ l2c_link_adjust_chnl_allocation ();
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ break;
+
+ case L2CEVT_L2CA_LE_CONNECT_RSP_NEG:
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ /* send the response to the remote */
+ l2cu_send_peer_le_credit_based_conn_rsp (p_ccb,
+ p_ccb->le_loc_conn_info.result);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_le_credit_based_conn_rsp (p_ccb, L2CAP_LE_CONN_NO_PSM);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_freebuf (p_data);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CA_LE_CON_RSP unhandled evt:%d",
+ event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_le_csm_open
+ **
+ ** Description This function handles events when the le channel is in
+ ** OPEN state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_le_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_DATA_IND_CB *data_ind = p_ccb->p_rcb->api.pL2CA_DataInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: OPEN evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+ p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL,
+ L2CAP_CHNL_DISCONNECT_TOUT);
+ (*disconnect_ind)(local_cid, TRUE);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ (*data_ind) (local_cid, (BT_HDR *)p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_send_peer_disc_req (p_ccb);
+ p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL,
+ L2CAP_CHNL_DISCONNECT_TOUT);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: OPEN unhandled evt:%d",
+ event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_csm_w4_l2cap_le_disconnect_rsp
+ **
+ ** Description This function handles events when the le channel is in
+ ** CST_W4_L2CAP_DISCONNECT_RSP state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_csm_w4_l2cap_le_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data)
+{
+ tL2CA_DISCONNECT_CFM_CB *disconnect_cfm =
+ p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CAP_DISC_RSP evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+ switch (event)
+ {
+ case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */
+ /* disconnection collision */
+ /* stop the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id,
+ p_ccb->local_cid, p_ccb->remote_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ break;
+
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ case L2CEVT_TIMEOUT: /* Timeout */
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ GKI_freebuf (p_data);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CAP_DISC_RSP unhandled evt:%d",
+ event);
+ break;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function l2c_csm_w4_l2ca_le_disconnect_rsp
+ **
+ ** Description This function handles events when the le channel is in
+ ** CST_W4_L2CA_DISCONNECT_RSP state.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void l2c_csm_w4_l2ca_le_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event,
+ void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind =
+ p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CA_DISC_RSP evt:%d LCID:0x%04x psm:%d",
+ event, p_ccb->local_cid, p_ccb->p_rcb->psm);
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id,
+ p_ccb->local_cid, p_ccb->remote_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper disconnect request */
+ case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper disconnect response */
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id,
+ p_ccb->local_cid, p_ccb->remote_cid);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ GKI_freebuf (p_data);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("LE-L2CAP - st: W4_L2CA_DISC_RSP unhandled evt:%d",
+ event);
+ break;
+ }
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
diff --git a/stack/l2cap/l2c_link.c b/stack/l2cap/l2c_link.c
index 822257e1c..6cda75d3a 100644
--- a/stack/l2cap/l2c_link.c
+++ b/stack/l2cap/l2c_link.c
@@ -41,7 +41,11 @@
#include "btm_api.h"
#include "btm_int.h"
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf);
+#else
static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf);
+#endif
/*******************************************************************************
**
@@ -279,6 +283,64 @@ BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
return (TRUE);
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+ **
+ ** Function l2c_le_link_sec_comp
+ **
+ ** Description This function is called when required security procedures
+ ** are completed for LE-L2CAP.
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+void l2c_le_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_next_ccb;
+ UINT8 event;
+
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: %s status=%d, transport=%d, p_ref_data=0x%x, ",
+ __FUNCTION__, status, transport, p_ref_data);
+
+ if (status == BTM_SUCCESS_NO_SECURITY)
+ status = BTM_BLE_SUCCESS;
+
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, transport);
+
+ /* If we don't have one, this is an error */
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: got sec_comp for unknown BD_ADDR");
+ return;
+ }
+
+ /* Match p_ccb with p_ref_data returned by sec manager */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb)
+ {
+ p_next_ccb = p_ccb->p_next_ccb;
+
+ if (p_ccb == p_ref_data)
+ {
+ switch(status)
+ {
+ case BTM_BLE_SUCCESS:
+ event = L2CEVT_SEC_COMP;
+ break;
+
+ default:
+ L2CAP_TRACE_ERROR ("LE-L2CAP: %s Sec Failed status",
+ __FUNCTION__, status);
+ event = L2CEVT_SEC_COMP_NEG;
+ }
+ l2c_le_csm_execute (p_ccb, event, (void *) &status);
+ break;
+ }
+ }
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
+
/*******************************************************************************
**
@@ -404,6 +466,13 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
*/
if (p_ccb != p_lcb->p_pending_ccb)
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((p_lcb->transport == BT_TRANSPORT_LE) && (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason);
}
p_ccb = pn;
@@ -465,6 +534,13 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
}
#endif
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if(p_lcb->p_pending_ccb && (p_lcb->transport == BT_TRANSPORT_LE) &&
+ (p_lcb->p_pending_ccb->is_le_coc))
+ {
+ transport = BT_TRANSPORT_LE;
+ }
+#endif
if (l2cu_create_conn(p_lcb, transport))
lcb_is_free = FALSE; /* still using this lcb */
}
@@ -552,7 +628,13 @@ void l2c_link_timeout (tL2C_LCB *p_lcb)
for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
{
tL2C_CCB *pn = p_ccb->p_next_ccb;
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((p_lcb->transport == BT_TRANSPORT_LE) && (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
p_ccb = pn;
@@ -588,7 +670,13 @@ void l2c_link_timeout (tL2C_LCB *p_lcb)
for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
{
tL2C_CCB *pn = p_ccb->p_next_ccb;
-
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((p_lcb->transport == BT_TRANSPORT_LE) && (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
p_ccb = pn;
@@ -862,6 +950,14 @@ void l2c_link_adjust_chnl_allocation (void)
if (!p_ccb->in_use)
continue;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) && (p_ccb->is_le_coc))
+ {
+ /* low data rate is 1, medium is 2, high is 3 and no traffic is 0 */
+ weighted_chnls[HCI_LE_ACL_POOL_ID] += p_ccb->tx_data_rate + p_ccb->rx_data_rate;
+ }
+ else
+#endif
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
weighted_chnls[p_ccb->ertm_info.user_tx_pool_id] += p_ccb->tx_data_rate;
@@ -913,7 +1009,17 @@ void l2c_link_adjust_chnl_allocation (void)
if (!p_ccb->in_use)
continue;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) && (p_ccb->is_le_coc))
+ {
+ p_ccb->buff_quota = quota_per_weighted_chnls[HCI_LE_ACL_POOL_ID] * p_ccb->tx_data_rate;
+ L2CAP_TRACE_EVENT ("LE-L2CAP: CID:0x%04x LE TxPool:%u Priority:%u TxDataRate:%u Quota:%u",
+ p_ccb->local_cid, HCI_LE_ACL_POOL_ID, p_ccb->ccb_priority,
+ p_ccb->tx_data_rate, p_ccb->buff_quota);
+ }
+ else
+#endif
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
p_ccb->buff_quota = quota_per_weighted_chnls[p_ccb->ertm_info.user_tx_pool_id] * p_ccb->tx_data_rate;
@@ -1275,7 +1381,11 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
** Returns TRUE for success, FALSE for fail
**
*******************************************************************************/
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
+#else
static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
+#endif
{
UINT16 num_segs;
UINT16 xmit_window, acl_data_size;
diff --git a/stack/l2cap/l2c_main.c b/stack/l2cap/l2c_main.c
index bbadbea98..18889d47e 100644
--- a/stack/l2cap/l2c_main.c
+++ b/stack/l2cap/l2c_main.c
@@ -38,6 +38,9 @@
#include "l2cdefs.h"
#include "osi/include/log.h"
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#include <pthread.h>
+#endif
/********************************************************************************/
/* L O C A L F U N C T I O N P R O T O T Y P E S */
/********************************************************************************/
@@ -50,6 +53,10 @@ static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
tL2C_CB l2cb;
#endif
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+pthread_mutex_t lock_mutex_le_credits;
+#endif
+
/*******************************************************************************
**
** Function l2c_bcst_msg
@@ -294,6 +301,23 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
GKI_freebuf (p_msg);
else
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (p_lcb->transport == BT_TRANSPORT_LE)
+ {
+ if ( (p_ccb->is_le_coc == TRUE) && (p_ccb->chnl_state == CST_OPEN))
+ {
+ if(!l2c_le_proc_pdu(p_ccb, p_msg))
+ GKI_freebuf (p_msg);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR("L2CAP-LE: Error: Not a LE COC chnl or state is not open" \
+ "state %d is_le_coc", p_ccb->chnl_state, p_ccb->is_le_coc);
+ GKI_freebuf (p_msg);
+ }
+ }
+ else
+#endif /* LE_L2CAP_CFC_INCLUDED */
/* Basic mode packets go straight to the state machine */
if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
@@ -440,7 +464,11 @@ static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
case L2CAP_CMD_CONN_REQ:
STREAM_TO_UINT16 (con_info.psm, p);
STREAM_TO_UINT16 (rcid, p);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL)
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm);
l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM);
@@ -911,12 +939,23 @@ void l2c_init (void)
l2cb.rcv_pending_q = list_new(NULL);
if (l2cb.rcv_pending_q == NULL)
LOG_ERROR("%s unable to allocate memory for link layer control block", __func__);
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ pthread_mutex_init(&lock_mutex_le_credits, NULL);
+#endif
}
void l2c_free(void) {
list_free(l2cb.rcv_pending_q);
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+void l2c_cleanup (void)
+{
+ pthread_mutex_destroy(&lock_mutex_le_credits);
+}
+#endif
+
/*******************************************************************************
**
** Function l2c_process_timeout
@@ -936,6 +975,14 @@ void l2c_process_timeout (TIMER_LIST_ENT *p_tle)
break;
case BTU_TTYPE_L2CAP_CHNL:
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport((tL2C_CCB *)p_tle->param))
+ && (((tL2C_CCB *)p_tle->param)->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
+ }
+ else
+#endif
l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
break;
@@ -979,6 +1026,19 @@ UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
#ifndef TESTER /* Tester may send any amount of data. otherwise sending message
bigger than mtu size of peer is a violation of protocol */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) &&
+ (p_ccb->is_le_coc == TRUE))
+ {
+ if (p_data->len > p_ccb->le_rmt_conn_info.le_mtu)
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: CID: 0x%04x Send msg bigger than peer's mtu", cid);
+ GKI_freebuf (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+ }
+ else
+#endif
if (p_data->len > p_ccb->peer_cfg.mtu)
{
L2CAP_TRACE_WARNING ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid);
@@ -1003,6 +1063,14 @@ UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
counter_add("l2cap.dyn.tx.bytes", p_data->len);
counter_add("l2cap.dyn.tx.pkts", 1);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) &&
+ (p_ccb->is_le_coc == TRUE))
+ {
+ l2c_le_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
+ }
+ else
+#endif
l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
if (p_ccb->cong_sent)
diff --git a/stack/l2cap/l2c_ucd.c b/stack/l2cap/l2c_ucd.c
index 2e5f00e52..80c94762f 100644
--- a/stack/l2cap/l2c_ucd.c
+++ b/stack/l2cap/l2c_ucd.c
@@ -101,7 +101,11 @@ static void l2c_ucd_data_ind_cback (BD_ADDR rem_bda, BT_HDR *p_buf)
p_buf->offset += L2CAP_UCD_OVERHEAD;
p_buf->len -= L2CAP_UCD_OVERHEAD;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
{
L2CAP_TRACE_ERROR ("L2CAP - no RCB for l2c_ucd_data_ind_cback, PSM: 0x%04x", psm);
GKI_freebuf (p_buf);
@@ -212,7 +216,11 @@ BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info )
return (FALSE);
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
{
L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdRegister, PSM: 0x%04x", psm);
return (FALSE);
@@ -222,9 +230,17 @@ BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info )
p_rcb->ucd.cb_info = *p_cb_info;
/* check if master rcb is created for UCD */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL)
+#endif
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID)) == NULL)
+#endif
{
L2CAP_TRACE_ERROR ("L2CAP - no RCB available for L2CA_UcdRegister");
return (FALSE);
@@ -270,7 +286,11 @@ BOOLEAN L2CA_UcdDeregister ( UINT16 psm )
L2CAP_TRACE_API ("L2CA_UcdDeregister() PSM: 0x%04x", psm);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+#endif
{
L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdDeregister, PSM: 0x%04x", psm);
return (FALSE);
@@ -288,7 +308,11 @@ BOOLEAN L2CA_UcdDeregister ( UINT16 psm )
}
/* delete master rcb for UCD */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID, BT_TRANSPORT_BR_EDR)) != NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL)
+#endif
{
l2cu_release_rcb (p_rcb);
}
@@ -334,8 +358,13 @@ BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type )
(rem_bda[4]<<8)+rem_bda[5], info_type);
/* Fail if the PSM is not registered */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+ ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+#else
if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDiscover, PSM: 0x%04x", psm);
return (FALSE);
@@ -401,8 +430,13 @@ UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 fla
(rem_bda[4]<<8)+rem_bda[5]);
/* Fail if the PSM is not registered */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm, BT_TRANSPORT_BR_EDR)) == NULL)
+ ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+#else
if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm);
GKI_freebuf (p_buf);
@@ -609,7 +643,11 @@ static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda )
/* Set the default channel priority value to use */
l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID, BT_TRANSPORT_BR_EDR)) == NULL)
+#else
if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL)
+#endif
{
L2CAP_TRACE_WARNING ("L2CAP - no UCD registered, l2c_ucd_connect");
return (FALSE);
@@ -906,8 +944,13 @@ BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg)
tL2C_CCB *p_ccb;
tL2C_RCB *p_rcb;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) != NULL)
+ ||((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID, BT_TRANSPORT_BR_EDR)) != NULL))
+#else
if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) != NULL)
||((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL))
+#endif
{
if (p_ccb == NULL)
{
diff --git a/stack/l2cap/l2c_utils.c b/stack/l2cap/l2c_utils.c
index e80d16b0f..4a1926850 100644
--- a/stack/l2cap/l2c_utils.c
+++ b/stack/l2cap/l2c_utils.c
@@ -41,6 +41,27 @@
#include "bt_utils.h"
#include "osi/include/allocator.h"
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+#include <pthread.h>
+#endif
+
+/*******************************************************************************
+**
+** Function l2cu_get_chnl_transport
+**
+** Description Get the transport corresponds to channel
+**
+** Returns transport or 0 if any error.
+**
+*******************************************************************************/
+tBT_TRANSPORT l2cu_get_chnl_transport (tL2C_CCB *p_ccb)
+{
+ if(!p_ccb || !p_ccb->p_lcb)
+ return 0;
+ else
+ return p_ccb->p_lcb->transport;
+}
+
/*******************************************************************************
**
** Function l2cu_allocate_lcb
@@ -934,6 +955,23 @@ void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb)
UINT16_TO_STREAM (p, p_ccb->remote_cid);
UINT16_TO_STREAM (p, p_ccb->local_cid);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if (BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb))
+ {
+ if(p_ccb->is_le_coc == TRUE)
+ {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: %s CID: 0x%04x is not a LE COC channel",
+ __FUNCTION__, p_ccb->local_cid);
+ GKI_freebuf(p_buf);
+ }
+ return;
+ }
+ else
+#endif
/* Move all queued data packets to the LCB. In FCR mode, assume the higher
layer checks that all buffers are sent before disconnecting.
*/
@@ -1515,6 +1553,9 @@ tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
p_ccb->in_use = TRUE;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_ccb->is_le_coc = FALSE;
+#endif
/* Get a CID for the connection */
p_ccb->local_cid = L2CAP_BASE_APPL_CID + (UINT16)(p_ccb - l2cb.ccb_pool);
@@ -1536,6 +1577,11 @@ tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
memset (&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO));
memset (&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ memset (&p_ccb->le_loc_conn_info, 0, sizeof(tL2CAP_LE_CONN_INFO));
+ memset (&p_ccb->le_rmt_conn_info, 0, sizeof(tL2CAP_LE_CONN_INFO));
+#endif
+
/* Put in default values for local/peer configurations */
p_ccb->our_cfg.flush_to = p_ccb->peer_cfg.flush_to = L2CAP_DEFAULT_FLUSH_TO;
p_ccb->our_cfg.mtu = p_ccb->peer_cfg.mtu = L2CAP_DEFAULT_MTU;
@@ -1685,7 +1731,11 @@ void l2cu_release_ccb (tL2C_CCB *p_ccb)
if (p_rcb && (p_rcb->psm != p_rcb->real_psm))
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ btm_sec_clr_service_by_psm(p_rcb->psm, p_lcb->transport);
+#else
btm_sec_clr_service_by_psm(p_rcb->psm);
+#endif
}
if (p_ccb->should_free_rcb)
@@ -1738,6 +1788,9 @@ void l2cu_release_ccb (tL2C_CCB *p_ccb)
/* Flag as not in use */
p_ccb->in_use = FALSE;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_ccb->is_le_coc = FALSE;
+#endif
/* If no channels on the connection, start idle timeout */
if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED))
@@ -1754,6 +1807,40 @@ void l2cu_release_ccb (tL2C_CCB *p_ccb)
}
}
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+
+/*******************************************************************************
+**
+** Function l2cu_find_ccb_by_local_id
+**
+** Description Look through all active CCBs on a link for a match based
+** on the ID.
+**
+** Returns pointer to matched CCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_CCB *l2cu_find_ccb_by_local_id (tL2C_LCB *p_lcb, UINT8 id)
+{
+ tL2C_CCB *p_ccb;
+
+ /* If LCB is NULL, look through all active links */
+ if (!p_lcb)
+ {
+ return NULL;
+ }
+ else
+ {
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ if ((p_ccb->in_use) && (p_ccb->local_id == id))
+ return (p_ccb);
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+#endif
+
/*******************************************************************************
**
** Function l2cu_find_ccb_by_remote_cid
@@ -1794,7 +1881,11 @@ tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid)
** Returns Pointer to the RCB or NULL if not found
**
*******************************************************************************/
-tL2C_RCB *l2cu_allocate_rcb (UINT16 psm)
+tL2C_RCB *l2cu_allocate_rcb (UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ )
{
tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
UINT16 xx;
@@ -1808,6 +1899,9 @@ tL2C_RCB *l2cu_allocate_rcb (UINT16 psm)
#if (L2CAP_UCD_INCLUDED == TRUE)
p_rcb->ucd.state = L2C_UCD_STATE_UNUSED;
#endif
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ p_rcb->transport = transport;
+#endif
return (p_rcb);
}
}
@@ -1875,14 +1969,22 @@ void l2cu_disconnect_chnl (tL2C_CCB *p_ccb)
** Returns Pointer to the RCB or NULL if not found
**
*******************************************************************************/
-tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm)
+tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ , tBT_TRANSPORT transport
+#endif
+ )
{
tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
UINT16 xx;
for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++)
{
- if ((p_rcb->in_use) && (p_rcb->psm == psm))
+ if ((p_rcb->in_use) && (p_rcb->psm == psm)
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ && (transport == p_rcb->transport)
+#endif
+ )
return (p_rcb);
}
@@ -2996,6 +3098,151 @@ void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id)
l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
}
+/* LE_L2CAP_CODE */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+/*******************************************************************************
+**
+** Function l2cu_send_peer_le_credit_based_conn_req
+**
+** Description Build and send a LE credit based connection request message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_le_credit_based_conn_req (tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id (p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, LE_L2CAP_CMD_CB_CONN_REQ_LEN,
+ LE_L2CAP_CMD_CB_CONN_REQ, p_ccb->local_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: l2cu_send_peer_le_credit_based_conn_req - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.le_psm);
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.le_mtu);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.le_mps);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.init_credits);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_reject_connection
+**
+** Description Build and send an LE L2CAP "connection response neg" message
+** to the peer. This function is called when there is no peer
+** CCB (non-existant PSM or no resources).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_le_reject_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header (p_lcb, LE_L2CAP_CMD_CB_CONN_RSP_LEN,
+ LE_L2CAP_CMD_CB_CONN_RSP, rem_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: l2cu_send_peer_le_credit_based_conn_rsp - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, 0);
+ UINT16_TO_STREAM (p, 0);
+ UINT16_TO_STREAM (p, 0);
+ UINT16_TO_STREAM (p, 0);
+ UINT16_TO_STREAM (p, result);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_le_credit_based_conn_rsp
+**
+** Description Build and send a LE credit based connection response message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_le_credit_based_conn_rsp (tL2C_CCB *p_ccb, UINT8 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, LE_L2CAP_CMD_CB_CONN_RSP_LEN,
+ LE_L2CAP_CMD_CB_CONN_RSP, p_ccb->remote_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: l2cu_send_peer_le_credit_based_conn_rsp - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.le_mtu);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.le_mps);
+ UINT16_TO_STREAM (p, p_ccb->le_loc_conn_info.init_credits);
+ UINT16_TO_STREAM (p, result);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_le_credit_based_flow_ctrl
+**
+** Description Build and send a LE credit based flow control message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_le_credit_based_flow_ctrl (tL2C_CCB *p_ccb, UINT16 credits)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id (p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, LE_L2CAP_CMD_CB_FLOW_CTRL_LEN,
+ LE_L2CAP_CMD_CB_FLOW_CTRL, p_ccb->p_lcb->id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("LE-L2CAP: l2cu_send_peer_le_credit_based_flow_ctrl - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, credits);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+#endif /* LE_L2CAP_CFC_INCLUDED */
#endif /* BLE_INCLUDED == TRUE */
@@ -3145,6 +3392,18 @@ static tL2C_CCB *l2cu_get_next_channel_in_rr(tL2C_LCB *p_lcb)
if (p_ccb->chnl_state != CST_OPEN)
continue;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((p_lcb->transport == BT_TRANSPORT_LE) && (p_ccb->is_le_coc == TRUE))
+ {
+ /* continue with next entry if chnl is LE coc chnl and no credits
+ available */
+ if((!p_ccb->xmit_hold_q._count) || (!p_ccb->le_rmt_conn_info.credits))
+ {
+ continue;
+ }
+ }
+ else
+#endif
/* eL2CAP option in use */
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
@@ -3221,6 +3480,18 @@ static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb)
if (p_ccb->chnl_state != CST_OPEN)
continue;
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((p_lcb->transport == BT_TRANSPORT_LE) && (p_ccb->is_le_coc == TRUE))
+ {
+ /* continue with next entry if chnl is LE coc chnl and no credits
+ available */
+ if((!p_ccb->xmit_hold_q.count) || (!p_ccb->le_rmt_conn_info.credits))
+ {
+ continue;
+ }
+ }
+#endif
+
if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy)
continue;
@@ -3246,6 +3517,128 @@ static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb)
}
#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+BT_HDR *l2c_le_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb)
+{
+ BOOLEAN first_seg = FALSE; /* The segment is the first part of data */
+ UINT16 sdu_len = 0;
+ BT_HDR *p_buf, *p_xmit;
+ UINT8 *p;
+ UINT16 max_pdu = p_ccb->le_rmt_conn_info.le_mps;
+ UINT8 i = 0;
+
+ p_buf = (BT_HDR *)p_ccb->xmit_hold_q._p_first;
+
+ if (p_buf->event == 0) // to know the first PDU
+ {
+ first_seg = TRUE;
+ sdu_len = p_buf->len;
+
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: first PDU max_pdu %d sdu_len %d", max_pdu, sdu_len);
+
+ /* If there is more data than the (MPS - SDU lenght filed), it requires segmentation */
+ if (p_buf->len > (max_pdu - L2CAP_SDU_LEN_OFFSET))
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: SDU requires Segmentation: ");
+ /* Get a new buffer and copy the data that can be sent in a PDU */
+ p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_LE_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET,
+ (max_pdu - L2CAP_SDU_LEN_OFFSET) , HCI_LE_ACL_POOL_ID);
+
+ if (p_xmit != NULL)
+ {
+ p_buf->event = p_ccb->local_cid;
+ p_xmit->event = p_ccb->local_cid;
+
+ p_buf->len -= (max_pdu - L2CAP_SDU_LEN_OFFSET);
+ p_buf->offset += (max_pdu - L2CAP_SDU_LEN_OFFSET);
+
+ }
+ else /* Should never happen if the application has configured buffers correctly */
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: cannot get buffer, for segmentation, pool: %u",
+ p_ccb->ertm_info.fcr_tx_pool_id);
+ return (NULL);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: SDU No Segmentation required ");
+ p_xmit = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+
+ if(p_xmit)
+ p_xmit->event = p_ccb->local_cid;
+ else
+ return (NULL);
+ }
+ }
+ else // follow up PDUs
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: followup PDU max_pdu %d sdu_len %d",
+ max_pdu, p_buf->len);
+ /* If there is more data than the MPS, it requires segmentation */
+ if (p_buf->len > max_pdu)
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: SDU requires Segmentation ");
+ /* Get a new buffer and copy the data that can be sent in a PDU */
+ p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_LE_MIN_OFFSET,
+ max_pdu, HCI_LE_ACL_POOL_ID);
+
+ if (p_xmit != NULL)
+ {
+ p_buf->event = p_ccb->local_cid;
+ p_xmit->event = p_ccb->local_cid;
+
+ p_buf->len -= max_pdu;
+ p_buf->offset += max_pdu;
+
+ }
+ else /* Should never happen if the application has configured buffers correctly */
+ {
+ L2CAP_TRACE_ERROR ("LE-L2CAP: cannot get buffer, for segmentation, pool: %u",
+ p_ccb->ertm_info.fcr_tx_pool_id);
+ return (NULL);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG ("LE-L2CAP: SDU No Segmentation required ");
+ p_xmit = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+
+ if(p_xmit)
+ p_xmit->event = p_ccb->local_cid;
+ else
+ return (NULL);
+ }
+ }
+
+ /* Step back to add the L2CAP headers */
+ p_xmit->offset -= (L2CAP_PKT_OVERHEAD);
+ p_xmit->len += L2CAP_PKT_OVERHEAD;
+
+ if (first_seg)
+ {
+ p_xmit->offset -= L2CAP_SDU_LEN_OVERHEAD;
+ p_xmit->len += L2CAP_SDU_LEN_OVERHEAD;
+ }
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_xmit + 1) + p_xmit->offset;
+
+ /* Now the L2CAP header */
+
+ UINT16_TO_STREAM (p, p_xmit->len - L2CAP_PKT_OVERHEAD);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+
+ if (first_seg)
+ {
+ UINT16_TO_STREAM (p, sdu_len);
+
+ first_seg = FALSE;
+ }
+ return (p_xmit);
+}
+#endif
+
/******************************************************************************
**
** Function l2cu_get_next_buffer_to_send
@@ -3320,6 +3713,41 @@ BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb)
}
#endif
+
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if(p_lcb->transport == BT_TRANSPORT_LE)
+ {
+ pthread_mutex_lock(&lock_mutex_le_credits);
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* get next serving channel in round-robin */
+ p_ccb = l2cu_get_next_channel_in_rr( p_lcb );
+#else
+ p_ccb = l2cu_get_next_channel( p_lcb );
+#endif
+ if(p_ccb && p_ccb->is_le_coc == TRUE)
+ {
+ if ((p_buf = l2c_le_get_next_xmit_sdu_seg(p_ccb)) != NULL)
+ {
+ l2cu_check_channel_congestion (p_ccb);
+
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+
+ if (l2c_link_send_to_lower (p_lcb, p_buf))
+ {
+ p_ccb->le_rmt_conn_info.credits--;
+ if ((p_ccb->xmit_hold_q._count == 0) &&
+ p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_TxComplete_Cb )
+ {
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, 1);
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock(&lock_mutex_le_credits);
+ return (NULL);
+ }
+#endif
+
#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
/* get next serving channel in round-robin */
p_ccb = l2cu_get_next_channel_in_rr( p_lcb );
@@ -3450,6 +3878,18 @@ void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
/* If the channel is not congested now, tell the app */
if (q_count <= (p_ccb->buff_quota / 2))
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ if((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) && (p_ccb->is_le_coc == TRUE))
+ {
+ /* ignore the if le chnl state is not open or state is open and no
+ * credits available */
+ if((p_ccb->chnl_state != CST_OPEN) ||
+ ((p_ccb->chnl_state == CST_OPEN) && (!p_ccb->le_rmt_conn_info.credits)))
+ {
+ return;
+ }
+ }
+#endif /*LE_L2CAP_CFC_INCLUDED*/
p_ccb->cong_sent = FALSE;
if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
{
@@ -3492,8 +3932,14 @@ void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
}
else
{
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
/* If this channel was not congested but it is congested now, tell the app */
+ if ((q_count > p_ccb->buff_quota) ||
+ ((BT_TRANSPORT_LE == l2cu_get_chnl_transport(p_ccb)) && (p_ccb->is_le_coc &&
+ (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->le_rmt_conn_info.credits))))
+#else
if (q_count > p_ccb->buff_quota)
+#endif /*LE_L2CAP_CFC_INCLUDED*/
{
p_ccb->cong_sent = TRUE;
if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
diff --git a/stack/mcap/mca_api.c b/stack/mcap/mca_api.c
index ae4cdb22b..dec139ed1 100644
--- a/stack/mcap/mca_api.c
+++ b/stack/mcap/mca_api.c
@@ -195,8 +195,13 @@ void MCA_Deregister(tMCA_HANDLE handle)
{
L2CA_Deregister(p_rcb->reg.ctrl_psm);
L2CA_Deregister(p_rcb->reg.data_psm);
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ btm_sec_clr_service_by_psm (p_rcb->reg.ctrl_psm, BT_TRANSPORT_BR_EDR);
+ btm_sec_clr_service_by_psm (p_rcb->reg.data_psm, BT_TRANSPORT_BR_EDR);
+#else
btm_sec_clr_service_by_psm (p_rcb->reg.ctrl_psm);
btm_sec_clr_service_by_psm (p_rcb->reg.data_psm);
+#endif
}
mca_rcb_dealloc(handle);
}
diff --git a/stack/mcap/mca_l2c.c b/stack/mcap/mca_l2c.c
index fdd9d0936..fe3b20dc3 100644
--- a/stack/mcap/mca_l2c.c
+++ b/stack/mcap/mca_l2c.c
@@ -46,6 +46,11 @@ const tL2CAP_APPL_INFO mca_l2c_int_appl =
mca_l2c_data_ind_cback,
mca_l2c_congestion_ind_cback,
NULL
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
};
/* Control channel eL2CAP default options */
diff --git a/test/l2test_ertm/l2test_ertm.c b/test/l2test_ertm/l2test_ertm.c
index 418d3a520..fe1c52af9 100644
--- a/test/l2test_ertm/l2test_ertm.c
+++ b/test/l2test_ertm/l2test_ertm.c
@@ -339,6 +339,11 @@ static tL2CAP_APPL_INFO l2test_l2c_appl = {
l2test_l2c_data_ind_cb,
l2test_l2c_congestion_ind_cb,
l2test_l2c_tx_complete_cb
+#if (defined(LE_L2CAP_CFC_INCLUDED) && (LE_L2CAP_CFC_INCLUDED == TRUE))
+ ,
+ NULL,
+ NULL
+#endif
};