diff --git a/components/bt/bluedroid/include/bt_target.h b/components/bt/bluedroid/include/bt_target.h index 34b8c194e..b5883416d 100644 --- a/components/bt/bluedroid/include/bt_target.h +++ b/components/bt/bluedroid/include/bt_target.h @@ -665,6 +665,12 @@ #define L2CAP_CLIENT_INCLUDED FALSE #endif +/* The maximum number of simultaneous applications that can register with LE L2CAP. */ +#ifndef BLE_MAX_L2CAP_CLIENTS +#define BLE_MAX_L2CAP_CLIENTS 15 +#endif + + /* The maximum number of simultaneous links that L2CAP can support. Up to 7*/ #ifndef MAX_ACL_CONNECTIONS #define MAX_L2CAP_LINKS 5 diff --git a/components/bt/bluedroid/stack/btm/btm_ble.c b/components/bt/bluedroid/stack/btm/btm_ble.c index 81f2d50fe..c4fdbffa6 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/bluedroid/stack/btm/btm_ble.c @@ -830,6 +830,171 @@ tBTM_STATUS BTM_SetBleDataLength(BD_ADDR bd_addr, UINT16 tx_pdu_length) } } +/******************************************************************************* +** +** Function btm_ble_determine_security_act +** +** Description This function checks the security of current LE link +** and returns the appropriate action that needs to be +** taken to achieve the required security. +** +** Parameter is_originator - True if outgoing connection +** bdaddr: remote device address +** security_required: Security required for the service. +** +** Returns The appropriate security action required. +** +*******************************************************************************/ +tBTM_SEC_ACTION btm_ble_determine_security_act(BOOLEAN is_originator, BD_ADDR bdaddr, UINT16 security_required) +{ + tBTM_LE_AUTH_REQ auth_req = 0x00; + + if (is_originator) + { + if ((security_required & BTM_SEC_OUT_FLAGS) == 0 && + (security_required & BTM_SEC_OUT_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for outgoing connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_OUT_MITM) + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + else + { + if ((security_required & BTM_SEC_IN_FLAGS) == 0&& (security_required & BTM_SEC_IN_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for incoming connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_IN_MITM) + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + + tBTM_BLE_SEC_REQ_ACT ble_sec_act; + btm_ble_link_sec_check(bdaddr, auth_req, &ble_sec_act); + + BTM_TRACE_DEBUG ("%s ble_sec_act %d", __func__ , ble_sec_act); + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_DISCARD) + return BTM_SEC_ENC_PENDING; + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_NONE) + return BTM_SEC_OK; + + UINT8 sec_flag = 0; + BTM_GetSecurityFlagsByTransport(bdaddr, &sec_flag, BT_TRANSPORT_LE); + + BOOLEAN is_link_encrypted = FALSE; + BOOLEAN is_key_mitm = FALSE; + if (sec_flag & (BTM_SEC_FLAG_ENCRYPTED| BTM_SEC_FLAG_LKEY_KNOWN)) + { + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) + is_link_encrypted = TRUE; + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) + is_key_mitm = TRUE; + } + + if (auth_req & BTM_LE_AUTH_REQ_MITM) + { + if (!is_key_mitm) + { + return BTM_SEC_ENCRYPT_MITM; + } else { + if (is_link_encrypted) + return BTM_SEC_OK; + else + return BTM_SEC_ENCRYPT; + } + } else { + if (is_link_encrypted) + return BTM_SEC_OK; + else + return BTM_SEC_ENCRYPT_NO_MITM; + } + + return BTM_SEC_OK; +} + + +/******************************************************************************* +** +** Function btm_ble_start_sec_check +** +** Description This function is to check and set the security required for +** LE link for LE COC. +** +** Parameter bdaddr: remote device address. +** psm : PSM of the LE COC sevice. +** is_originator: TRUE if outgoing connection. +** p_callback : Pointer to the callback function. +** p_ref_data : Pointer to be returned along with the callback. +** +** Returns TRUE if link already meets the required security; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + /* Find the service record for the PSM */ + tBTM_SEC_SERV_REC *p_serv_rec = btm_sec_find_first_serv (is_originator, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + BTM_TRACE_WARNING ("%s PSM: %d no application registerd", __func__, psm); + (*p_callback) (bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_MODE_UNSUPPORTED); + return FALSE; + } + + tBTM_SEC_ACTION sec_act = btm_ble_determine_security_act(is_originator, + bd_addr, p_serv_rec->security_flags); + + tBTM_BLE_SEC_ACT ble_sec_act = BTM_BLE_SEC_NONE; + BOOLEAN status = FALSE; + + switch (sec_act) + { + case BTM_SEC_OK: + BTM_TRACE_DEBUG ("%s Security met", __func__); + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_SUCCESS); + status = TRUE; + break; + + case BTM_SEC_ENCRYPT: + BTM_TRACE_DEBUG ("%s Encryption needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + + case BTM_SEC_ENCRYPT_MITM: + BTM_TRACE_DEBUG ("%s Pairing with MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + + case BTM_SEC_ENCRYPT_NO_MITM: + BTM_TRACE_DEBUG ("%s Pairing with No MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + + case BTM_SEC_ENC_PENDING: + BTM_TRACE_DEBUG ("%s Ecryption pending", __func__); + break; + } + + if (ble_sec_act == BTM_BLE_SEC_NONE) + return status; + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + p_lcb->sec_act = sec_act; + BTM_SetEncryption(bd_addr, BT_TRANSPORT_LE, p_callback, p_ref_data); + + return FALSE; +} + + /******************************************************************************* ** ** Function btm_ble_rand_enc_complete diff --git a/components/bt/bluedroid/stack/btm/btm_sec.c b/components/bt/bluedroid/stack/btm/btm_sec.c index bf4a28319..146c10c86 100644 --- a/components/bt/bluedroid/stack/btm/btm_sec.c +++ b/components/bt/bluedroid/stack/btm/btm_sec.c @@ -56,7 +56,6 @@ 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 * *********************************************************************************/ #if (SMP_INCLUDED == TRUE) -static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (BOOLEAN is_originator, UINT16 psm); static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur); static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, UINT32 mx_proto_id, @@ -2049,15 +2048,6 @@ static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_origi ** Returns tBTM_STATUS ** *******************************************************************************/ -#define BTM_SEC_OUT_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHORIZE) -#define BTM_SEC_IN_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE) - -#define BTM_SEC_OUT_LEVEL4_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | \ - BTM_SEC_OUT_MITM | BTM_SEC_MODE4_LEVEL4) - -#define BTM_SEC_IN_LEVEL4_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | \ - BTM_SEC_IN_MITM | BTM_SEC_MODE4_LEVEL4) - tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle, CONNECTION_TYPE conn_type, tBTM_SEC_CALLBACK *p_callback, @@ -3480,7 +3470,7 @@ void btm_io_capabilities_rsp (UINT8 *p) ** ** Returns void ** -*******************************************************************************/ +*******************************************************************************/ void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p) { tBTM_STATUS status = BTM_ERR_PROCESSING; @@ -5422,7 +5412,7 @@ BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]) ** *******************************************************************************/ #if (SMP_INCLUDED == TRUE) -static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm) +tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm) { tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; int i; diff --git a/components/bt/bluedroid/stack/include/btm_int.h b/components/bt/bluedroid/stack/include/btm_int.h index 42468d958..1b4cd7259 100644 --- a/components/bt/bluedroid/stack/include/btm_int.h +++ b/components/bt/bluedroid/stack/include/btm_int.h @@ -416,6 +416,15 @@ void btm_sco_chk_pend_rolechange (UINT16 hci_handle); ** Define structure for Security Service Record. ** A record exists for each service registered with the Security Manager */ +#define BTM_SEC_OUT_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHORIZE) +#define BTM_SEC_IN_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE) + +#define BTM_SEC_OUT_LEVEL4_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | \ + BTM_SEC_OUT_MITM | BTM_SEC_MODE4_LEVEL4) + +#define BTM_SEC_IN_LEVEL4_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | \ + BTM_SEC_IN_MITM | BTM_SEC_MODE4_LEVEL4) + typedef struct { UINT32 mx_proto_id; /* Service runs over this multiplexer protocol */ UINT32 orig_mx_chan_id; /* Channel on the multiplexer protocol */ @@ -878,6 +887,15 @@ typedef struct{ }tBTM_CallbackFunc; extern tBTM_CallbackFunc conn_param_update_cb; +/* security action for L2CAP COC channels */ +#define BTM_SEC_OK 1 +#define BTM_SEC_ENCRYPT 2 /* encrypt the link with current key */ +#define BTM_SEC_ENCRYPT_NO_MITM 3 /* unauthenticated encryption or better */ +#define BTM_SEC_ENCRYPT_MITM 4 /* authenticated encryption */ +#define BTM_SEC_ENC_PENDING 5 /* wait for link encryption pending */ + +typedef UINT8 tBTM_SEC_ACTION; + /* #ifdef __cplusplus extern "C" @@ -1082,6 +1100,10 @@ BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda); void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec); BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda); BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr); +extern BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +extern tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm); + #endif /* BLE_INCLUDED */ tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); diff --git a/components/bt/bluedroid/stack/include/l2c_api.h b/components/bt/bluedroid/stack/include/l2c_api.h index e6e7e44f0..e2faae417 100644 --- a/components/bt/bluedroid/stack/include/l2c_api.h +++ b/components/bt/bluedroid/stack/include/l2c_api.h @@ -124,6 +124,8 @@ typedef UINT8 tL2CAP_CHNL_DATA_RATE; */ #define L2C_INVALID_PSM(psm) (((psm) & 0x0101) != 0x0001) #define L2C_IS_VALID_PSM(psm) (((psm) & 0x0101) == 0x0001) +#define L2C_IS_VALID_LE_PSM(psm) (((psm) > 0x0000) && ((psm) < 0x0100)) + /***************************************************************************** ** Type Definitions @@ -164,6 +166,17 @@ typedef struct { UINT16 flags; /* bit 0: 0-no continuation, 1-continuation */ } tL2CAP_CFG_INFO; +/* Define a structure to hold the configuration parameter for LE L2CAP connection +** oriented channels. +*/ +typedef struct +{ + UINT16 mtu; + UINT16 mps; + UINT16 credits; +} tL2CAP_LE_CFG_INFO; + + /* L2CAP channel configured field bitmap */ #define L2CAP_CH_CFG_MASK_MTU 0x0001 #define L2CAP_CH_CFG_MASK_QOS 0x0002 @@ -486,6 +499,72 @@ extern BOOLEAN L2CA_DisconnectReq (UINT16 cid); extern BOOLEAN L2CA_DisconnectRsp (UINT16 cid); #endif ///CLASSIC_BT_INCLUDED == TRUE +/******************************************************************************* +** +** Function L2CA_RegisterLECoc +** +** Description Other layers call this function to register for L2CAP +** Connection Oriented Channel. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectLECocReq() +** and BTM_SetSecurityLevel(). +** +*******************************************************************************/ +extern UINT16 L2CA_RegisterLECoc (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info); + +/******************************************************************************* +** +** Function L2CA_DeregisterLECoc +** +** Description Other layers call this function to deregister for L2CAP +** Connection Oriented Channel. +** +** Returns void +** +*******************************************************************************/ +extern void L2CA_DeregisterLECoc (UINT16 psm); + +/******************************************************************************* +** +** Function L2CA_ConnectLECocReq +** +** Description Higher layers call this function to create an L2CAP LE COC. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +extern UINT16 L2CA_ConnectLECocReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_ConnectLECocRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP LE COC connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +extern BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_GetPeerLECocConfig +** +** Description Get peers configuration for LE Connection Oriented Channel. +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +extern BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg); /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/l2cdefs.h b/components/bt/bluedroid/stack/include/l2cdefs.h index db5013a13..56ddfb0df 100644 --- a/components/bt/bluedroid/stack/include/l2cdefs.h +++ b/components/bt/bluedroid/stack/include/l2cdefs.h @@ -41,6 +41,10 @@ #define L2CAP_CMD_BLE_UPDATE_REQ 0x12 #define L2CAP_CMD_BLE_UPDATE_RSP 0x13 +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ 0x14 +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES 0x15 +#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT 0x16 + /* Define some packet and header lengths @@ -70,6 +74,11 @@ #define L2CAP_CMD_BLE_UPD_REQ_LEN 8 /* Min and max interval, latency, tout */ #define L2CAP_CMD_BLE_UPD_RSP_LEN 2 /* Result */ +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN 10 /* LE_PSM, SCID, MTU, MPS, Init Credit */ +#define L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN 10 /* DCID, MTU, MPS, Init credit, Result */ +#define L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN 4 /* CID, Credit */ + + /* Define the packet boundary flags */ diff --git a/components/bt/bluedroid/stack/l2cap/include/l2c_int.h b/components/bt/bluedroid/stack/l2cap/include/l2c_int.h index ef31d1095..44ef74b28 100644 --- a/components/bt/bluedroid/stack/l2cap/include/l2c_int.h +++ b/components/bt/bluedroid/stack/l2cap/include/l2c_int.h @@ -34,6 +34,17 @@ #define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */ +/* LE credit based L2CAP connection parameters */ +#define L2CAP_LE_MIN_MTU 23 +#define L2CAP_LE_MIN_MPS 23 +#define L2CAP_LE_MAX_MPS 65533 +#define L2CAP_LE_MIN_CREDIT 0 +#define L2CAP_LE_MAX_CREDIT 65535 +#define L2CAP_LE_DEFAULT_MTU 512 +#define L2CAP_LE_DEFAULT_MPS 23 +#define L2CAP_LE_DEFAULT_CREDIT 1 + + /* Timeouts. Since L2CAP works off a 1-second list, all are in seconds. */ #define L2CAP_LINK_ROLE_SWITCH_TOUT 10 /* 10 seconds */ @@ -240,6 +251,17 @@ typedef struct { tL2CAP_APPL_INFO api; } tL2C_RCB; +typedef void (tL2CAP_SEC_CBACK) (BD_ADDR bd_addr, tBT_TRANSPORT trasnport, + void *p_ref_data, tBTM_STATUS result); + +typedef struct +{ + UINT16 psm; + tBT_TRANSPORT transport; + BOOLEAN is_originator; + tL2CAP_SEC_CBACK *p_callback; + void *p_ref_data; +}tL2CAP_SEC_DATA; #ifndef L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA #define L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA 100 @@ -252,6 +274,8 @@ typedef struct { typedef struct t_l2c_ccb { BOOLEAN in_use; /* TRUE when in use, FALSE when not */ tL2C_CHNL_STATE chnl_state; /* Channel state */ + tL2CAP_LE_CFG_INFO local_conn_cfg; /* Our config for ble conn oriented channel */ + tL2CAP_LE_CFG_INFO peer_conn_cfg; /* Peer device config ble conn oriented channel */ struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */ struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */ @@ -400,7 +424,8 @@ typedef struct t_l2c_linkcb { #if (BLE_INCLUDED == TRUE) tBLE_ADDR_TYPE ble_addr_type; UINT16 tx_data_len; /* tx data length used in data length extension */ - + fixed_queue_t *le_sec_pending_q; /* LE coc channels waiting for security check completion */ + UINT8 sec_act; #define L2C_BLE_CONN_UPDATE_DISABLE 0x1 /* disable update connection parameters */ #define L2C_BLE_NEW_CONN_PARAM 0x2 /* new connection parameter to be set */ #define L2C_BLE_UPDATE_PENDING 0x4 /* waiting for connection update finished */ @@ -488,6 +513,7 @@ typedef struct { UINT16 ble_round_robin_quota; /* Round-robin link quota */ UINT16 ble_round_robin_unacked; /* Round-robin unacked */ BOOLEAN ble_check_round_robin; /* Do a round robin check */ + tL2C_RCB ble_rcb_pool[BLE_MAX_L2CAP_CLIENTS]; /* Registration info pool */ #endif tL2CA_ECHO_DATA_CB *p_echo_data_cb; /* Echo data callback */ @@ -632,6 +658,12 @@ BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data); #if (BLE_INCLUDED == TRUE) extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id); +extern void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result); +extern void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result); +extern void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value); +extern void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb); + #endif extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr); @@ -649,6 +681,9 @@ 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 void l2cu_release_rcb (tL2C_RCB *p_rcb); +extern tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm); +extern tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm); + extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); extern void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); @@ -748,6 +783,13 @@ extern void l2cble_process_conn_update_evt (UINT16 handle, UINT8 status, UINT16 UINT16 conn_latency, UINT16 conn_timeout); extern void l2cble_get_conn_param_format_err_from_contoller(UINT8 status, UINT16 handle); +extern void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb); +extern void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result); +extern void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb); +extern void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value); +extern BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data); + + #if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) extern void l2cble_process_rc_param_request_evt(UINT16 handle, UINT16 int_min, UINT16 int_max, UINT16 latency, UINT16 timeout); diff --git a/components/bt/bluedroid/stack/l2cap/l2c_api.c b/components/bt/bluedroid/stack/l2cap/l2c_api.c index 78c927fcd..280a2c687 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_api.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_api.c @@ -1293,6 +1293,309 @@ UINT8 L2CA_GetChnlFcrMode (UINT16 lcid) #endif ///CLASSIC_BT_INCLUDED == TRUE +/******************************************************************************* +** +** Function L2CA_RegisterLECoc +** +** Description Other layers call this function to register for L2CAP +** Connection Oriented Channel. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectLECocReq() +** and L2CA_DeregisterLECoc() +** +*******************************************************************************/ +UINT16 L2CA_RegisterLECoc(UINT16 psm, tL2CAP_APPL_INFO *p_cb_info) +{ + L2CAP_TRACE_API("%s called for LE PSM: 0x%04x", __func__, psm); + + /* Verify that the required callback info has been filled in + ** Note: Connection callbacks are required but not checked + ** for here because it is possible to be only a client + ** or only a server. + */ + if ((!p_cb_info->pL2CA_DataInd_Cb) + || (!p_cb_info->pL2CA_DisconnectInd_Cb)) + { + L2CAP_TRACE_ERROR("%s No cb registering BLE PSM: 0x%04x", __func__, psm); + return 0; + } + + /* Verify PSM is valid */ + if (!L2C_IS_VALID_LE_PSM(psm)) + { + L2CAP_TRACE_ERROR("%s Invalid BLE PSM value, PSM: 0x%04x", __func__, psm); + return 0; + } + + tL2C_RCB *p_rcb; + UINT16 vpsm = psm; + + /* Check if this is a registration for an outgoing-only connection to */ + /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */ + if ((psm >= 0x0080) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL)) + { + for (vpsm = 0x0080; vpsm < 0x0100; vpsm++) + { + p_rcb = l2cu_find_ble_rcb_by_psm(vpsm); + if (p_rcb == NULL) + break; + } + + L2CAP_TRACE_API("%s Real PSM: 0x%04x Virtual PSM: 0x%04x", __func__, psm, vpsm); + } + + /* If registration block already there, just overwrite it */ + p_rcb = l2cu_find_ble_rcb_by_psm(vpsm); + if (p_rcb == NULL) + { + p_rcb = l2cu_allocate_ble_rcb(vpsm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s No BLE RCB available, PSM: 0x%04x vPSM: 0x%04x", + __func__, psm, vpsm); + return 0; + } + } + + p_rcb->api = *p_cb_info; + p_rcb->real_psm = psm; + + return vpsm; +} + +/******************************************************************************* +** +** Function L2CA_DeregisterLECoc +** +** Description Other layers call this function to de-register for L2CAP +** Connection Oriented Channel. +** +** Returns void +** +*******************************************************************************/ +void L2CA_DeregisterLECoc(UINT16 psm) +{ + L2CAP_TRACE_API("%s called for PSM: 0x%04x", __func__, psm); + + tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s PSM: 0x%04x not found for deregistration", __func__, psm); + return; + } + + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + for (int i = 0; i < MAX_L2CAP_LINKS; i++, p_lcb++) + { + if (!p_lcb->in_use || p_lcb->transport != BT_TRANSPORT_LE) + continue; + + tL2C_CCB *p_ccb = p_lcb->ccb_queue.p_first_ccb; + if ((p_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_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + } + + l2cu_release_rcb (p_rcb); +} + +/******************************************************************************* +** +** Function L2CA_ConnectLECocReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Parameters: PSM: L2CAP PSM for the connection +** BD address of the peer +** Local Coc configurations + +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg) +{ + L2CAP_TRACE_API("%s PSM: 0x%04x BDA: %02x:%02x:%02x:%02x:%02x:%02x", __func__, psm, + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING("%s BTU not ready", __func__); + return 0; + } + + /* Fail if the PSM is not registered */ + tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm); + if (p_rcb == NULL) + { + L2CAP_TRACE_WARNING("%s No BLE RCB, PSM: 0x%04x", __func__, psm); + return 0; + } + + /* First, see if we already have a le link to the remote */ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE); + if (p_lcb == NULL) + { + /* No link. Get an LCB and start link establishment */ + p_lcb = l2cu_allocate_lcb(p_bd_addr, FALSE, BT_TRANSPORT_LE); + if ((p_lcb == NULL) + /* currently use BR/EDR for ERTM mode l2cap connection */ + || (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) ) + { + L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: 0x%p", + __func__, psm, p_lcb); + return 0; + } + } + + /* Allocate a channel control block */ + tL2C_CCB *p_ccb = l2cu_allocate_ccb(p_lcb, 0); + if (p_ccb == NULL) + { + L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm); + return 0; + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + /* Save the configuration */ + if (p_cfg) + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + + /* If link is up, start the L2CAP connection */ + if (p_lcb->link_state == LST_CONNECTED) + { + if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) + { + L2CAP_TRACE_DEBUG("%s LE Link is up", __func__); + l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL); + } + } + + /* If link is disconnecting, save link info to retry after disconnect + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + else if (p_lcb->link_state == LST_DISCONNECTING) + { + L2CAP_TRACE_DEBUG("%s link disconnecting: RETRY LATER", __func__); + + /* Save ccb so it can be started after disconnect is finished */ + p_lcb->p_pending_ccb = p_ccb; + } + + L2CAP_TRACE_API("%s(psm: 0x%04x) returned CID: 0x%04x", __func__, psm, p_ccb->local_cid); + + /* Return the local CID as our handle */ + return p_ccb->local_cid; +} + +/******************************************************************************* +** +** Function L2CA_ConnectLECocRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP COC connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg) +{ + L2CAP_TRACE_API("%s CID: 0x%04x Result: %d Status: %d BDA: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, lcid, result, status, + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + + /* First, find the link control block */ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE); + if (p_lcb == NULL) + { + /* No link. Get an LCB and start link establishment */ + L2CAP_TRACE_WARNING("%s no LCB", __func__); + return FALSE; + } + + /* Now, find the channel control block */ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); + if (p_ccb == NULL) + { + L2CAP_TRACE_WARNING("%s no CCB", __func__); + return FALSE; + } + + /* The IDs must match */ + if (p_ccb->remote_id != id) + { + L2CAP_TRACE_WARNING("%s bad id. Expected: %d Got: %d", __func__, p_ccb->remote_id, id); + return FALSE; + } + + if (p_cfg) + memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + + if (result == L2CAP_CONN_OK) + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); + else + { + tL2C_CONN_INFO conn_info; + memcpy(conn_info.bd_addr, p_bd_addr, BD_ADDR_LEN); + conn_info.l2cap_result = result; + conn_info.l2cap_status = status; + l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function L2CA_GetPeerLECocConfig +** +** Description Get a peers configuration for LE Connection Oriented Channel. +** +** Parameters: local channel id +** Pointers to peers configuration storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg) +{ + L2CAP_TRACE_API ("%s CID: 0x%04x", __func__, lcid); + + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + if (p_ccb == NULL) + { + L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid); + return FALSE; + } + + if (peer_cfg != NULL) + memcpy(peer_cfg, &p_ccb->peer_conn_cfg, sizeof(tL2CAP_LE_CFG_INFO)); + + return TRUE; +} + + #if (L2CAP_NUM_FIXED_CHNLS > 0) /******************************************************************************* diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/bluedroid/stack/l2cap/l2c_ble.c index 48f3a5d0c..9f180fe04 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ble.c @@ -1203,4 +1203,239 @@ UINT32 CalConnectParamTimeout(tL2C_LCB *p_lcb) return timeout; } +/******************************************************************************* +** +** Function l2cble_credit_based_conn_req +** +** Description This function sends LE Credit Based Connection Request for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb) +{ + if (!p_ccb) + return; + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_conn_req (p_ccb); + return; +} + +/******************************************************************************* +** +** Function l2cble_credit_based_conn_res +** +** Description This function sends LE Credit Based Connection Response for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result) +{ + if (!p_ccb) + return; + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_conn_res (p_ccb, result); + return; +} + +/******************************************************************************* +** +** Function l2cble_send_flow_control_credit +** +** Description This function sends flow control credits for +** LE connection oriented channels. +** +** Returns void +** +*******************************************************************************/ +void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value) +{ + if (!p_ccb) + return; + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_flow_control_credit(p_ccb, credit_value); + return; + +} + +/******************************************************************************* +** +** Function l2cble_send_peer_disc_req +** +** Description This function sends disconnect request +** to the peer LE device +** +** Returns void +** +*******************************************************************************/ +void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb) +{ + L2CAP_TRACE_DEBUG ("%s",__func__); + if (!p_ccb) + return; + + if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) + { + L2CAP_TRACE_WARNING ("LE link doesn't exist"); + return; + } + + l2cu_send_peer_ble_credit_based_disconn_req(p_ccb); + return; +} + +/******************************************************************************* +** +** Function l2cble_sec_comp +** +** Description This function is called when security procedure for an LE COC +** link is done +** +** Returns void +** +*******************************************************************************/ +void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bda, BT_TRANSPORT_LE); + tL2CAP_SEC_DATA *p_buf = NULL; + UINT8 sec_flag; + UINT8 sec_act; + + if (!p_lcb) + { + L2CAP_TRACE_WARNING ("%s security complete for unknown device", __func__); + return; + } + + sec_act = p_lcb->sec_act; + p_lcb->sec_act = 0; + + if (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + if (!p_buf) + { + L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", + __func__); + return; + } + + if (status != BTM_SUCCESS) + { + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } + else + { + if (sec_act == BTM_SEC_ENCRYPT_MITM) + { + BTM_GetSecurityFlagsByTransport(p_bda, &sec_flag, transport); + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + else + { + L2CAP_TRACE_DEBUG ("%s MITM Protection Not present", __func__); + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, + BTM_FAILED_ON_SECURITY); + } + } + else + { + L2CAP_TRACE_DEBUG ("%s MITM Protection not required sec_act = %d", + __func__, p_lcb->sec_act); + + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + } + } + } + else + { + L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", __func__); + return; + } + osi_free(p_buf); + + while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + + if (status != BTM_SUCCESS) + (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status); + else + l2ble_sec_access_req(p_bda, p_buf->psm, p_buf->is_originator, + p_buf->p_callback, p_buf->p_ref_data); + + osi_free(p_buf); + } +} + +/******************************************************************************* +** +** Function l2ble_sec_access_req +** +** Description This function is called by LE COC link to meet the +** security requirement for the link +** +** Returns TRUE - security procedures are started +** FALSE - failure +** +*******************************************************************************/ +BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data) +{ + L2CAP_TRACE_DEBUG ("%s", __func__); + BOOLEAN status; + tL2C_LCB *p_lcb = NULL; + + if (!p_callback) + { + L2CAP_TRACE_ERROR("%s No callback function", __func__); + return FALSE; + } + + p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + + if (!p_lcb) + { + L2CAP_TRACE_ERROR ("%s Security check for unknown device", __func__); + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_UNKNOWN_ADDR); + return FALSE; + } + + tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) osi_malloc((UINT16)sizeof(tL2CAP_SEC_DATA)); + if (!p_buf) + { + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_NO_RESOURCES); + return FALSE; + } + + p_buf->psm = psm; + p_buf->is_originator = is_originator; + p_buf->p_callback = p_callback; + p_buf->p_ref_data = p_ref_data; + fixed_queue_enqueue(p_lcb->le_sec_pending_q, p_buf); + status = btm_ble_start_sec_check(bd_addr, psm, is_originator, &l2cble_sec_comp, p_ref_data); + + return status; +} + #endif /* (BLE_INCLUDED == TRUE) */ diff --git a/components/bt/bluedroid/stack/l2cap/l2c_main.c b/components/bt/bluedroid/stack/l2cap/l2c_main.c index f5ec21703..a4c5c7e5a 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_main.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_main.c @@ -122,6 +122,7 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) tL2C_LCB *p_lcb; tL2C_CCB *p_ccb = NULL; UINT16 l2cap_len, rcv_cid, psm; + UINT16 credit; /* Extract the handle */ STREAM_TO_UINT16 (handle, p); @@ -275,6 +276,20 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) if (p_ccb == NULL) { osi_free (p_msg); } else { + if (p_lcb->transport == BT_TRANSPORT_LE) { + // Got a pkt, valid send out credits to the peer device + credit = L2CAP_LE_DEFAULT_CREDIT; + L2CAP_TRACE_DEBUG("%s Credits received %d",__func__, credit); + if((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) { + /* we have received credits more than max coc credits, + * so disconnecting the Le Coc Channel + */ + l2cble_send_peer_disc_req (p_ccb); + } else { + p_ccb->peer_conn_cfg.credits += credit; + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + } /* Basic mode packets go straight to the state machine */ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { #if (CLASSIC_BT_INCLUDED == TRUE) diff --git a/components/bt/bluedroid/stack/l2cap/l2c_utils.c b/components/bt/bluedroid/stack/l2cap/l2c_utils.c index b6514b23b..68bc2b58d 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_utils.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_utils.c @@ -71,6 +71,7 @@ tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPOR #if (BLE_INCLUDED == TRUE) p_lcb->transport = transport; p_lcb->tx_data_len = controller_get_interface()->get_ble_default_data_packet_length(); + p_lcb->le_sec_pending_q = fixed_queue_new(SIZE_MAX); if (transport == BT_TRANSPORT_LE) { l2cb.num_ble_links_active++; @@ -228,6 +229,20 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb) (*p_cb) (L2CAP_PING_RESULT_NO_LINK); } + + /* Check and release all the LE COC connections waiting for security */ + if (p_lcb->le_sec_pending_q) + { + while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q)) + { + tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q); + if (p_buf->p_callback) + p_buf->p_callback(p_lcb->remote_bd_addr, p_lcb->transport, p_buf->p_ref_data, BTM_DEV_RESET); + osi_free(p_buf); + } + fixed_queue_free(p_lcb->le_sec_pending_q, NULL); + p_lcb->le_sec_pending_q = NULL; + } } @@ -1720,6 +1735,37 @@ tL2C_RCB *l2cu_allocate_rcb (UINT16 psm) return (NULL); } +/******************************************************************************* +** +** Function l2cu_allocate_ble_rcb +** +** Description Look through the BLE Registration Control Blocks for a free +** one. +** +** Returns Pointer to the BLE RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (!p_rcb->in_use) + { + p_rcb->in_use = TRUE; + p_rcb->psm = psm; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; +#endif + return (p_rcb); + } + } + + /* If here, no free RCB found */ + return (NULL); +} /******************************************************************************* ** @@ -1791,6 +1837,32 @@ tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm) return (NULL); } +/******************************************************************************* +** +** Function l2cu_find_ble_rcb_by_psm +** +** Description Look through the BLE Registration Control Blocks to see if +** anyone registered to handle the PSM in question +** +** Returns Pointer to the BLE RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if ((p_rcb->in_use) && (p_rcb->psm == psm)) + return (p_rcb); + } + + /* If here, no match found */ + return (NULL); +} + + /******************************************************************************* ** @@ -2841,6 +2913,215 @@ 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); } +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_req +** +** Description Build and send a BLE packet to establish LE connection oriented +** L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + UINT16 mtu; + UINT16 mps; + UINT16 initial_credit; + + if (!p_ccb) + return; + p_lcb = p_ccb->p_lcb; + + /* 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_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_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; + + mtu = p_ccb->local_conn_cfg.mtu; + mps = p_ccb->local_conn_cfg.mps; + initial_credit = p_ccb->local_conn_cfg.credits; + + L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_req PSM:0x%04x local_cid:%d\ + mtu:%d mps:%d initial_credit:%d", p_ccb->p_rcb->real_psm,\ + p_ccb->local_cid, mtu, mps, initial_credit); + + UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm); + UINT16_TO_STREAM (p, p_ccb->local_cid); + UINT16_TO_STREAM (p, mtu); + UINT16_TO_STREAM (p, mps); + UINT16_TO_STREAM (p, initial_credit); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_reject_ble_connection +** +** Description Build and send an L2CAP "Credit based connection res" message +** to the peer. This function is called for non-success cases. +** +** Returns void +** +*******************************************************************************/ +void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, rem_id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_reject_ble_connection - 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); /* Local CID of 0 */ + UINT16_TO_STREAM (p, 0); /* MTU */ + UINT16_TO_STREAM (p, 0); /* MPS */ + UINT16_TO_STREAM (p, 0); /* initial credit */ + UINT16_TO_STREAM (p, result); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_res +** +** Description Build and send an L2CAP "Credit based connection res" message +** to the peer. This function is called in case of success. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_res"); + if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN, + L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, p_ccb->remote_id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_res - 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); /* Local CID */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mtu); /* MTU */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mps); /* MPS */ + UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.credits); /* initial credit */ + UINT16_TO_STREAM (p, result); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_flow_control_credit +** +** Description Build and send a BLE packet to give credits to peer device +** for LE connection oriented L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + + if (!p_ccb) + return; + p_lcb = p_ccb->p_lcb; + + /* 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_lcb, L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN, + L2CAP_CMD_BLE_FLOW_CTRL_CREDIT, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_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->local_cid); + UINT16_TO_STREAM (p, credit_value); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_credit_based_conn_req +** +** Description Build and send a BLE packet to disconnect LE connection oriented +** L2CAP channel. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + tL2C_LCB *p_lcb = NULL; + L2CAP_TRACE_DEBUG ("%s",__func__); + + if (!p_ccb) + return; + p_lcb = p_ccb->p_lcb; + + /* 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_lcb, L2CAP_DISC_REQ_LEN, + L2CAP_CMD_DISC_REQ, p_lcb->id)) == NULL ) + { + L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_disconn_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->remote_cid); + UINT16_TO_STREAM (p,p_ccb->local_cid); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + #endif /* BLE_INCLUDED == TRUE */