diff options
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 }; |