diff options
Diffstat (limited to 'stack/gap/gap_conn.c')
-rw-r--r-- | stack/gap/gap_conn.c | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/stack/gap/gap_conn.c b/stack/gap/gap_conn.c new file mode 100644 index 000000000..43765b767 --- /dev/null +++ b/stack/gap/gap_conn.c @@ -0,0 +1,1270 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2013 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. + * + ******************************************************************************/ + + +#include "bt_target.h" +#include "btu.h" +#include "gap_int.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include <string.h> +#if GAP_CONN_INCLUDED == TRUE +#include "btm_int.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 gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id); +static void gap_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested); + +static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid); +static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle); +static tGAP_CCB *gap_allocate_ccb (void); +static void gap_release_ccb (tGAP_CCB *p_ccb); + +/******************************************************************************* +** +** Function gap_conn_init +** +** Description This function is called to initialize GAP connection management +** +** Returns void +** +*******************************************************************************/ +void gap_conn_init (void) +{ +#if AMP_INCLUDED == TRUE + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind; + gap_cb.conn.reg_info.pAMP_ConnectCfm_Cb = gap_connect_cfm; + gap_cb.conn.reg_info.pAMP_ConnectPnd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_ConfigInd_Cb = gap_config_ind; + gap_cb.conn.reg_info.pAMP_ConfigCfm_Cb = gap_config_cfm; + gap_cb.conn.reg_info.pAMP_DisconnectInd_Cb = gap_disconnect_ind; + gap_cb.conn.reg_info.pAMP_DisconnectCfm_Cb = NULL; + gap_cb.conn.reg_info.pAMP_QoSViolationInd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_DataInd_Cb = gap_data_ind; + gap_cb.conn.reg_info.pAMP_CongestionStatus_Cb = gap_congestion_ind; + gap_cb.conn.reg_info.pAMP_TxComplete_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveInd_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveRsp_Cb = NULL; + gap_cb.conn.reg_info.pAMP_MoveCfm_Cb = NULL; //gap_move_cfm + gap_cb.conn.reg_info.pAMP_MoveCfmRsp_Cb = NULL; //gap_move_cfm_rsp + +#else + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind; + gap_cb.conn.reg_info.pL2CA_ConnectCfm_Cb = gap_connect_cfm; + gap_cb.conn.reg_info.pL2CA_ConnectPnd_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_ConfigInd_Cb = gap_config_ind; + gap_cb.conn.reg_info.pL2CA_ConfigCfm_Cb = gap_config_cfm; + gap_cb.conn.reg_info.pL2CA_DisconnectInd_Cb = gap_disconnect_ind; + gap_cb.conn.reg_info.pL2CA_DisconnectCfm_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_QoSViolationInd_Cb = NULL; + gap_cb.conn.reg_info.pL2CA_DataInd_Cb = gap_data_ind; + gap_cb.conn.reg_info.pL2CA_CongestionStatus_Cb = gap_congestion_ind; + gap_cb.conn.reg_info.pL2CA_TxComplete_Cb = NULL; +#endif +} + + +/******************************************************************************* +** +** Function GAP_ConnOpen +** +** Description This function is called to open an L2CAP connection. +** +** Parameters: is_server - If TRUE, the connection is not created +** but put into a "listen" mode waiting for +** the remote side to connect. +** +** service_id - Unique service ID from +** BTM_SEC_SERVICE_FIRST_EMPTY (6) +** to BTM_SEC_MAX_SERVICE_RECORDS (32) +** +** p_rem_bda - Pointer to remote BD Address. +** If a server, and we don't care about the +** remote BD Address, then NULL should be passed. +** +** psm - the PSM used for the connection +** +** p_config - Optional pointer to configuration structure. +** If NULL, the default GAP configuration will +** be used. +** +** security - security flags +** chan_mode_mask - (GAP_FCR_CHAN_OPT_BASIC, GAP_FCR_CHAN_OPT_ERTM, GAP_FCR_CHAN_OPT_STREAM) +** +** p_cb - Pointer to callback function for events. +** +** Returns handle of the connection if successful, else GAP_INVALID_HANDLE +** +*******************************************************************************/ +UINT16 GAP_ConnOpen (char *p_serv_name, UINT8 service_id, BOOLEAN is_server, + BD_ADDR p_rem_bda, UINT16 psm, tL2CAP_CFG_INFO *p_cfg, + UINT16 security, UINT8 chan_mode_mask, tGAP_CONN_CALLBACK *p_cb) +{ + tGAP_CCB *p_ccb; + UINT16 cid; + tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}}; + + GAP_TRACE_EVENT0 ("GAP_CONN - Open Request"); + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = gap_allocate_ccb()) == NULL) + return (GAP_INVALID_HANDLE); + + /* If caller specified a BD address, save it */ + if (p_rem_bda) + { + /* the bd addr is not BT_BD_ANY, then a bd address was specified */ + if (memcmp (p_rem_bda, BT_BD_ANY, BD_ADDR_LEN)) + p_ccb->rem_addr_specified = TRUE; + + memcpy (&p_ccb->rem_dev_address[0], p_rem_bda, BD_ADDR_LEN); + } + else if (!is_server) + { + /* remore addr is not specified and is not a server -> bad */ + return (GAP_INVALID_HANDLE); + } + + /* A client MUST have specified a bd addr to connect with */ + if (!p_ccb->rem_addr_specified && !is_server) + { + gap_release_ccb (p_ccb); + GAP_TRACE_ERROR0 ("GAP ERROR: Client must specify a remote BD ADDR to connect to!"); + return (GAP_INVALID_HANDLE); + } + + /* Check if configuration was specified */ + if (p_cfg) + p_ccb->cfg = *p_cfg; + + p_ccb->p_callback = p_cb; + + /* If originator, use a dynamic PSM */ +#if AMP_INCLUDED == TRUE + if (!is_server) + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = NULL; + else + gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind; +#else + if (!is_server) + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = NULL; + else + gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind; +#endif + + /* Register the PSM with L2CAP */ + if ((p_ccb->psm = L2CA_REGISTER (psm, &gap_cb.conn.reg_info, AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE)) == 0) + { + GAP_TRACE_ERROR1 ("GAP_ConnOpen: Failure registering PSM 0x%04x", psm); + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + + /* Register with Security Manager for the specific security level */ + p_ccb->service_id = service_id; + if (!BTM_SetSecurityLevel ((UINT8)!is_server, p_serv_name, p_ccb->service_id, security, p_ccb->psm, 0, 0)) + { + GAP_TRACE_ERROR0 ("GAP_CONN - Security Error"); + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + + /* Fill in eL2CAP parameter data */ + if( p_ccb->cfg.fcr_present ) + { + p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode; + p_ccb->ertm_info.user_rx_pool_id = GAP_DATA_POOL_ID; + p_ccb->ertm_info.user_tx_pool_id = GAP_DATA_POOL_ID; + p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_DEFAULT_ERM_POOL_ID; + p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_DEFAULT_ERM_POOL_ID; + } + + /* optional FCR channel modes */ + p_ccb->ertm_info.allowed_modes = (chan_mode_mask) ? chan_mode_mask : (UINT8)L2CAP_FCR_CHAN_OPT_BASIC; + + if (is_server) + { + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; /* assume btm/l2cap would handle it */ + p_ccb->con_state = GAP_CCB_STATE_LISTENING; + return (p_ccb->gap_handle); + } + else + { + /* We are the originator of this connection */ + p_ccb->con_flags = GAP_CCB_FLAGS_IS_ORIG; + + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_ccb->con_state = GAP_CCB_STATE_CONN_SETUP; + + /* mark security done flag, when security is not required */ + if ((security & (BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT) ) == 0) + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; + + /* Check if L2CAP started the connection process */ + if (p_rem_bda && ((cid = L2CA_CONNECT_REQ (p_ccb->psm, p_rem_bda, &p_ccb->ertm_info, &bt_uuid)) != 0)) + { + p_ccb->connection_id = cid; + return (p_ccb->gap_handle); + } + else + { + gap_release_ccb (p_ccb); + return (GAP_INVALID_HANDLE); + } + } +} + + +/******************************************************************************* +** +** Function GAP_ConnClose +** +** Description This function is called to close a connection. +** +** Parameters: handle - Handle of the connection returned by GAP_ConnOpen +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnClose (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + GAP_TRACE_EVENT1 ("GAP_CONN - close handle: 0x%x", gap_handle); + + if (p_ccb) + { + /* Check if we have a connection ID */ + if (p_ccb->con_state != GAP_CCB_STATE_LISTENING) + L2CA_DISCONNECT_REQ (p_ccb->connection_id); + + gap_release_ccb (p_ccb); + + return (BT_PASS); + } + + return (GAP_ERR_BAD_HANDLE); +} + + + +/******************************************************************************* +** +** Function GAP_ConnReadData +** +** Description Normally not GKI aware application will call this function +** after receiving GAP_EVT_RXDATA event. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + BT_HDR *p_buf; + UINT16 copy_len; + + if (!p_ccb) + return (GAP_ERR_BAD_HANDLE); + + *p_len = 0; + + p_buf = (BT_HDR *)GKI_getfirst (&p_ccb->rx_queue); + if (!p_buf) + return (GAP_NO_DATA_AVAIL); + + GKI_disable(); + + while (max_len && p_buf) + { + copy_len = (p_buf->len > max_len)?max_len:p_buf->len; + max_len -= copy_len; + *p_len += copy_len; + if (p_data) + { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, copy_len); + p_data += copy_len; + } + + if (p_buf->len > copy_len) + { + p_buf->offset += copy_len; + p_buf->len -= copy_len; + break; + } + else + { + if (max_len) + { + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + GKI_freebuf (GKI_dequeue (&p_ccb->rx_queue)); + } + } + + p_ccb->rx_queue_size -= *p_len; + + GKI_enable(); + + GAP_TRACE_EVENT2 ("GAP_ConnReadData - rx_queue_size left=%d, *p_len=%d", + p_ccb->rx_queue_size, *p_len); + + return (BT_PASS); +} + +/******************************************************************************* +** +** Function GAP_GetRxQueueCnt +** +** Description This function return number of bytes on the rx queue. +** +** Parameters: handle - Handle returned in the GAP_ConnOpen +** p_rx_queue_count - Pointer to return queue count in. +** +** +*******************************************************************************/ +int GAP_GetRxQueueCnt (UINT16 handle, UINT32 *p_rx_queue_count) +{ + tGAP_CCB *p_ccb; + int rc = BT_PASS; + + /* Check that handle is valid */ + if (handle < GAP_MAX_CONNECTIONS) + { + p_ccb = &gap_cb.conn.ccb_pool[handle]; + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) + { + *p_rx_queue_count = p_ccb->rx_queue_size; + } + else + rc = GAP_INVALID_HANDLE; + } + else + rc = GAP_INVALID_HANDLE; + + GAP_TRACE_EVENT2 ("GAP_GetRxQueueCnt - rc = 0x%04x, rx_queue_count=%d", + rc , *p_rx_queue_count); + + return (rc); +} + +/******************************************************************************* +** +** Function GAP_ConnBTRead +** +** Description Bluetooth aware applications will call this function after receiving +** GAP_EVT_RXDATA event. +** +** Parameters: handle - Handle of the connection returned in the Open +** pp_buf - pointer to address of buffer with data, +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_NO_DATA_AVAIL - no data available +** +*******************************************************************************/ +UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + BT_HDR *p_buf; + + if (!p_ccb) + return (GAP_ERR_BAD_HANDLE); + + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->rx_queue); + + if (p_buf) + { + *pp_buf = p_buf; + + p_ccb->rx_queue_size -= p_buf->len; + return (BT_PASS); + } + else + { + *pp_buf = NULL; + return (GAP_NO_DATA_AVAIL); + } +} + + +/******************************************************************************* +** +** Function GAP_ConnBTWrite +** +** Description Bluetooth Aware applications can call this function to write data. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_buf - pointer to address of buffer with data, +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_INVALID_BUF_OFFSET - buffer offset is invalid +*******************************************************************************/ +UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + if (!p_ccb) + { + GKI_freebuf (p_buf); + return (GAP_ERR_BAD_HANDLE); + } + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) + { + GKI_freebuf (p_buf); + return (GAP_ERR_BAD_STATE); + } + + if (p_buf->offset < L2CAP_MIN_OFFSET) + { + GKI_freebuf (p_buf); + return (GAP_ERR_BUF_OFFSET); + } + + GKI_enqueue (&p_ccb->tx_queue, p_buf); + + if (p_ccb->is_congested) + { + return (BT_PASS); + } + + /* Send the buffer through L2CAP */ +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + gap_send_event (gap_handle); +#else + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL) + { + UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) + { + p_ccb->is_congested = TRUE; + break; + } + else if (status != L2CAP_DW_SUCCESS) + return (GAP_ERR_BAD_STATE); + } +#endif + return (BT_PASS); +} + + +/******************************************************************************* +** +** Function GAP_ConnWriteData +** +** Description Normally not GKI aware application will call this function +** to send data to the connection. +** +** Parameters: handle - Handle of the connection returned in the Open +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +** Returns BT_PASS - data read +** GAP_ERR_BAD_HANDLE - invalid handle +** GAP_ERR_BAD_STATE - connection not established +** GAP_CONGESTION - system is congested +** +*******************************************************************************/ +UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + BT_HDR *p_buf; + + *p_len = 0; + + if (!p_ccb) + return (GAP_ERR_BAD_HANDLE); + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) + return (GAP_ERR_BAD_STATE); + + while (max_len) + { + if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + { + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (p_ccb->ertm_info.user_tx_pool_id)) == NULL) + return (GAP_ERR_CONGESTED); + } + else + { + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (GAP_DATA_POOL_ID)) == NULL) + return (GAP_ERR_CONGESTED); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = (p_ccb->rem_mtu_size < max_len) ? p_ccb->rem_mtu_size : max_len; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len); + + *p_len += p_buf->len; + max_len -= p_buf->len; + p_data += p_buf->len; + + GAP_TRACE_EVENT1 ("GAP_WriteData %d bytes", p_buf->len); + + GKI_enqueue (&p_ccb->tx_queue, p_buf); + } + + if (p_ccb->is_congested) + { + return (BT_PASS); + } + + /* Send the buffer through L2CAP */ +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + gap_send_event (gap_handle); +#else + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL) + { + UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) + { + p_ccb->is_congested = TRUE; + break; + } + else if (status != L2CAP_DW_SUCCESS) + return (GAP_ERR_BAD_STATE); + } +#endif + return (BT_PASS); +} + + +/******************************************************************************* +** +** Function GAP_ConnReconfig +** +** Description Applications can call this function to reconfigure the connection. +** +** Parameters: handle - Handle of the connection +** p_cfg - Pointer to new configuration +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnReconfig (UINT16 gap_handle, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + if (!p_ccb) + return (GAP_ERR_BAD_HANDLE); + + p_ccb->cfg = *p_cfg; + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) + L2CA_CONFIG_REQ (p_ccb->connection_id, p_cfg); + + return (BT_PASS); +} + + + +/******************************************************************************* +** +** Function GAP_ConnSetIdleTimeout +** +** Description Higher layers call this function to set the idle timeout for +** a connection, or for all future connections. The "idle timeout" +** is the amount of time that a connection can remain up with +** no L2CAP channels on it. A timeout of zero means that the +** connection will be torn down immediately when the last channel +** is removed. A timeout of 0xFFFF means no timeout. Values are +** in seconds. +** +** Parameters: handle - Handle of the connection +** timeout - in secs +** 0 = immediate disconnect when last channel is removed +** 0xFFFF = no idle timeout +** +** Returns BT_PASS - config process started +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT16 GAP_ConnSetIdleTimeout (UINT16 gap_handle, UINT16 timeout) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) + return (GAP_ERR_BAD_HANDLE); + + if (L2CA_SetIdleTimeout (p_ccb->connection_id, timeout, FALSE)) + return (BT_PASS); + else + return (GAP_ERR_BAD_HANDLE); +} + + + +/******************************************************************************* +** +** Function GAP_ConnGetRemoteAddr +** +** Description This function is called to get the remote BD address +** of a connection. +** +** Parameters: handle - Handle of the connection returned by GAP_ConnOpen +** +** Returns BT_PASS - closed OK +** GAP_ERR_BAD_HANDLE - invalid handle +** +*******************************************************************************/ +UINT8 *GAP_ConnGetRemoteAddr (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle); + + GAP_TRACE_EVENT1 ("GAP_ConnGetRemoteAddr gap_handle = %d", gap_handle); + + if ((p_ccb) && (p_ccb->con_state > GAP_CCB_STATE_LISTENING)) + { + GAP_TRACE_EVENT6("GAP_ConnGetRemoteAddr bda :0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", \ + p_ccb->rem_dev_address[0],p_ccb->rem_dev_address[1],p_ccb->rem_dev_address[2], + p_ccb->rem_dev_address[3],p_ccb->rem_dev_address[4],p_ccb->rem_dev_address[5]); + return (p_ccb->rem_dev_address); + } + else + { + GAP_TRACE_EVENT0 ("GAP_ConnGetRemoteAddr return Error "); + return (NULL); + } +} + + +/******************************************************************************* +** +** Function GAP_ConnGetRemMtuSize +** +** Description Returns the remote device's MTU size +** +** Parameters: handle - Handle of the connection +** +** Returns UINT16 - maximum size buffer that can be transmitted to the peer +** +*******************************************************************************/ +UINT16 GAP_ConnGetRemMtuSize (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) + return (0); + + return (p_ccb->rem_mtu_size); +} + +/******************************************************************************* +** +** Function GAP_ConnGetL2CAPCid +** +** Description Returns the L2CAP channel id +** +** Parameters: handle - Handle of the connection +** +** Returns UINT16 - The L2CAP channel id +** 0, if error +** +*******************************************************************************/ +UINT16 GAP_ConnGetL2CAPCid (UINT16 gap_handle) +{ + tGAP_CCB *p_ccb; + + if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL) + return (0); + + return (p_ccb->connection_id); +} + + +/******************************************************************************* +** +** Function gap_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}}; + + /* See if we have a CCB listening for the connection */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if ((p_ccb->con_state == GAP_CCB_STATE_LISTENING) + && (p_ccb->psm == psm) + && ((p_ccb->rem_addr_specified == FALSE) + || (!memcmp (bd_addr, p_ccb->rem_dev_address, BD_ADDR_LEN)))) + break; + } + + if (xx == GAP_MAX_CONNECTIONS) + { + GAP_TRACE_WARNING0("*******"); + GAP_TRACE_WARNING0("WARNING: GAP Conn Indication for Unexpected Bd Addr...Disconnecting"); + GAP_TRACE_WARNING0("*******"); + + /* Disconnect because it is an unexpected connection */ + L2CA_DISCONNECT_REQ (l2cap_cid); + return; + } + + /* Transition to the next appropriate state, waiting for config setup. */ + p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->rem_dev_address[0], bd_addr, BD_ADDR_LEN); + p_ccb->connection_id = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_CONNECT_RSP (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK, &p_ccb->ertm_info, &bt_uuid); + + GAP_TRACE_EVENT1("GAP_CONN - Rcvd L2CAP conn ind, CID: 0x%x", p_ccb->connection_id); + + /* Send a Configuration Request. */ + L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg); +} + +/******************************************************************************* +** +** Function gap_checks_con_flags +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_checks_con_flags (tGAP_CCB *p_ccb) +{ + GAP_TRACE_EVENT1 ("gap_checks_con_flags conn_flags:0x%x, ", p_ccb->con_flags); + /* if all the required con_flags are set, report the OPEN event now */ + if ((p_ccb->con_flags & GAP_CCB_FLAGS_CONN_DONE) == GAP_CCB_FLAGS_CONN_DONE) + { + p_ccb->con_state = GAP_CCB_STATE_CONNECTED; + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_OPENED); + } +} + +/******************************************************************************* +** +** Function gap_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void gap_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tGAP_CCB *p_ccb = (tGAP_CCB *)p_ref_data; + + GAP_TRACE_EVENT3 ("gap_sec_check_complete conn_state:%d, conn_flags:0x%x, status:%d", + p_ccb->con_state, p_ccb->con_flags, res); + if (p_ccb->con_state == GAP_CCB_STATE_IDLE) + return; + + if (res == BTM_SUCCESS) + { + p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; + gap_checks_con_flags (p_ccb); + } + else + { + /* security failed - disconnect the channel */ + L2CA_DISCONNECT_REQ (p_ccb->connection_id); + } +} + +/******************************************************************************* +** +** Function gap_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void gap_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) + return; + + /* initiate security process, if needed */ + if ( (p_ccb->con_flags & GAP_CCB_FLAGS_SEC_DONE) == 0) + { + btm_sec_mx_access_request (p_ccb->rem_dev_address, p_ccb->psm, TRUE, + 0, 0, &gap_sec_check_complete, p_ccb); + } + + /* If the connection response contains success status, then */ + /* Transition to the next state and startup the timer. */ + if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == GAP_CCB_STATE_CONN_SETUP)) + { + p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP; + + /* Send a Configuration Request. */ + L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg); + } + else + { + /* Tell the user if he has a callback */ + if (p_ccb->p_callback) + (*p_ccb->p_callback) (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + + gap_release_ccb (p_ccb); + } +} + +/******************************************************************************* +** +** Function gap_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb; + UINT16 local_mtu_size; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) + return; + + /* Remember the remote MTU size */ + + if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + { + local_mtu_size = GKI_get_pool_bufsize (p_ccb->ertm_info.user_tx_pool_id) + - sizeof(BT_HDR) - L2CAP_MIN_OFFSET; + } + else + local_mtu_size = L2CAP_MTU_SIZE; + + if ((!p_cfg->mtu_present)||(p_cfg->mtu > local_mtu_size)) + { + p_ccb->rem_mtu_size = local_mtu_size; + } + else + p_ccb->rem_mtu_size = p_cfg->mtu; + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + p_cfg->fcs_present = FALSE; + + L2CA_CONFIG_RSP (l2cap_cid, p_cfg); + + p_ccb->con_flags |= GAP_CCB_FLAGS_HIS_CFG_DONE; + + gap_checks_con_flags (p_ccb); +} + + +/******************************************************************************* +** +** Function gap_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) + return; + + if (p_cfg->result == L2CAP_CFG_OK) + { + p_ccb->con_flags |= GAP_CCB_FLAGS_MY_CFG_DONE; + + + if (p_ccb->cfg.fcr_present) + p_ccb->cfg.fcr.mode = p_cfg->fcr.mode; + else + p_ccb->cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + + gap_checks_con_flags (p_ccb); + } + else + { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + gap_release_ccb (p_ccb); + } +} + + +/******************************************************************************* +** +** Function gap_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tGAP_CCB *p_ccb; + + GAP_TRACE_EVENT1 ("GAP_CONN - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) + return; + + if (ack_needed) + L2CA_DISCONNECT_RSP (l2cap_cid); + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + gap_release_ccb (p_ccb); +} + + +/******************************************************************************* +** +** Function gap_data_ind +** +** Description This function is called when data is received from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + tGAP_CCB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL) + { + GKI_freebuf (p_msg); + return; + } + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) + { + GKI_enqueue (&p_ccb->rx_queue, p_msg); + + p_ccb->rx_queue_size += p_msg->len; + /* + GAP_TRACE_EVENT2 ("gap_data_ind - rx_queue_size=%d, msg len=%d", + p_ccb->rx_queue_size, p_msg->len); + */ + + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_DATA_AVAIL); + } + else + { + GKI_freebuf (p_msg); + } +} + + +/******************************************************************************* +** +** Function gap_congestion_ind +** +** Description This is a callback function called by L2CAP when +** data L2CAP congestion status changes +** +*******************************************************************************/ +static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested) +{ + tGAP_CCB *p_ccb; + UINT16 event; + BT_HDR *p_buf; + UINT8 status; + + GAP_TRACE_EVENT2 ("GAP_CONN - Rcvd L2CAP Is Congested (%d), CID: 0x%x", + is_congested, lcid); + + /* Find CCB based on CID */ + if ((p_ccb = gap_find_ccb_by_cid (lcid)) == NULL) + return; + + p_ccb->is_congested = is_congested; + + event = (is_congested) ? GAP_EVT_CONN_CONGESTED : GAP_EVT_CONN_UNCONGESTED; + p_ccb->p_callback (p_ccb->gap_handle, event); + + if (!is_congested) + { + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL) + { + status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) + { + p_ccb->is_congested = TRUE; + break; + } + else if (status != L2CAP_DW_SUCCESS) + break; + } + } +} + + +/******************************************************************************* +** +** Function gap_find_ccb_by_cid +** +** Description This function searches the CCB table for an entry with the +** passed CID. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + + /* Look through each connection control block */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->connection_id == cid)) + return (p_ccb); + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_find_ccb_by_handle +** +** Description This function searches the CCB table for an entry with the +** passed handle. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle) +{ + tGAP_CCB *p_ccb; + + /* Check that handle is valid */ + if (handle < GAP_MAX_CONNECTIONS) + { + p_ccb = &gap_cb.conn.ccb_pool[handle]; + + if (p_ccb->con_state != GAP_CCB_STATE_IDLE) + return (p_ccb); + } + + /* If here, handle points to invalid connection */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_allocate_ccb +** +** Description This function allocates a new CCB. +** +** Returns CCB address, or NULL if none available. +** +*******************************************************************************/ +static tGAP_CCB *gap_allocate_ccb (void) +{ + UINT16 xx; + tGAP_CCB *p_ccb; + + /* Look through each connection control block for a free one */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if (p_ccb->con_state == GAP_CCB_STATE_IDLE) + { + memset (p_ccb, 0, sizeof (tGAP_CCB)); + + p_ccb->gap_handle = xx; + p_ccb->rem_mtu_size = L2CAP_MTU_SIZE; + + return (p_ccb); + } + } + + /* If here, no free CCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function gap_release_ccb +** +** Description This function releases a CCB. +** +** Returns void +** +*******************************************************************************/ +static void gap_release_ccb (tGAP_CCB *p_ccb) +{ + UINT16 xx; + UINT16 psm = p_ccb->psm; + UINT8 service_id = p_ccb->service_id; + + /* Drop any buffers we may be holding */ + p_ccb->rx_queue_size = 0; + + while (p_ccb->rx_queue.p_first) + GKI_freebuf (GKI_dequeue (&p_ccb->rx_queue)); + + while (p_ccb->tx_queue.p_first) + GKI_freebuf (GKI_dequeue (&p_ccb->tx_queue)); + + p_ccb->con_state = GAP_CCB_STATE_IDLE; + + /* If no-one else is using the PSM, deregister from L2CAP */ + for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->psm == psm)) + return; + } + + /* Free the security record for this PSM */ + BTM_SecClrService(service_id); + L2CA_DEREGISTER (psm); +} + +#if (GAP_CONN_POST_EVT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function gap_send_event +** +** Description Send BT_EVT_TO_GAP_MSG event to BTU task +** +** Returns None +** +*******************************************************************************/ +void gap_send_event (UINT16 gap_handle) +{ + BT_HDR *p_msg; + + if ((p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE)) != NULL) + { + p_msg->event = BT_EVT_TO_GAP_MSG; + p_msg->len = 0; + p_msg->offset = 0; + p_msg->layer_specific = gap_handle; + + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); + } + else + { + GAP_TRACE_ERROR0("Unable to allocate message buffer for event."); + } +} + +/******************************************************************************* +** +** Function gap_proc_btu_event +** +** Description Event handler for BT_EVT_TO_GAP_MSG event from BTU task +** +** Returns None +** +*******************************************************************************/ +void gap_proc_btu_event(BT_HDR *p_msg) +{ + tGAP_CCB *p_ccb = gap_find_ccb_by_handle (p_msg->layer_specific); + UINT8 status; + BT_HDR *p_buf; + + if (!p_ccb) + { + return; + } + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) + { + return; + } + + if (p_ccb->is_congested) + { + return; + } + + /* Send the buffer through L2CAP */ + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL) + { + status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf); + + if (status == L2CAP_DW_CONGESTED) + { + p_ccb->is_congested = TRUE; + break; + } + else if (status != L2CAP_DW_SUCCESS) + break; + } + +} +#endif /* (GAP_CONN_POST_EVT_INCLUDED == TRUE) */ +#endif /* GAP_CONN_INCLUDED */ |