summaryrefslogtreecommitdiffstats
path: root/stack/l2cap
diff options
context:
space:
mode:
Diffstat (limited to 'stack/l2cap')
-rw-r--r--stack/l2cap/l2c_api.c1800
-rw-r--r--stack/l2cap/l2c_ble.c585
-rw-r--r--stack/l2cap/l2c_csm.c1321
-rw-r--r--stack/l2cap/l2c_fcr.c2697
-rw-r--r--stack/l2cap/l2c_int.h757
-rw-r--r--stack/l2cap/l2c_link.c1703
-rw-r--r--stack/l2cap/l2c_main.c979
-rw-r--r--stack/l2cap/l2c_ucd.c1158
-rw-r--r--stack/l2cap/l2c_utils.c3397
9 files changed, 14397 insertions, 0 deletions
diff --git a/stack/l2cap/l2c_api.c b/stack/l2cap/l2c_api.c
new file mode 100644
index 0000000..476ded1
--- /dev/null
+++ b/stack/l2cap/l2c_api.c
@@ -0,0 +1,1800 @@
+/*****************************************************************************
+**
+** Name: l2c_api.c
+**
+** Description: This file contains the L2CAP API code
+**
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "btu.h"
+#include "btm_api.h"
+
+/*******************************************************************************
+**
+** Function L2CA_Register
+**
+** Description Other layers call this function to register for L2CAP
+** services.
+**
+** 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_ConnectReq(),
+** L2CA_ErtmConnectReq() and L2CA_Deregister()
+**
+*******************************************************************************/
+UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
+{
+ tL2C_RCB *p_rcb;
+ UINT16 vpsm = psm;
+
+ L2CAP_TRACE_API1 ("L2CAP - L2CA_Register() called for PSM: 0x%04x", 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_ConfigCfm_Cb)
+ || (!p_cb_info->pL2CA_ConfigInd_Cb)
+ || (!p_cb_info->pL2CA_DataInd_Cb)
+ || (!p_cb_info->pL2CA_DisconnectInd_Cb))
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - no cb registering PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Verify PSM is valid */
+ if (L2C_INVALID_PSM(psm))
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - invalid PSM value, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* 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 >= 0x1001) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL) )
+ {
+ for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2)
+ {
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
+ break;
+ }
+
+ L2CAP_TRACE_API2 ("L2CA_Register - Real PSM: 0x%04x Virtual PSM: 0x%04x", psm, vpsm);
+ }
+
+ /* If registration block already there, just overwrite it */
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL)
+ {
+ if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - no RCB available, PSM: 0x%04x vPSM: 0x%04x", psm, vpsm);
+ return (0);
+ }
+ }
+
+ p_rcb->api = *p_cb_info;
+ p_rcb->real_psm = psm;
+
+ return (vpsm);
+}
+
+
+
+/*******************************************************************************
+**
+** Function L2CA_Deregister
+**
+** Description Other layers call this function to de-register for L2CAP
+** services.
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_Deregister (UINT16 psm)
+{
+ tL2C_RCB *p_rcb;
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+ int ii;
+
+ L2CAP_TRACE_API1 ("L2CAP - L2CA_Deregister() called for PSM: 0x%04x", psm);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) != 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_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ }
+ }
+ l2cu_release_rcb (p_rcb);
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - PSM: 0x%04x not found for deregistration", psm);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_AllocatePSM
+**
+** Description Other layers call this function to find an unused PSM for L2CAP
+** services.
+**
+** Returns PSM to use.
+**
+*******************************************************************************/
+UINT16 L2CA_AllocatePSM(void)
+{
+ BOOLEAN done = FALSE;
+ UINT16 psm = l2cb.dyn_psm;
+
+ L2CAP_TRACE_API0( "L2CA_AllocatePSM");
+ while (!done)
+ {
+ psm += 2;
+ if (psm > 0xfeff)
+ {
+ psm = 0x1001;
+ }
+ else if (psm & 0x0100)
+ {
+ /* the upper byte must be even */
+ psm += 0x0100;
+ }
+
+ /* if psm is in range of reserved BRCM Aware features */
+ if ((BRCM_RESERVED_PSM_START <= psm)&&(psm <= BRCM_RESERVED_PSM_END))
+ continue;
+
+ /* make sure the newlly allocated psm is not used right now */
+ if ((l2cu_find_rcb_by_psm (psm)) == NULL)
+ done = TRUE;
+ }
+ l2cb.dyn_psm = psm;
+
+ return(psm);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectReq
+**
+** 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.
+**
+** Returns the CID of the connection, or 0 if it failed to start
+**
+*******************************************************************************/
+UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr)
+{
+ return L2CA_ErtmConnectReq (psm, p_bd_addr, NULL);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ErtmConnectReq
+**
+** 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.
+**
+** Returns the CID of the connection, or 0 if it failed to start
+**
+*******************************************************************************/
+UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_ertm_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API6 ("L2CA_ErtmConnectReq() PSM: 0x%04x BDA: %08x%04x p_ertm_info: 0x%08x allowed:0x%x preferred:%d", psm,
+ (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], p_ertm_info,
+ (p_ertm_info) ? p_ertm_info->allowed_modes : 0, (p_ertm_info) ? p_ertm_info->preferred_mode : 0);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP connect req - BTU not ready");
+ return (0);
+ }
+ /* Fail if the PSM is not registered */
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ if ( ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL)
+ || (l2cu_create_conn(p_lcb) == FALSE) )
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - conn not started for PSM: 0x%04x p_lcb: 0x%08x", psm, p_lcb);
+ return (0);
+ }
+ }
+
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ if (p_ertm_info)
+ {
+ p_ccb->ertm_info = *p_ertm_info;
+
+ /* Replace default indicators with the actual default pool */
+ if (p_ccb->ertm_info.fcr_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID;
+
+ if (p_ccb->ertm_info.fcr_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID;
+
+ if (p_ccb->ertm_info.user_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID;
+
+ if (p_ccb->ertm_info.user_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID;
+
+ p_ccb->max_rx_mtu = GKI_get_pool_bufsize (p_ertm_info->user_rx_pool_id) - (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN);
+ }
+
+ /* If link is up, start the L2CAP connection */
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ 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_DEBUG0 ("L2CAP API - link disconnecting: RETRY LATER");
+
+ /* Save ccb so it can be started after disconnect is finished */
+ p_lcb->p_pending_ccb = p_ccb;
+ }
+
+ L2CAP_TRACE_API2 ("L2CAP - L2CA_conn_req(psm: 0x%04x) returned CID: 0x%04x", psm, p_ccb->local_cid);
+
+ /* Return the local CID as our handle */
+ return (p_ccb->local_cid);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectRsp
+**
+** Description Higher layers call this function to accept an incoming
+** L2CAP connection, for which they had gotten an connect
+** indication callback.
+**
+** Returns TRUE for success, FALSE for failure
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, UINT16 status)
+{
+ return L2CA_ErtmConnectRsp (p_bd_addr, id, lcid, result, status, NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ErtmConnectRsp
+**
+** Description Higher layers call this function to accept an incoming
+** L2CAP connection, for which they had gotten an connect
+** indication callback.
+**
+** Returns TRUE for success, FALSE for failure
+**
+*******************************************************************************/
+BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result,
+ UINT16 status, tL2CAP_ERTM_INFO *p_ertm_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API6 ("L2CA_ErtmConnectRsp() CID: 0x%04x Result: %d Status: %d BDA: %08x%04x p_ertm_info:0x%08x",
+ lcid, result, status,
+ (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], p_ertm_info);
+
+ /* First, find the link control block */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_conn_rsp");
+ return (FALSE);
+ }
+
+ /* Now, find the channel control block */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_conn_rsp");
+ return (FALSE);
+ }
+
+ /* The IDs must match */
+ if (p_ccb->remote_id != id)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - bad id in L2CA_conn_rsp. Exp: %d Got: %d", p_ccb->remote_id, id);
+ return (FALSE);
+ }
+
+ if (p_ertm_info)
+ {
+ p_ccb->ertm_info = *p_ertm_info;
+
+ /* Replace default indicators with the actual default pool */
+ if (p_ccb->ertm_info.fcr_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID;
+
+ if (p_ccb->ertm_info.fcr_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID;
+
+ if (p_ccb->ertm_info.user_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID;
+
+ if (p_ccb->ertm_info.user_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID)
+ p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID;
+
+ p_ccb->max_rx_mtu = GKI_get_pool_bufsize (p_ertm_info->user_rx_pool_id) - (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN);
+ }
+
+ if (result == L2CAP_CONN_OK)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL);
+ }
+ else
+ {
+ tL2C_CONN_INFO conn_info;
+
+ conn_info.l2cap_result = result;
+ conn_info.l2cap_status = status;
+
+ if (result == L2CAP_CONN_PENDING)
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, &conn_info);
+ else
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ConfigReq
+**
+** Description Higher layers call this function to send configuration.
+**
+** Note: The FCR options of p_cfg are not used.
+**
+** Returns TRUE if configuration sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API5 ("L2CA_ConfigReq() CID 0x%04x: fcr_present:%d (mode %d) mtu_present:%d (%d)",
+ cid, p_cfg->fcr_present, p_cfg->fcr.mode, p_cfg->mtu_present, p_cfg->mtu);
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_cfg_req, CID: %d", cid);
+ return (FALSE);
+ }
+
+ /* We need to have at least one mode type common with the peer */
+ if (!l2c_fcr_adj_our_req_options(p_ccb, p_cfg))
+ return (FALSE);
+
+ /* Don't adjust FCR options if not used */
+ if ((!p_cfg->fcr_present)||(p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE))
+ {
+ /* FCR and FCS options are not used in basic mode */
+ p_cfg->fcs_present = FALSE;
+ p_cfg->ext_flow_spec_present = FALSE;
+
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) )
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - adjust MTU: %u too large", p_cfg->mtu);
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+ }
+ }
+
+ /* Save the adjusted configuration in case it needs to be used for renegotiation */
+ p_ccb->our_cfg = *p_cfg;
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_REQ, p_cfg);
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ConfigRsp
+**
+** Description Higher layers call this function to send a configuration
+** response.
+**
+** Returns TRUE if configuration response sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API6 ("L2CA_ConfigRsp() CID: 0x%04x Result: %d MTU present:%d Flush TO:%d FCR:%d FCS:%d",
+ cid, p_cfg->result, p_cfg->mtu_present, p_cfg->flush_to_present, p_cfg->fcr_present, p_cfg->fcs_present);
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_cfg_rsp, CID: %d", cid);
+ return (FALSE);
+ }
+
+ if ( (p_cfg->result == L2CAP_CFG_OK) || (p_cfg->result == L2CAP_CFG_PENDING) )
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP, p_cfg);
+ else
+ {
+ p_cfg->fcr_present = FALSE; /* FCR options already negotiated before this point */
+
+ /* Clear out any cached options that are being returned as an error (excluding FCR) */
+ if (p_cfg->mtu_present)
+ p_ccb->peer_cfg.mtu_present = FALSE;
+ if (p_cfg->flush_to_present)
+ p_ccb->peer_cfg.flush_to_present = FALSE;
+ if (p_cfg->qos_present)
+ p_ccb->peer_cfg.qos_present = FALSE;
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP_NEG, p_cfg);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_DisconnectReq
+**
+** Description Higher layers call this function to disconnect a channel.
+**
+** Returns TRUE if disconnect sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_DisconnectReq (UINT16 cid)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API1 ("L2CA_DisconnectReq() CID: 0x%04x", 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_WARNING1 ("L2CAP - no CCB for L2CA_disc_req, CID: %d", cid);
+ return (FALSE);
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DisconnectRsp
+**
+** Description Higher layers call this function to acknowledge the
+** disconnection of a channel.
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN L2CA_DisconnectRsp (UINT16 cid)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API1 ("L2CA_DisconnectRsp() CID: 0x%04x", 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_WARNING1 ("L2CAP - no CCB for L2CA_disc_rsp, CID: %d", cid);
+ return (FALSE);
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_Ping
+**
+** Description Higher layers call this function to send an echo request.
+**
+** Returns TRUE if echo request sent, else FALSE.
+**
+*******************************************************************************/
+BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_callback)
+{
+ tL2C_LCB *p_lcb;
+
+ L2CAP_TRACE_API6 ("L2CA_Ping() BDA: %02x-%02x-%02x-%02x-%02x-%02x",
+ 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())
+ return (FALSE);
+
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_ping");
+ return (FALSE);
+ }
+ if (l2cu_create_conn(p_lcb) == FALSE)
+ {
+ return (FALSE);
+ }
+
+ p_lcb->p_echo_rsp_cb = p_callback;
+
+ return (TRUE);
+ }
+
+ /* We only allow 1 ping outstanding at a time */
+ if (p_lcb->p_echo_rsp_cb != NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - rejected second L2CA_ping");
+ return (FALSE);
+ }
+
+ /* Have a link control block. If link is disconnecting, tell user to retry later */
+ if (p_lcb->link_state == LST_DISCONNECTING)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - L2CA_ping rejected - link disconnecting");
+ return (FALSE);
+ }
+
+ /* Save address of callback */
+ p_lcb->p_echo_rsp_cb = p_callback;
+
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
+ l2cu_send_peer_echo_req (p_lcb, NULL, 0);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_Echo
+**
+** Description Higher layers call this function to send an echo request
+** with application-specific data.
+**
+** Returns TRUE if echo request sent, else FALSE.
+**
+*******************************************************************************/
+BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback)
+{
+ tL2C_LCB *p_lcb;
+ UINT8 *pp;
+
+ L2CAP_TRACE_API2 ("L2CA_Echo() BDA: %08X%04X",
+ ((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])));
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp())
+ return (FALSE);
+
+ if ((memcmp(BT_BD_ANY, p_bd_addr, BD_ADDR_LEN) == 0) && (p_data == NULL))
+ {
+ /* Only register callback without sending message. */
+ l2cb.p_echo_data_cb = p_callback;
+ return TRUE;
+ }
+
+ /* We assume the upper layer will call this function only when the link is established. */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CA_Echo ERROR : link not established");
+ return FALSE;
+ }
+
+ if (p_lcb->link_state != LST_CONNECTED)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CA_Echo ERROR : link is not connected");
+ return FALSE;
+ }
+
+ /* Save address of callback */
+ l2cb.p_echo_data_cb = p_callback;
+
+ /* Set the pointer to the beginning of the data */
+ pp = (UINT8 *)(p_data + 1) + p_data->offset;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
+ l2cu_send_peer_echo_req (p_lcb, pp, p_data->len);
+
+ return (TRUE);
+
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetIdleTimeout
+**
+** 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.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This timeout takes effect after at least 1 channel has been
+** established and removed. L2CAP maintains its own timer from
+** whan a connection is established till the first channel is
+** set up.
+*******************************************************************************/
+BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, BOOLEAN is_global)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+
+ if (is_global)
+ {
+ l2cb.idle_timeout = timeout;
+ }
+ else
+ {
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_SetIdleTimeout, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_lcb = p_ccb->p_lcb;
+
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED))
+ p_lcb->idle_timeout = timeout;
+ else
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetIdleTimeoutByBdAddr
+**
+** Description Higher layers call this function to set the idle timeout for
+** a connection. 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.
+** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY,
+** then the idle timeouts for all active l2cap links will be
+** changed.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This timeout applies to all logical channels active on the
+** ACL link.
+*******************************************************************************/
+BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout)
+{
+ tL2C_LCB *p_lcb;
+
+ if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN))
+ {
+ p_lcb = l2cu_find_lcb_by_bd_addr( bd_addr );
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED))
+ p_lcb->idle_timeout = timeout;
+ else
+ return (FALSE);
+ }
+ else
+ {
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED))
+ {
+ p_lcb->idle_timeout = timeout;
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetTraceLevel
+**
+** Description This function sets the trace level for L2CAP. If called with
+** a value of 0xFF, it simply reads the current trace level.
+**
+** Returns the new (current) trace level
+**
+*******************************************************************************/
+UINT8 L2CA_SetTraceLevel (UINT8 new_level)
+{
+ if (new_level != 0xFF)
+ l2cb.l2cap_trace_level = new_level;
+
+ return (l2cb.l2cap_trace_level);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetDesireRole
+**
+** Description This function sets the desire role for L2CAP.
+** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on
+** HciCreateConnection.
+** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on
+** HciCreateConnection.
+**
+** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE),
+** the desire role is set to the new value. Otherwise, it is not changed.
+**
+** Returns the new (current) role
+**
+*******************************************************************************/
+UINT8 L2CA_SetDesireRole (UINT8 new_role)
+{
+ L2CAP_TRACE_API2 ("L2CA_SetDesireRole() new:x%x, disallow_switch:%d",
+ new_role, l2cb.disallow_switch);
+
+ if (L2CAP_ROLE_CHECK_SWITCH != (L2CAP_ROLE_CHECK_SWITCH & new_role))
+ {
+ /* do not process the allow_switch when both bits are set */
+ if (new_role & L2CAP_ROLE_ALLOW_SWITCH)
+ {
+ l2cb.disallow_switch = FALSE;
+ }
+ if (new_role & L2CAP_ROLE_DISALLOW_SWITCH)
+ {
+ l2cb.disallow_switch = TRUE;
+ }
+ }
+
+ if (new_role == HCI_ROLE_MASTER || new_role == HCI_ROLE_SLAVE)
+ l2cb.desire_role = new_role;
+
+ return (l2cb.desire_role);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_LocalLoopbackReq
+**
+** Description This function sets up a CID for local loopback
+**
+** Returns CID of 0 if none.
+**
+*******************************************************************************/
+UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API2 ("L2CA_LocalLoopbackReq() PSM: %d Handle: 0x%04x", psm, handle);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP loop req - BTU not ready");
+ return (0);
+ }
+
+ /* Fail if the PSM is not registered */
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm);
+ return (0);
+ }
+
+ if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_conn_req");
+ return (0);
+ }
+
+ p_lcb->link_state = LST_CONNECTED;
+ p_lcb->handle = handle;
+
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_conn_req");
+ return (0);
+ }
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->chnl_state = CST_OPEN;
+ p_ccb->remote_cid = p_ccb->local_cid;
+ p_ccb->config_done = CFG_DONE_MASK;
+
+ /* Return the local CID as our handle */
+ return (p_ccb->local_cid);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetAclPriority
+**
+** Description Sets the transmission priority for a channel.
+** (For initial implementation only two values are valid.
+** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH).
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority)
+{
+ L2CAP_TRACE_API6 ("L2CA_SetAclPriority() bdaddr: %02x%02x%02x%02x%04x, priority:%d",
+ bd_addr[0], bd_addr[1], bd_addr[2],
+ bd_addr[3], (bd_addr[4] << 8) + bd_addr[5], priority);
+
+ return (l2cu_set_acl_priority(bd_addr, priority, FALSE));
+}
+
+/*******************************************************************************
+**
+** Function L2CA_FlowControl
+**
+** Description Higher layers call this function to flow control a channel.
+**
+** data_enabled - TRUE data flows, FALSE data is stopped
+**
+** Returns TRUE if valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled)
+{
+ tL2C_CCB *p_ccb;
+ BOOLEAN on_off = !data_enabled;
+
+ L2CAP_TRACE_API2 ("L2CA_FlowControl(%d) CID: 0x%04x", on_off, 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_WARNING2 ("L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x data_enabled: %d", cid, data_enabled);
+ return (FALSE);
+ }
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE)
+ {
+ L2CAP_TRACE_EVENT1 ("L2CA_FlowControl() invalid mode:%d", p_ccb->peer_cfg.fcr.mode);
+ return (FALSE);
+ }
+ if (p_ccb->fcrb.local_busy != on_off)
+ {
+ p_ccb->fcrb.local_busy = on_off;
+
+ if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) )
+ {
+ if (on_off)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SendTestSFrame
+**
+** Description Higher layers call this function to send a test S-frame.
+**
+** Returns TRUE if valid Channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SendTestSFrame (UINT16 cid, UINT8 sup_type, UINT8 back_track)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API3 ("L2CA_SendTestSFrame() CID: 0x%04x Type: 0x%02x back_track: %u", cid, sup_type, back_track);
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid);
+ return (FALSE);
+ }
+
+ if ( (p_ccb->chnl_state != CST_OPEN) || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) )
+ return (FALSE);
+
+ p_ccb->fcrb.next_seq_expected -= back_track;
+
+ l2c_fcr_send_S_frame (p_ccb, (UINT16)(sup_type & 3), (UINT16)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT)));
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetTxPriority
+**
+** Description Sets the transmission priority for a channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API2 ("L2CA_SetTxPriority() CID: 0x%04x, priority:%d", cid, priority);
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_SetTxPriority, CID: %d", cid);
+ return (FALSE);
+ }
+
+ /* it will update the order of CCB in LCB by priority and update round robin service variables */
+ l2cu_change_pri_ccb (p_ccb, priority);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetChnlDataRate
+**
+** Description Sets the tx/rx data rate for a channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API3 ("L2CA_SetChnlDataRate() CID: 0x%04x, tx:%d, rx:%d", cid, tx, rx);
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_SetChnlDataRate, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_ccb->tx_data_rate = tx;
+ p_ccb->rx_data_rate = rx;
+
+ /* Adjust channel buffer allocation */
+ l2c_link_adjust_chnl_allocation ();
+
+ return(TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetFlushTimeout
+**
+** Description This function set the automatic flush time out in Baseband
+** for ACL-U packets.
+** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY
+** then the flush time out will be applied to all ACL link.
+** FlushTimeout: flush time out in ms
+** 0x0000 : No automatic flush
+** L2CAP_NO_RETRANSMISSION : No retransmission
+** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5)
+** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot).
+** Otherwise, return FALSE.
+** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This flush timeout applies to all logical channels active on the
+** ACL link.
+*******************************************************************************/
+BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 hci_flush_to;
+ UINT32 temp;
+
+ /* no automatic flush (infinite timeout) */
+ if (flush_tout == 0x0000)
+ {
+ hci_flush_to = flush_tout;
+ flush_tout = L2CAP_NO_AUTOMATIC_FLUSH;
+ }
+ /* no retransmission */
+ else if (flush_tout == L2CAP_NO_RETRANSMISSION)
+ {
+ /* not mandatory range for controller */
+ /* Packet is flushed before getting any ACK/NACK */
+ /* To do this, flush timeout should be 1 baseband slot */
+ hci_flush_to = flush_tout;
+ }
+ /* no automatic flush (infinite timeout) */
+ else if (flush_tout == L2CAP_NO_AUTOMATIC_FLUSH)
+ {
+ hci_flush_to = 0x0000;
+ }
+ else
+ {
+ /* convert L2CAP flush_to to 0.625 ms units, with round */
+ temp = (((UINT32)flush_tout * 8) + 3) / 5;
+
+ /* if L2CAP flush_to within range of HCI, set HCI flush timeout */
+ if (temp > HCI_MAX_AUTO_FLUSH_TOUT)
+ {
+ L2CAP_TRACE_WARNING1("WARNING L2CA_SetFlushTimeout timeout(0x%x) is out of range", flush_tout);
+ return FALSE;
+ }
+ else
+ {
+ hci_flush_to = (UINT16)temp;
+ }
+ }
+
+ if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN))
+ {
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr);
+
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED))
+ {
+ if (p_lcb->link_flush_tout != flush_tout)
+ {
+ p_lcb->link_flush_tout = flush_tout;
+
+ L2CAP_TRACE_API4 ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]",
+ flush_tout, bd_addr[3], bd_addr[4], bd_addr[5]);
+
+ if (!btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to))
+ return (FALSE);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING3 ("WARNING L2CA_SetFlushTimeout No lcb for bd_addr [...;%02x%02x%02x]",
+ bd_addr[3], bd_addr[4], bd_addr[5]);
+ return (FALSE);
+ }
+ }
+ else
+ {
+ int xx;
+ p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED))
+ {
+ if (p_lcb->link_flush_tout != flush_tout)
+ {
+ p_lcb->link_flush_tout = flush_tout;
+
+ L2CAP_TRACE_API4 ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]",
+ flush_tout, p_lcb->remote_bd_addr[3],
+ p_lcb->remote_bd_addr[4], p_lcb->remote_bd_addr[5]);
+
+ if (!btsnd_hcic_write_auto_flush_tout(p_lcb->handle, hci_flush_to))
+ return (FALSE);
+ }
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetPeerFeatures
+**
+** Description Get a peers features and fixed channel map
+**
+** Parameters: BD address of the peer
+** Pointers to features and channel mask storage area
+**
+** Return value: TRUE if peer is connected
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask)
+{
+ tL2C_LCB *p_lcb;
+
+ /* We must already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr)) == NULL)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CA_GetPeerFeatures() No BDA: %08x%04x",
+ (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3],
+ (bd_addr[4]<<8)+bd_addr[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API4 ("L2CA_GetPeerFeatures() BDA: %08x%04x ExtFea: 0x%08x Chnl_Mask[0]: 0x%02x",
+ (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3],
+ (bd_addr[4]<<8)+bd_addr[5], p_lcb->peer_ext_fea, p_lcb->peer_chnl_mask[0]);
+
+ *p_ext_feat = p_lcb->peer_ext_fea;
+
+ memcpy (p_chnl_mask, p_lcb->peer_chnl_mask, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetBDAddrbyHandle
+**
+** Description Get BD address for the given HCI handle
+**
+** Parameters: HCI handle
+** BD address of the peer
+**
+** Return value: TRUE if found lcb for the given handle, FALSE otherwise
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb = NULL;
+ BOOLEAN found_dev = FALSE;
+
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+ if (p_lcb)
+ {
+ found_dev = TRUE;
+ memcpy (bd_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+ }
+
+ return found_dev;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetChnlFcrMode
+**
+** Description Get the channel FCR mode
+**
+** Parameters: Local CID
+**
+** Return value: Channel mode
+**
+*******************************************************************************/
+UINT8 L2CA_GetChnlFcrMode (UINT16 lcid)
+{
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, lcid);
+
+ if (p_ccb)
+ {
+ L2CAP_TRACE_API1 ("L2CA_GetChnlFcrMode() returns mode %d", p_ccb->peer_cfg.fcr.mode);
+ return (p_ccb->peer_cfg.fcr.mode);
+ }
+
+ L2CAP_TRACE_API0 ("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE");
+ return (L2CAP_FCR_BASIC_MODE);
+}
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+/*******************************************************************************
+**
+** Function L2CA_RegisterFixedChannel
+**
+** Description Register a fixed channel.
+**
+** Parameters: Fixed Channel #
+** Channel Callbacks and config
+**
+** Return value: -
+**
+*******************************************************************************/
+BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg)
+{
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) )
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_RegisterFixedChannel() Invalid CID: 0x%04x", fixed_cid);
+
+ return (FALSE);
+ }
+
+ l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = *p_freg;
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectFixedChnl
+**
+** Description Connect an fixed signalling channel to a remote device.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+**
+** Return value: TRUE if connection started
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+#if BLE_INCLUDED == TRUE
+ UINT16 reason;
+#endif
+
+ L2CAP_TRACE_API3 ("L2CA_ConnectFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* Check CID is valid and registered */
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) )
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_ConnectFixedChnl() Invalid CID: 0x%04x", fixed_cid);
+ return (FALSE);
+ }
+
+ /* Fail if BT is not yet up */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - BTU not ready", fixed_cid);
+ return (FALSE);
+ }
+
+ /* If we already have a link to the remote, check if it supports that CID */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) != NULL)
+ {
+ if (!(p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)))
+ {
+ L2CAP_TRACE_EVENT3 ("L2CA_ConnectFixedChnl() CID: 0x%04x BDA: %08x%04x not supported", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return (FALSE);
+ }
+ /* Get a CCB and link the lcb to it */
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - LCB but no CCB", fixed_cid);
+ return (FALSE);
+ }
+#if BLE_INCLUDED == TRUE
+ reason = (p_lcb->is_ble_link) ? 1: 0;
+ (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, reason);
+#else
+ (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, 0);
+#endif
+ return (TRUE);
+ }
+
+ /* No link. Get an LCB and start link establishment */
+ if ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - no LCB", fixed_cid);
+ return (FALSE);
+ }
+
+ /* Get a CCB and link the lcb to it */
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES;
+ L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - no CCB", fixed_cid);
+ l2cu_release_lcb (p_lcb);
+ return (FALSE);
+ }
+
+ return (l2cu_create_conn(p_lcb));
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SendFixedChnlData
+**
+** Description Write data on a fixed channel.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+** Pointer to buffer of type BT_HDR
+**
+** Return value L2CAP_DW_SUCCESS, if data accepted
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
+{
+ tL2C_LCB *p_lcb;
+
+ L2CAP_TRACE_API3 ("L2CA_SendFixedChnlData() CID: 0x%04x BDA: %08x%04x", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* Check CID is valid and registered */
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) )
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_SendFixedChnlData() Invalid CID: 0x%04x", fixed_cid);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* Fail if BT is not yet up */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* We need to have a link up */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData(0x%04x) - no LCB", fixed_cid);
+ return (L2CAP_DW_FAILED);
+ }
+
+ if ((p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)) == 0)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData() - peer does not support fixed chnl: 0x%04x", fixed_cid);
+ return (L2CAP_DW_FAILED);
+ }
+
+ p_buf->event = 0;
+ p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED;
+
+ if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL])
+ {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x", fixed_cid);
+ return (L2CAP_DW_FAILED);
+ }
+ }
+
+ l2c_enqueue_peer_data (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+
+ /* If there is no dynamic CCB on the link, restart the idle timer each time something is sent */
+ if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb)
+ {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+
+ return (L2CAP_DW_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_RemoveFixedChnl
+**
+** Description Remove a fixed channel to a remote device.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+** Idle timeout to use (or 0xFFFF if don't care)
+**
+** Return value: TRUE if channel removed
+**
+*******************************************************************************/
+BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ /* Check CID is valid and registered */
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) )
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_RemoveFixedChnl() Invalid CID: 0x%04x", fixed_cid);
+ return (FALSE);
+ }
+
+ /* Is a fixed channel connected to the remote BDA ?*/
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda);
+ if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) )
+ {
+ L2CAP_TRACE_WARNING3 ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API3 ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* Release the CCB, starting an inactivity timeout on the LCB if no other CCBs exist */
+ p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];
+
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = NULL;
+ p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST;
+ l2cu_release_ccb (p_ccb);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetFixedChannelTout
+**
+** Description Higher layers call this function to set the idle timeout for
+** a fixed channel. 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.
+** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY,
+** then the idle timeouts for all active l2cap links will be
+** changed.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout)
+{
+ tL2C_LCB *p_lcb;
+
+ /* Is a fixed channel connected to the remote BDA ?*/
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda);
+ if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) )
+ {
+ L2CAP_TRACE_WARNING3 ("L2CA_SetFixedChannelTout() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return (FALSE);
+ }
+
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->fixed_chnl_idle_tout = idle_tout;
+
+ if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb)
+ {
+ /* If there are no dynamic CCBs, (re)start the idle timer in case we changed it */
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+
+ return (TRUE);
+}
+
+#endif /* #if (L2CAP_NUM_FIXED_CHNLS > 0) */
+
+/*******************************************************************************
+**
+** Function L2CA_GetCurrentConfig
+**
+** Description This function returns configurations of L2CAP channel
+** pp_our_cfg : pointer of our saved configuration options
+** p_our_cfg_bits : valid config in bitmap
+** pp_peer_cfg: pointer of peer's saved configuration options
+** p_peer_cfg_bits : valid config in bitmap
+**
+** Returns TRUE if successful
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid,
+ tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits,
+ tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API1 ("L2CA_GetCurrentConfig() CID: 0x%04x", lcid);
+
+ p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
+
+ if (p_ccb)
+ {
+ *pp_our_cfg = &(p_ccb->our_cfg);
+
+ /* convert valid config items into bitmap */
+ *p_our_cfg_bits = 0;
+ if (p_ccb->our_cfg.mtu_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
+ if (p_ccb->our_cfg.qos_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
+ if (p_ccb->our_cfg.flush_to_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
+ if (p_ccb->our_cfg.fcr_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
+ if (p_ccb->our_cfg.fcs_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
+ if (p_ccb->our_cfg.ext_flow_spec_present)
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC;
+
+ *pp_peer_cfg = &(p_ccb->peer_cfg);
+ *p_peer_cfg_bits = p_ccb->peer_cfg_bits;
+
+ return TRUE;
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR1 ("No CCB for CID:0x%04x", lcid);
+ return FALSE;
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_RegForNoCPEvt
+**
+** Description Register callback for Number of Completed Packets event.
+**
+** Input Param p_cb - callback for Number of completed packets event
+** p_bda - BT address of remote device
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda)
+{
+ tL2C_LCB *p_lcb;
+
+ /* Find the link that is associated with this remote bdaddr */
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda);
+
+ /* If no link for this handle, nothing to do. */
+ if (!p_lcb)
+ return FALSE;
+
+ p_lcb->p_nocp_cb = p_cb;
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DataWrite
+**
+** Description Higher layers call this function to write data.
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data)
+{
+ L2CAP_TRACE_API2 ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len);
+ return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetChnlFlushability
+**
+** Description Higher layers call this function to set a channels
+** flushability flags
+**
+** Returns TRUE if CID found, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable)
+{
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+
+ tL2C_CCB *p_ccb;
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_SetChnlFlushability, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_ccb->is_flushable = is_flushable;
+
+ L2CAP_TRACE_API2 ("L2CA_SetChnlFlushability() CID: 0x%04x is_flushable: %d", cid, is_flushable);
+
+#endif
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DataWriteEx
+**
+** Description Higher layers call this function to write data with extended
+** flags.
+** flags : L2CAP_FLUSHABLE_CH_BASED
+** L2CAP_FLUSHABLE_PKT
+** L2CAP_NON_FLUSHABLE_PKT
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags)
+{
+ L2CAP_TRACE_API3 ("L2CA_DataWriteEx() CID: 0x%04x Len: %d Flags:0x%04X",
+ cid, p_data->len, flags);
+ return l2c_data_write (cid, p_data, flags);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_FlushChannel
+**
+** Description This function flushes none, some or all buffers queued up
+** for xmission for a particular CID. If called with
+** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number
+** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff)
+** flushes all buffers. All other values specifies the maximum
+** buffers to flush.
+**
+** Returns Number of buffers left queued for that CID
+**
+*******************************************************************************/
+UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+ UINT16 num_left = 0,
+ num_flushed1 = 0,
+ num_flushed2 = 0;
+ BT_HDR *p_buf1, *p_buf;
+
+ p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
+
+ if ( !p_ccb || ((p_lcb = p_ccb->p_lcb) == NULL) )
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_FlushChannel() abnormally returning 0 CID: 0x%04x", lcid);
+ return (0);
+ }
+
+ if (num_to_flush != L2CAP_FLUSH_CHANS_GET)
+ {
+ L2CAP_TRACE_API4 ("L2CA_FlushChannel (FLUSH) CID: 0x%04x NumToFlush: %d QC: %u pFirst: 0x%08x",
+ lcid, num_to_flush, p_ccb->xmit_hold_q.count, p_ccb->xmit_hold_q.p_first);
+ }
+ else
+ {
+ L2CAP_TRACE_API1 ("L2CA_FlushChannel (QUERY) CID: 0x%04x", lcid);
+ }
+
+ /* Cannot flush eRTM buffers once they have a sequence number */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE)
+ {
+#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE
+ if (num_to_flush != L2CAP_FLUSH_CHANS_GET)
+ {
+ /* If the controller supports enhanced flush, flush the data queued at the controller */
+ if ( (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ()))
+ && (BTM_GetNumScoLinks() == 0) )
+ {
+ if ( l2cb.is_flush_active == FALSE )
+ {
+ l2cb.is_flush_active = TRUE;
+
+ /* The only packet type defined - 0 - Automatically-Flushable Only */
+ btsnd_hcic_enhanced_flush (p_lcb->handle, 0);
+ }
+ }
+ }
+#endif
+
+ p_buf = (BT_HDR *)p_lcb->link_xmit_data_q.p_first;
+
+ /* First flush the number we are asked to flush */
+ while ((p_buf != NULL) && (num_to_flush != 0))
+ {
+ /* Do not flush other CIDs or partial segments */
+ if ( (p_buf->layer_specific == 0) && (p_buf->event == lcid) )
+ {
+ p_buf1 = p_buf;
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ num_to_flush--;
+ num_flushed1++;
+
+ GKI_remove_from_queue (&p_lcb->link_xmit_data_q, p_buf1);
+ GKI_freebuf (p_buf1);
+ }
+ else
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ }
+ }
+
+ /* If needed, flush buffers in the CCB xmit hold queue */
+ while ( (num_to_flush != 0) && (p_ccb->xmit_hold_q.count != 0) )
+ {
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+ if (p_buf)
+ GKI_freebuf (p_buf);
+ num_to_flush--;
+ num_flushed2++;
+ }
+
+ /* If app needs to track all packets, call him */
+ if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (num_flushed2) )
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, num_flushed2);
+
+ /* Now count how many are left */
+ p_buf = (BT_HDR *)p_lcb->link_xmit_data_q.p_first;
+
+ while (p_buf != NULL)
+ {
+ if (p_buf->event == lcid)
+ num_left++;
+
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ }
+
+ /* Add in the number in the CCB xmit queue */
+ num_left += p_ccb->xmit_hold_q.count;
+
+ /* Return the local number of buffers left for the CID */
+ L2CAP_TRACE_DEBUG3 ("L2CA_FlushChannel() flushed: %u + %u, num_left: %u", num_flushed1, num_flushed2, num_left);
+
+ /* If we were congested, and now we are not, tell the app */
+ l2cu_check_channel_congestion (p_ccb);
+
+ return (num_left);
+}
+
diff --git a/stack/l2cap/l2c_ble.c b/stack/l2cap/l2c_ble.c
new file mode 100644
index 0000000..8c69dee
--- /dev/null
+++ b/stack/l2cap/l2c_ble.c
@@ -0,0 +1,585 @@
+/*****************************************************************************
+**
+** Name: l2c_ble.c
+**
+** Description: this file contains functions relating to BLE management.
+**
+**
+** Copyright (c) 2009-2012, Broadcom Corp, All Rights Reserved.
+******************************************************************************/
+
+#include <string.h>
+#include "bt_target.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "btu.h"
+#include "btm_int.h"
+#include "hcimsgs.h"
+
+#if (BLE_INCLUDED == TRUE)
+
+/*******************************************************************************
+**
+** Function L2CA_CancelBleConnectReq
+**
+** Description Cancel a pending connection attempt to a BLE device.
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if connection was cancelled
+**
+*******************************************************************************/
+BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+
+ /* There can be only one BLE connection request outstanding at a time */
+ if (!l2cb.is_ble_connecting)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CA_CancelBleConnectReq - no connection pending");
+ return(FALSE);
+ }
+
+ if (memcmp (rem_bda, l2cb.ble_connecting_bda, BD_ADDR_LEN))
+ {
+ L2CAP_TRACE_WARNING4 ("L2CA_CancelBleConnectReq - different BDA Connecting: %08x%04x Cancel: %08x%04x",
+ (l2cb.ble_connecting_bda[0]<<24)+(l2cb.ble_connecting_bda[1]<<16)+(l2cb.ble_connecting_bda[2]<<8)+l2cb.ble_connecting_bda[3],
+ (l2cb.ble_connecting_bda[4]<<8)+l2cb.ble_connecting_bda[5],
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+
+ return(FALSE);
+ }
+
+ if (btsnd_hcic_ble_create_conn_cancel())
+ {
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) != NULL)
+ {
+ p_lcb->disc_reason = L2CAP_CONN_CANCEL;
+ l2cu_release_lcb (p_lcb);
+ }
+
+ l2cb.is_ble_connecting = FALSE;
+ btm_ble_update_bg_state();
+ btm_ble_resume_bg_conn(NULL, TRUE);
+
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_UpdateBleConnParams
+**
+** Description Update BLE connection parameters.
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if update started
+**
+*******************************************************************************/
+BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bda, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout)
+{
+ tL2C_LCB *p_lcb;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CA_UpdateBleConnParams - unknown BD_ADDR %08x%04x",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return(FALSE);
+ }
+
+ if (!p_lcb->is_ble_link)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CA_UpdateBleConnParams - BD_ADDR %08x%04x not LE",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return(FALSE);
+ }
+
+ if (p_lcb->link_role == HCI_ROLE_MASTER)
+ btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, min_int, max_int, latency, timeout, 0, 0);
+ else
+ l2cu_send_peer_ble_par_req (p_lcb, min_int, max_int, latency, timeout);
+
+ return(TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_EnableUpdateBleConnParams
+**
+** Description Enable or disable update based on the request from the peer
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if update started
+**
+*******************************************************************************/
+BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable)
+{
+ tL2C_LCB *p_lcb;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CA_EnableUpdateBleConnParams - unknown BD_ADDR %08x%04x",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API4 ("L2CA_EnableUpdateBleConnParams - BD_ADDR %08x%04x enable %d current upd state %d",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5], enable, p_lcb->upd_disabled);
+
+ if (!p_lcb->is_ble_link || (p_lcb->link_role != HCI_ROLE_MASTER))
+ {
+ L2CAP_TRACE_WARNING3 ("L2CA_EnableUpdateBleConnParams - BD_ADDR %08x%04x not LE or not master %d",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5], p_lcb->link_role);
+ return (FALSE);
+ }
+
+ if (enable)
+ {
+ /* application allows to do update, if we were delaying one do it now, otherwise
+ just mark lcb that updates are enabled */
+ if (p_lcb->upd_disabled == UPD_PENDING)
+ {
+ btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, p_lcb->min_interval, p_lcb->max_interval,
+ p_lcb->latency, p_lcb->timeout, 0, 0);
+ p_lcb->upd_disabled = UPD_UPDATED;
+ }
+ else
+ {
+ p_lcb->upd_disabled = UPD_ENABLED;
+ }
+ }
+ else
+ {
+ /* application requests to disable parameters update. If parameters are already updated, lets set them
+ up to what has been requested during connection establishement */
+ if (p_lcb->upd_disabled == UPD_UPDATED)
+ {
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (rem_bda);
+
+ btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle,
+ (UINT16)((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.min_conn_int : L2CAP_LE_INT_MIN),
+ (UINT16)((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.max_conn_int : L2CAP_LE_INT_MAX),
+ (UINT16)((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.slave_latency : 0),
+ (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.supervision_tout : L2CAP_LE_TIMEOUT_MAX),
+ 0, 0);
+ }
+ p_lcb->upd_disabled = UPD_DISABLED;
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetBleConnRole
+**
+** Description This function returns the connection role.
+**
+** Returns link role.
+**
+*******************************************************************************/
+UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr)
+{
+ UINT8 role = HCI_ROLE_UNKNOWN;
+
+ tL2C_LCB *p_lcb;
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr)) != NULL)
+ role = p_lcb->link_role;
+
+ return role;
+}
+/*******************************************************************************
+**
+** Function L2CA_GetDisconnectReason
+**
+** Description This function returns the disconnect reason code.
+**
+** Returns disconnect reason
+**
+*******************************************************************************/
+UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 reason = 0;
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda)) != NULL)
+ reason = p_lcb->disc_reason;
+
+ L2CAP_TRACE_DEBUG1 ("L2CA_GetDisconnectReason=%d ",reason);
+
+ return reason;
+}
+
+/*******************************************************************************
+**
+** Function l2cble_scanner_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received while we are a scanner (so we are master).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ tL2C_LCB *p_lcb;
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda);
+
+ L2CAP_TRACE_DEBUG5 ("l2cble_scanner_conn_comp: HANDLE=%d addr_type=%d conn_interval=%d slave_latency=%d supervision_tout=%d",
+ handle, type, conn_interval, conn_latency, conn_timeout);
+
+ l2cb.is_ble_connecting = FALSE;
+
+ p_dev_rec->device_type = BT_DEVICE_TYPE_BLE;
+ p_dev_rec->ble.ble_addr_type = type;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bda);
+
+ /* If we don't have one, create one. this is auto connection complete. */
+ if (!p_lcb)
+ {
+ p_lcb = l2cu_allocate_lcb (bda, FALSE);
+ if (!p_lcb)
+ {
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_ERROR0 ("l2cble_scanner_conn_comp - failed to allocate LCB");
+ return;
+ }
+ else
+ {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_WARNING0 ("l2cble_scanner_conn_comp - LCB but no CCB");
+ return ;
+ }
+ }
+ }
+ else if (p_lcb->link_state != LST_CONNECTING)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP got BLE scanner conn_comp in bad state: %d", p_lcb->link_state);
+ return;
+ }
+ btu_stop_timer(&p_lcb->timer_entry);
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ /* Connected OK. Change state to connected, we were scanning so we are master */
+ p_lcb->link_state = LST_CONNECTED;
+ p_lcb->link_role = HCI_ROLE_MASTER;
+ p_lcb->is_ble_link = TRUE;
+
+ /* If there are any preferred connection parameters, set them now */
+ if ( (p_dev_rec->conn_params.min_conn_int >= L2CAP_LE_INT_MIN ) &&
+ (p_dev_rec->conn_params.min_conn_int <= L2CAP_LE_INT_MAX ) &&
+ (p_dev_rec->conn_params.max_conn_int >= L2CAP_LE_INT_MIN ) &&
+ (p_dev_rec->conn_params.max_conn_int <= L2CAP_LE_INT_MAX ) &&
+ (p_dev_rec->conn_params.slave_latency <= L2CAP_LE_LATENCY_MAX ) &&
+ (p_dev_rec->conn_params.supervision_tout >= L2CAP_LE_TIMEOUT_MIN) &&
+ (p_dev_rec->conn_params.supervision_tout <= L2CAP_LE_TIMEOUT_MAX) &&
+ ((conn_interval < p_dev_rec->conn_params.min_conn_int &&
+ p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ||
+ (conn_interval > p_dev_rec->conn_params.max_conn_int) ||
+ (conn_latency > p_dev_rec->conn_params.slave_latency) ||
+ (conn_timeout > p_dev_rec->conn_params.supervision_tout)))
+ {
+ L2CAP_TRACE_ERROR5 ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d",
+ handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout);
+
+ btsnd_hcic_ble_upd_ll_conn_params (handle,
+ p_dev_rec->conn_params.min_conn_int,
+ p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency,
+ p_dev_rec->conn_params.supervision_tout,
+ 0, 0);
+ }
+
+ /* Tell BTM Acl management about the link */
+ btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, TRUE);
+
+ if (p_lcb->p_echo_rsp_cb)
+ {
+ L2CAP_TRACE_ERROR0 ("l2cu_send_peer_echo_req");
+ l2cu_send_peer_echo_req (p_lcb, NULL, 0);
+ }
+
+ p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+
+ l2cu_process_fixed_chnl_resp (p_lcb);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cble_advertiser_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received while we are an advertiser (so we are slave).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ tL2C_LCB *p_lcb;
+ tBTM_SEC_DEV_REC *p_dev_rec;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bda);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb)
+ {
+ p_lcb = l2cu_allocate_lcb (bda, FALSE);
+ if (!p_lcb)
+ {
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_ERROR0 ("l2cble_advertiser_conn_comp - failed to allocate LCB");
+ return;
+ }
+ else
+ {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_WARNING0 ("l2cble_scanner_conn_comp - LCB but no CCB");
+ return ;
+ }
+ }
+ }
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ /* Connected OK. Change state to connected, we were advertising, so we are slave */
+ p_lcb->link_state = LST_CONNECTED;
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ p_lcb->is_ble_link = TRUE;
+
+ /* Tell BTM Acl management about the link */
+ p_dev_rec = btm_find_or_alloc_dev (bda);
+
+ p_dev_rec->device_type = BT_DEVICE_TYPE_BLE;
+ p_dev_rec->ble.ble_addr_type = type;
+
+ btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, TRUE);
+
+ p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+
+ l2cu_process_fixed_chnl_resp (p_lcb);
+}
+
+/*******************************************************************************
+**
+** Function l2cble_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_conn_comp(UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ if (role == HCI_ROLE_MASTER)
+ {
+ l2cble_scanner_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout);
+ }
+ else
+ {
+ l2cble_advertiser_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout);
+ }
+}
+/*******************************************************************************
+**
+** Function l2cble_process_sig_cmd
+**
+** Description This function is called when a signalling packet is received
+** on the BLE signalling CID
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
+{
+ UINT8 *p_pkt_end;
+ UINT8 cmd_code, id;
+ UINT16 cmd_len, rej_reason;
+ UINT16 result;
+ UINT16 min_interval, max_interval, latency, timeout;
+
+ p_pkt_end = p + pkt_len;
+
+ STREAM_TO_UINT8 (cmd_code, p);
+ STREAM_TO_UINT8 (id, p);
+ STREAM_TO_UINT16 (cmd_len, p);
+
+ /* Check command length does not exceed packet length */
+ if ((p + cmd_len) > p_pkt_end)
+ {
+ L2CAP_TRACE_WARNING3 ("L2CAP - LE - format error, pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code);
+ return;
+ }
+
+ switch (cmd_code)
+ {
+ case L2CAP_CMD_REJECT:
+ case L2CAP_CMD_ECHO_RSP:
+ case L2CAP_CMD_INFO_RSP:
+ STREAM_TO_UINT16 (rej_reason, p);
+ break;
+ case L2CAP_CMD_ECHO_REQ:
+ case L2CAP_CMD_INFO_REQ:
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ break;
+
+ case L2CAP_CMD_BLE_UPDATE_REQ:
+ STREAM_TO_UINT16 (min_interval, p); /* 0x0006 - 0x0C80 */
+ STREAM_TO_UINT16 (max_interval, p); /* 0x0006 - 0x0C80 */
+ STREAM_TO_UINT16 (latency, p); /* 0x0000 - 0x03E8 */
+ STREAM_TO_UINT16 (timeout, p); /* 0x000A - 0x0C80 */
+ /* If we are a master, the slave wants to update the parameters */
+ if (p_lcb->link_role == HCI_ROLE_MASTER)
+ {
+ if (min_interval < L2CAP_LE_INT_MIN || min_interval > L2CAP_LE_INT_MAX ||
+ max_interval < L2CAP_LE_INT_MIN || max_interval > L2CAP_LE_INT_MAX ||
+ latency > L2CAP_LE_LATENCY_MAX ||
+ /*(timeout >= max_interval && latency > (timeout * 10/(max_interval * 1.25) - 1)) ||*/
+ timeout < L2CAP_LE_TIMEOUT_MIN || timeout > L2CAP_LE_TIMEOUT_MAX ||
+ max_interval < min_interval)
+ {
+ result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ }
+ else
+ {
+
+ l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_OK, id);
+
+ p_lcb->min_interval = min_interval;
+ p_lcb->max_interval = max_interval;
+ p_lcb->latency = latency;
+ p_lcb->timeout = timeout;
+
+ if (p_lcb->upd_disabled == UPD_ENABLED)
+ {
+ btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, min_interval, max_interval,
+ latency, timeout, 0, 0);
+ p_lcb->upd_disabled = UPD_UPDATED;
+ }
+ else
+ {
+ L2CAP_TRACE_EVENT0 ("L2CAP - LE - update currently disabled");
+ p_lcb->upd_disabled = UPD_PENDING;
+ }
+ }
+ }
+ else
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ break;
+
+ case L2CAP_CMD_BLE_UPDATE_RSP:
+ STREAM_TO_UINT16 (result, p);
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING1 ("L2CAP - LE - unknown cmd code: %d", cmd_code);
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ return;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cble_create_conn
+**
+** Description This function initiates an acl connection via HCI
+**
+** Returns TRUE if successful, FALSE if connection not started.
+**
+*******************************************************************************/
+BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb)
+{
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_lcb->remote_bd_addr);
+ tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
+ UINT16 scan_int, scan_win;
+
+ /* There can be only one BLE connection request outstanding at a time */
+ if (l2cb.is_ble_connecting)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - LE - cannot start new connection, already connecting");
+ return(FALSE);
+ }
+
+ p_lcb->link_state = LST_CONNECTING;
+ l2cb.is_ble_connecting = TRUE;
+
+ memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+ btm_ble_suspend_bg_conn();
+
+ scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? L2CAP_LE_INT_MIN : p_cb->scan_int;
+ scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? L2CAP_LE_INT_MIN : p_cb->scan_win;
+
+ if (!btsnd_hcic_ble_create_ll_conn (scan_int,/* UINT16 scan_int */
+ scan_win, /* UINT16 scan_win */
+ FALSE, /* UINT8 white_list */
+ p_lcb->ble_addr_type, /* UINT8 addr_type_peer */
+ p_lcb->remote_bd_addr, /* BD_ADDR bda_peer */
+ BLE_ADDR_PUBLIC, /* UINT8 addr_type_own */
+ (UINT16) ((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.min_conn_int : L2CAP_LE_INT_MIN), /* UINT16 conn_int_min */
+ (UINT16) ((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.max_conn_int : L2CAP_LE_INT_MIN), /* UINT16 conn_int_max */
+ (UINT16) ((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.slave_latency : 0), /* UINT16 conn_latency */
+ (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.supervision_tout : L2CAP_LE_TIMEOUT_MAX), /* UINT16 conn_timeout */
+ 0, /* UINT16 min_len */
+ 0)) /* UINT16 max_len */
+ {
+ /* No buffer for connection request ? */
+ l2cb.is_ble_connecting = FALSE;
+ p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES;
+ l2cu_release_lcb (p_lcb);
+ return(FALSE);
+ }
+ else
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_BLE_LINK_CONNECT_TOUT);
+
+ return(TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_processs_ble_num_bufs
+**
+** Description This function is called when a "controller buffer size"
+** event is first received from the controller. It updates
+** the L2CAP values.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_processs_ble_num_bufs (UINT16 num_lm_ble_bufs)
+{
+ l2cb.num_lm_ble_bufs = l2cb.controller_le_xmit_window = num_lm_ble_bufs;
+}
+
+#endif /* (BLE_INCLUDED == TRUE) */
diff --git a/stack/l2cap/l2c_csm.c b/stack/l2cap/l2c_csm.c
new file mode 100644
index 0000000..216f45b
--- /dev/null
+++ b/stack/l2cap/l2c_csm.c
@@ -0,0 +1,1321 @@
+/*****************************************************************************
+**
+** Name: l2c_csm.c
+**
+** Description: This file contains the L2CAP channel state machine
+**
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bt_target.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_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+
+#if (BT_TRACE_VERBOSE == TRUE)
+static char *l2c_csm_get_event_name (UINT16 event);
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_csm_execute
+**
+** Description This function executes the state machine.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ switch (p_ccb->chnl_state)
+ {
+ case CST_CLOSED:
+ l2c_csm_closed (p_ccb, event, p_data);
+ break;
+
+ case CST_ORIG_W4_SEC_COMP:
+ l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
+ break;
+
+ case CST_TERM_W4_SEC_COMP:
+ l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_CONNECT_RSP:
+ l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_CONNECT_RSP:
+ l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_CONFIG:
+ l2c_csm_config (p_ccb, event, p_data);
+ break;
+
+ case CST_OPEN:
+ l2c_csm_open (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_DISCONNECT_RSP:
+ l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_DISCONNECT_RSP:
+ l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_csm_closed
+**
+** Description This function handles events when the channel is in
+** CLOSED state. This state exists only when the link is
+** being initially established.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data;
+ UINT16 local_cid = p_ccb->local_cid;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind;
+ tL2CA_CONNECT_CFM_CB *connect_cfm;
+
+ if (p_ccb->p_rcb == NULL)
+ {
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_ERROR2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s p_rcb == NULL", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_ERROR2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: 0x%04x p_rcb == NULL", p_ccb->local_cid, event);
+#endif
+ return;
+ }
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) )
+ {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: CLOSED evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb);
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */
+ /* Disconnect unless ACL collision and upper layer wants to handle it */
+ if (p_ci->status != HCI_ERR_CONNECTION_EXISTS
+ || !btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr))
+ {
+ L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, p_ci->status);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, p_ci->status);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONNECT_REQ: /* API connect request */
+ /* Cancel sniff mode if needed */
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ {
+ tBTM_PM_PWR_MD settings;
+// btla-specific ++
+ memset((void*)&settings, 0, sizeof(settings));
+// btla-specific --
+ settings.mode = BTM_PM_MD_ACTIVE;
+/* COVERITY
+Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details]
+Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details]
+Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode"
+// FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment.
+// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored
+*/
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+#else
+ BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
+#endif
+
+ /* If sec access does not result in started SEC_COM or COMP_NEG are already processed */
+ if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_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;
+
+ /* Wait for the info resp in this state before sending connect req (if needed) */
+ if (!p_ccb->p_lcb->w4_info_rsp)
+ {
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb))
+ {
+ l2cu_release_ccb (p_ccb);
+ (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_NO_LINK);
+ }
+ else
+ {
+ l2cu_send_peer_connect_req (p_ccb);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ }
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG: /* something is really bad with security */
+ L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_SECURITY_BLOCK);
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connect request */
+ /* stop link timer to avoid race condition between A2MP, Security, and L2CAP */
+ btu_stop_timer (&p_ccb->p_lcb->timer_entry);
+
+ /* Cancel sniff mode if needed */
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ {
+ tBTM_PM_PWR_MD settings;
+// btla-specific ++
+ memset((void*)&settings, 0, sizeof(settings));
+// btla-specific --
+ settings.mode = BTM_PM_MD_ACTIVE;
+/* COVERITY
+Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details]
+Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details]
+Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode"
+// FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment.
+// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored
+*/
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+#else
+ BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
+#endif
+
+ p_ccb->chnl_state = CST_TERM_W4_SEC_COMP;
+ if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED)
+ {
+ /* started the security process, tell the peer to set a longer timer */
+ l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
+ }
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT);
+ 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;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_orig_w4_sec_comp
+**
+** Description This function handles events when the channel is in
+** CST_ORIG_W4_SEC_COMP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_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_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: ORIG_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: ORIG_W4_SEC_COMP evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) )
+ {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb);
+ break;
+
+ case L2CEVT_SEC_COMP: /* Security completed success */
+ /* Wait for the info resp in this state before sending connect req (if needed) */
+ p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;
+ if (!p_ccb->p_lcb->w4_info_rsp)
+ {
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb))
+ {
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK);
+ }
+ else
+ {
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ l2cu_send_peer_connect_req (p_ccb); /* Start Connection */
+ }
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, HCI_ERR_AUTH_FAILURE);
+
+ /* If last channel immediately disconnect the ACL for better security.
+ Also prevents a race condition between BTM and L2CAP */
+ if ( (p_ccb == p_ccb->p_lcb->ccb_queue.p_first_ccb) && (p_ccb == p_ccb->p_lcb->ccb_queue.p_last_ccb) )
+ {
+ p_ccb->p_lcb->idle_timeout = 0;
+ }
+
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, HCI_ERR_AUTH_FAILURE);
+ 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 */
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_term_w4_sec_comp
+**
+** Description This function handles events when the channel is in
+** CST_TERM_W4_SEC_COMP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: TERM_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: TERM_W4_SEC_COMP evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) )
+ {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP;
+
+ /* Wait for the info resp in next state before sending connect ind (if needed) */
+ if (!p_ccb->p_lcb->w4_info_rsp)
+ {
+ /* Don't need to get info from peer or already retrieved so continue */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid,
+ p_ccb->p_rcb->psm, p_ccb->remote_id);
+ }
+ else
+ {
+ /*
+ ** L2CAP Connect Response will be sent out by 3 sec timer expiration
+ ** because Bluesoleil doesn't respond to L2CAP Information Request.
+ ** Bluesoleil seems to disconnect ACL link as failure case, because
+ ** it takes too long (4~7secs) to get response.
+ ** product version : Bluesoleil 2.1.1.0 EDR Release 060123
+ ** stack version : 05.04.11.20060119
+ */
+
+ /* Waiting for the info resp, tell the peer to set a longer timer */
+ l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK)
+ {
+ /* start a timer - encryption change not received before L2CAP connect req */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4);
+ }
+ else
+ {
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_SECURITY_BLOCK, 0);
+ 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;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid);
+
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* SM4 related. */
+ if (!btsnd_hcic_disconnect (p_ccb->p_lcb->handle, HCI_ERR_AUTH_FAILURE))
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling btsnd_hcic_disconnect for handle %i failed", p_ccb->p_lcb->handle);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 1);
+ }
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2cap_connect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CAP_CONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CAP_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CAP_CON_RSP evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ /* Send disc indication unless peer to peer race condition AND normal disconnect */
+ /* *((UINT8 *)p_data) != HCI_ERR_PEER_USER happens when peer device try to disconnect for normal reason */
+ p_ccb->chnl_state = CST_CLOSED;
+ if ((p_ccb->flags & CCB_FLAG_NO_RETRY) || !p_data || (*((UINT8 *)p_data) != HCI_ERR_PEER_USER))
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ }
+ p_ccb->flags |= CCB_FLAG_NO_RETRY;
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP: /* Got peer connect confirm */
+ p_ccb->remote_cid = p_ci->remote_cid;
+ p_ccb->chnl_state = CST_CONFIG;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Success", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_OK);
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Got peer connect pending */
+ p_ccb->remote_cid = p_ci->remote_cid;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT);
+ if (p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb)
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Pnd_Cb(), CID: 0x%04x", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb)(p_ccb->local_cid);
+ }
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer rejected connection */
+ L2CAP_TRACE_API2 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Failure Code: %d", p_ccb->local_cid, p_ci->l2cap_result);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, p_ci->l2cap_result);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Timeout", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ /* If we know peer CID from connect pending, we can send disconnect */
+ if (p_ccb->remote_cid != 0)
+ {
+ 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);
+ }
+ else
+ 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_L2CAP_INFO_RSP:
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb))
+ {
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK);
+ }
+ else
+ {
+ /* We have feature info, so now send peer connect request */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ l2cu_send_peer_connect_req (p_ccb); /* Start Connection */
+ }
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2ca_connect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CA_CONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CA_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CA_CON_RSP evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_CONNECT_RSP:
+ p_ci = (tL2C_CONN_INFO *)p_data;
+
+ /* Result should be OK or PENDING */
+ if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK))
+ {
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_OK, 0);
+ p_ccb->chnl_state = CST_CONFIG;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ }
+ else
+ {
+ /* If pending, stay in same state and start extended timer */
+ l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONNECT_RSP_NEG:
+ p_ci = (tL2C_CONN_INFO *)p_data;
+ l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_NO_PSM, 0);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ 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;
+
+ 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_L2CAP_INFO_RSP:
+ /* We have feature info, so now give the upper layer connect IND */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr,
+ p_ccb->local_cid,
+ p_ccb->p_rcb->psm,
+ p_ccb->remote_id);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_config
+**
+** Description This function handles events when the channel is in
+** CONFIG state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CAP_CFG_INFO *p_cfg = (tL2CAP_CFG_INFO *)p_data;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+ UINT8 cfg_result;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: CONFIG evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: CONFIG evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
+
+ if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
+ {
+ L2CAP_TRACE_EVENT2 ("L2CAP - Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d",
+ p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT));
+ (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
+ }
+ else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT)
+ {
+ /* Disconnect if channels are incompatible */
+ L2CAP_TRACE_EVENT0 ("L2CAP - incompatible configurations disconnect");
+ l2cu_disconnect_chnl (p_ccb);
+ }
+ else /* Return error to peer so he can renegotiate if possible */
+ {
+ L2CAP_TRACE_EVENT0 ("L2CAP - incompatible configurations trying reconfig");
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ }
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */
+ l2cu_process_peer_cfg_rsp (p_ccb, p_cfg);
+
+ if (p_cfg->result != L2CAP_CFG_PENDING)
+ {
+ /* TBD: When config options grow beyong minimum MTU (48 bytes)
+ * logic needs to be added to handle responses with
+ * continuation bit set in flags field.
+ * 1. Send additional config request out until C-bit is cleared in response
+ */
+ p_ccb->config_done |= OB_CFG_DONE;
+
+ if (p_ccb->config_done & IB_CFG_DONE)
+ {
+ /* Verify two sides are in compatible modes before continuing */
+ if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode)
+ {
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_WARNING1 ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+
+ p_ccb->config_done |= RECONFIG_FLAG;
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_link_adjust_chnl_allocation ();
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ /* If using eRTM and waiting for an ACK, restart the ACK timer */
+ if (p_ccb->fcrb.wait_ack)
+ l2c_fcr_start_timer(p_ccb);
+
+ /*
+ ** check p_ccb->our_cfg.fcr.mon_tout and p_ccb->our_cfg.fcr.rtrans_tout
+ ** we may set them to zero when sending config request during renegotiation
+ */
+ if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ &&((p_ccb->our_cfg.fcr.mon_tout == 0)||(p_ccb->our_cfg.fcr.rtrans_tout)))
+ {
+ l2c_fcr_adj_monitor_retran_timeout (p_ccb);
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.connect_tick_count = GKI_get_os_tick_count();
+#endif
+ /* See if we can forward anything on the hold queue */
+ if (p_ccb->xmit_hold_q.count)
+ {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ }
+ }
+
+ L2CAP_TRACE_API1 ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg);
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer config error rsp */
+ /* Disable the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ /* If failure was channel mode try to renegotiate */
+ if (l2c_fcr_renegotiate_chan(p_ccb, p_cfg) == FALSE)
+ {
+ L2CAP_TRACE_API2 ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x, Failure: %d", p_ccb->local_cid, p_cfg->result);
+ (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP;
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
+ break;
+
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
+ l2cu_process_our_cfg_req (p_ccb, p_cfg);
+ l2cu_send_peer_config_req (p_ccb, p_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */
+ l2cu_process_our_cfg_rsp (p_ccb, p_cfg);
+
+ /* Not finished if continuation flag is set */
+ if ( (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT) || (p_cfg->result == L2CAP_CFG_PENDING) )
+ {
+ /* Send intermediate response; remain in cfg state */
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ break;
+ }
+
+ /* Local config done; clear cached configuration in case reconfig takes place later */
+ p_ccb->peer_cfg.mtu_present = FALSE;
+ p_ccb->peer_cfg.flush_to_present = FALSE;
+ p_ccb->peer_cfg.qos_present = FALSE;
+
+ p_ccb->config_done |= IB_CFG_DONE;
+
+ if (p_ccb->config_done & OB_CFG_DONE)
+ {
+ /* Verify two sides are in compatible modes before continuing */
+ if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode)
+ {
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_WARNING1 ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+
+ p_ccb->config_done |= RECONFIG_FLAG;
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_link_adjust_chnl_allocation ();
+ btu_stop_timer (&p_ccb->timer_entry);
+ }
+
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+
+ /* If using eRTM and waiting for an ACK, restart the ACK timer */
+ if (p_ccb->fcrb.wait_ack)
+ l2c_fcr_start_timer(p_ccb);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.connect_tick_count = GKI_get_os_tick_count();
+#endif
+
+ /* See if we can forward anything on the hold queue */
+ if ( (p_ccb->chnl_state == CST_OPEN) && (p_ccb->xmit_hold_q.count) )
+ {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config reject */
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ 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_L2CAP_DATA: /* Peer data packet rcvd */
+ L2CAP_TRACE_API1 ("L2CAP - Calling DataInd_Cb(), CID: 0x%04x", p_ccb->local_cid);
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_ccb->local_cid < L2CAP_BASE_APPL_CID)
+ {
+ if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_ccb->p_lcb->remote_bd_addr,(BT_HDR *)p_data);
+ else
+ GKI_freebuf (p_data);
+
+ break;
+ }
+#endif
+ (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ if (p_ccb->config_done & OB_CFG_DONE)
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
+ else
+ GKI_freebuf (p_data);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_open
+**
+** Description This function handles events when the channel is in
+** OPEN state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ UINT16 local_cid = p_ccb->local_cid;
+ tL2CAP_CFG_INFO *p_cfg;
+ tL2C_CHNL_STATE tempstate;
+ UINT8 tempcfgdone;
+ UINT8 cfg_result;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) )
+ {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ if (p_ccb->p_rcb)
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */
+ /* Tell upper layer. If service guaranteed, then clear the channel */
+ if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
+ (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
+ p_cfg = (tL2CAP_CFG_INFO *)p_data;
+
+ tempstate = p_ccb->chnl_state;
+ tempcfgdone = p_ccb->config_done;
+ p_ccb->chnl_state = CST_CONFIG;
+ p_ccb->config_done &= ~CFG_DONE_MASK;
+
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+
+ if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
+ {
+ (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
+ }
+
+ /* Error in config parameters: reset state and config flag */
+ else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE)
+ {
+ btu_stop_timer(&p_ccb->timer_entry);
+ p_ccb->chnl_state = tempstate;
+ p_ccb->config_done = tempcfgdone;
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ }
+ else /* L2CAP_PEER_CFG_DISCONNECT */
+ {
+ /* Disconnect if channels are incompatible
+ * Note this should not occur if reconfigure
+ * since this should have never passed original config.
+ */
+ l2cu_disconnect_chnl (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+// btla-specific ++
+ /* Make sure we are not in sniff mode */
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ {
+ tBTM_PM_PWR_MD settings;
+ settings.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+#else
+ BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
+#endif
+// btla-specific --
+
+ p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ /* Make sure we are not in sniff mode */
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ {
+ tBTM_PM_PWR_MD settings;
+ settings.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+#else
+ BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
+#endif
+
+ 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;
+
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
+ p_ccb->chnl_state = CST_CONFIG;
+ p_ccb->config_done &= ~CFG_DONE_MASK;
+ l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
+ l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* Process the monitor/retransmission time-outs in flow control/retrans mode */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ l2c_fcr_proc_tout (p_ccb);
+ break;
+
+ case L2CEVT_ACK_TIMEOUT:
+ l2c_fcr_proc_ack_tout (p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2cap_disconnect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CAP_DISCONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2cap_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;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CAP_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CAP_DISC_RSP evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */
+ l2cu_release_ccb (p_ccb);
+ if (disconnect_cfm)
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */
+ 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);
+ if (disconnect_cfm)
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*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);
+ if (disconnect_cfm)
+ {
+ L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_TIMEOUT);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ GKI_freebuf (p_data);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2ca_disconnect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CA_DISCONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2ca_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;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CA_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CA_DISC_RSP evt: %d", event);
+#endif
+
+ switch (event)
+ {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ 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);
+ L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_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;
+ }
+}
+
+
+#if (BT_TRACE_VERBOSE == TRUE)
+/*******************************************************************************
+**
+** Function l2c_csm_get_event_name
+**
+** Description This function returns the event name.
+**
+** NOTE conditionally compiled to save memory.
+**
+** Returns pointer to the name
+**
+*******************************************************************************/
+static char *l2c_csm_get_event_name (UINT16 event)
+{
+ switch (event)
+ {
+ case L2CEVT_LP_CONNECT_CFM: /* Lower layer connect confirm */
+ return ("LOWER_LAYER_CONNECT_CFM");
+ case L2CEVT_LP_CONNECT_CFM_NEG: /* Lower layer connect confirm (failed) */
+ return ("LOWER_LAYER_CONNECT_CFM_NEG");
+ case L2CEVT_LP_CONNECT_IND: /* Lower layer connect indication */
+ return ("LOWER_LAYER_CONNECT_IND");
+ case L2CEVT_LP_DISCONNECT_IND: /* Lower layer disconnect indication */
+ return ("LOWER_LAYER_DISCONNECT_IND");
+ case L2CEVT_LP_QOS_CFM: /* Lower layer QOS confirmation */
+ return ("LOWER_LAYER_QOS_CFM");
+ case L2CEVT_LP_QOS_CFM_NEG: /* Lower layer QOS confirmation (failed)*/
+ return ("LOWER_LAYER_QOS_CFM_NEG");
+ case L2CEVT_LP_QOS_VIOLATION_IND: /* Lower layer QOS violation indication */
+ return ("LOWER_LAYER_QOS_VIOLATION_IND");
+
+ case L2CEVT_SEC_COMP: /* Security cleared successfully */
+ return ("SECURITY_COMPLETE");
+ case L2CEVT_SEC_COMP_NEG: /* Security procedure failed */
+ return ("SECURITY_COMPLETE_NEG");
+
+ case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connection request */
+ return ("PEER_CONNECT_REQ");
+ case L2CEVT_L2CAP_CONNECT_RSP: /* Peer connection response */
+ return ("PEER_CONNECT_RSP");
+ case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Peer connection response pending */
+ return ("PEER_CONNECT_RSP_PND");
+ case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer connection response (failed) */
+ return ("PEER_CONNECT_RSP_NEG");
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer configuration request */
+ return ("PEER_CONFIG_REQ");
+ case L2CEVT_L2CAP_CONFIG_RSP: /* Peer configuration response */
+ return ("PEER_CONFIG_RSP");
+ case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer configuration response (failed) */
+ return ("PEER_CONFIG_RSP_NEG");
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */
+ return ("PEER_DISCONNECT_REQ");
+ case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */
+ return ("PEER_DISCONNECT_RSP");
+ case L2CEVT_L2CAP_DATA: /* Peer data */
+ return ("PEER_DATA");
+
+ case L2CEVT_L2CA_CONNECT_REQ: /* Upper layer connect request */
+ return ("UPPER_LAYER_CONNECT_REQ");
+ case L2CEVT_L2CA_CONNECT_RSP: /* Upper layer connect response */
+ return ("UPPER_LAYER_CONNECT_RSP");
+ case L2CEVT_L2CA_CONNECT_RSP_NEG: /* Upper layer connect response (failed)*/
+ return ("UPPER_LAYER_CONNECT_RSP_NEG");
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config request */
+ return ("UPPER_LAYER_CONFIG_REQ");
+ case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config response */
+ return ("UPPER_LAYER_CONFIG_RSP");
+ case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config response (failed) */
+ return ("UPPER_LAYER_CONFIG_RSP_NEG");
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper layer disconnect request */
+ return ("UPPER_LAYER_DISCONNECT_REQ");
+ case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper layer disconnect response */
+ return ("UPPER_LAYER_DISCONNECT_RSP");
+ case L2CEVT_L2CA_DATA_READ: /* Upper layer data read */
+ return ("UPPER_LAYER_DATA_READ");
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data write */
+ return ("UPPER_LAYER_DATA_WRITE");
+ case L2CEVT_TIMEOUT: /* Timeout */
+ return ("TIMEOUT");
+ case L2CEVT_SEC_RE_SEND_CMD:
+ return ("SEC_RE_SEND_CMD");
+ case L2CEVT_L2CAP_INFO_RSP: /* Peer information response */
+ return ("L2CEVT_L2CAP_INFO_RSP");
+ case L2CEVT_ACK_TIMEOUT:
+ return ("L2CEVT_ACK_TIMEOUT");
+
+ default:
+ return ("???? UNKNOWN EVENT");
+ }
+}
+#endif /* (BT_TRACE_VERBOSE == TRUE) */
+
+
+/*******************************************************************************
+**
+** Function l2c_enqueue_peer_data
+**
+** Description Enqueues data destined for the peer in the ccb. Handles
+** FCR segmentation and checks for congestion.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ UINT8 *p;
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ p_buf->event = 0;
+ }
+ else
+ {
+ /* Save the channel ID for faster counting */
+ p_buf->event = p_ccb->local_cid;
+
+ /* Step back to add the L2CAP header */
+ p_buf->offset -= L2CAP_PKT_OVERHEAD;
+ p_buf->len += L2CAP_PKT_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* Now the L2CAP header */
+ UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ }
+
+ GKI_enqueue (&p_ccb->xmit_hold_q, p_buf);
+
+ l2cu_check_channel_congestion (p_ccb);
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* if new packet is higher priority than serving ccb and it is not overrun */
+ if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority )
+ &&( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0))
+ {
+ /* send out higher priority packet */
+ p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority;
+ }
+#endif
+
+ /* if we are doing a round robin scheduling, set the flag */
+ if (p_ccb->p_lcb->link_xmit_quota == 0)
+ l2cb.check_round_robin = TRUE;
+}
+
+
diff --git a/stack/l2cap/l2c_fcr.c b/stack/l2cap/l2c_fcr.c
new file mode 100644
index 0000000..25a9b3f
--- /dev/null
+++ b/stack/l2cap/l2c_fcr.c
@@ -0,0 +1,2697 @@
+/*****************************************************************************
+**
+** Name: l2c_fcr.c
+**
+** Description: This file contains the L2CAP 1.2 Flow Control and
+** retransmissions functions
+**
+**
+**
+** Copyright (c) 2004-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "l2c_api.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+
+
+/* Flag passed to retransmit_i_frames() when all packets should be retransmitted */
+#define L2C_FCR_RETX_ALL_PKTS 0xFF
+
+#if BT_TRACE_VERBOSE == TRUE
+static char *SAR_types[] = { "Unsegmented", "Start", "End", "Continuation" };
+static char *SUP_types[] = { "RR", "REJ", "RNR", "SREJ" };
+#endif
+
+/* Look-up table for the CRC calculation */
+static const unsigned short crctab[256] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
+};
+
+
+/*******************************************************************************
+** Static local functions
+*/
+static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word);
+static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word);
+static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack);
+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 L2CAP_CORRUPT_ERTM_PKTS == TRUE
+static BOOLEAN l2c_corrupt_the_fcr_packet (tL2C_CCB *p_ccb, BT_HDR *p_buf,
+ BOOLEAN is_rx, UINT16 ctrl_word);
+static BOOLEAN l2c_bypass_sframe_packet (tL2C_CCB *p_ccb);
+#endif
+
+#if (L2CAP_ERTM_STATS == TRUE)
+static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked);
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_fcr_updcrc
+**
+** Description This function computes the CRC using the look-up table.
+**
+** Returns CRC
+**
+*******************************************************************************/
+unsigned short l2c_fcr_updcrc(unsigned short icrc, unsigned char *icp, int icnt)
+{
+ register unsigned short crc = icrc;
+ register unsigned char *cp = icp;
+ register int cnt = icnt;
+
+ while (cnt--)
+ {
+ crc = ((crc >> 8) & 0xff) ^ crctab[(crc & 0xff) ^ *cp++];
+ }
+
+ return(crc);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_tx_get_fcs
+**
+** Description This function computes the CRC for a frame to be TXed.
+**
+** Returns CRC
+**
+*******************************************************************************/
+UINT16 l2c_fcr_tx_get_fcs (BT_HDR *p_buf)
+{
+ UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+
+ return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_rx_get_fcs
+**
+** Description This function computes the CRC for a received frame.
+**
+** Returns CRC
+**
+*******************************************************************************/
+UINT16 l2c_fcr_rx_get_fcs (BT_HDR *p_buf)
+{
+ UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+
+ /* offset points past the L2CAP header, but the CRC check includes it */
+ p -= L2CAP_PKT_OVERHEAD;
+
+ return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len + L2CAP_PKT_OVERHEAD));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_start_timer
+**
+** Description This function starts the (monitor or retransmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_start_timer (tL2C_CCB *p_ccb)
+{
+ UINT32 tout;
+
+ /* The timers which are in milliseconds */
+ if (p_ccb->fcrb.wait_ack)
+ {
+ tout = (UINT32)p_ccb->our_cfg.fcr.mon_tout;
+ }
+ else
+ {
+ tout = (UINT32)p_ccb->our_cfg.fcr.rtrans_tout;
+ }
+/*
+ L2CAP_TRACE_DEBUG4 ("l2c_fcr_start_timer Tout: %u Already Running: %u wait_ack: %u ack_q_count: %u",
+ tout, p_ccb->timer_entry.in_use, p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count);
+*/
+ /* Only start a timer that was not started */
+ if (p_ccb->fcrb.mon_retrans_timer.in_use == 0)
+ btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, tout*QUICK_TIMER_TICKS_PER_SEC/1000);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_stop_timer
+**
+** Description This function stops the (monitor or transmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_stop_timer (tL2C_CCB *p_ccb)
+{
+ if (p_ccb->fcrb.mon_retrans_timer.in_use)
+ {
+/*
+ L2CAP_TRACE_DEBUG2 ("l2c_fcr_stop_timer wait_ack: %u ack_q_count: %u",
+ p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count);
+*/
+ btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_cleanup
+**
+** Description This function cleans up the variable used for flow-control/retrans.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_cleanup (tL2C_CCB *p_ccb)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+
+ l2c_fcr_stop_timer (p_ccb);
+
+ if (p_fcrb->p_rx_sdu)
+ GKI_freebuf (p_fcrb->p_rx_sdu);
+
+ while (p_fcrb->waiting_for_ack_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_fcrb->waiting_for_ack_q));
+
+ while (p_fcrb->srej_rcv_hold_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_fcrb->srej_rcv_hold_q));
+
+ while (p_fcrb->retrans_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_fcrb->retrans_q));
+
+ btu_stop_quick_timer (&p_fcrb->ack_timer);
+ btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) )
+ {
+ UINT32 dur = GKI_get_os_tick_count() - p_ccb->fcrb.connect_tick_count;
+ char *p_str = (char *)GKI_getbuf(120);
+ UINT16 i;
+ UINT32 throughput_avg, ack_delay_avg, ack_q_count_avg;
+
+ dur = GKI_OS_TICKS_TO_MS(dur);
+ BT_TRACE_2(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "--- L2CAP ERTM Stats for CID: 0x%04x Duration: %08ums", p_ccb->local_cid, dur);
+ BT_TRACE_4(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "Retransmissions:%08u Times Flow Controlled:%08u Retrans Touts:%08u Ack Touts:%08u",
+ p_ccb->fcrb.pkts_retransmitted, p_ccb->fcrb.xmit_window_closed, p_ccb->fcrb.retrans_touts, p_ccb->fcrb.xmit_ack_touts);
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "Times there is less than 2 packets in controller when flow controlled:%08u", p_ccb->fcrb.controller_idle);
+ BT_TRACE_2(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "max_held_acks:%08u, in_cfg.fcr.tx_win_sz:%08u", p_ccb->fcrb.max_held_acks, p_ccb->peer_cfg.fcr.tx_win_sz );
+ if (p_str)
+ {
+ sprintf(p_str, "Sent Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u",
+ p_ccb->fcrb.ertm_pkt_counts[0], p_ccb->fcrb.ertm_byte_counts[0],
+ (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[0] * 100) / (dur / 10) : 0),
+ p_ccb->fcrb.s_frames_sent[0], p_ccb->fcrb.s_frames_sent[1], p_ccb->fcrb.s_frames_sent[2], p_ccb->fcrb.s_frames_sent[3]);
+
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ sprintf(p_str, "Rcvd Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u",
+ p_ccb->fcrb.ertm_pkt_counts[1], p_ccb->fcrb.ertm_byte_counts[1],
+ (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[1] * 100) / (dur / 10) : 0),
+ p_ccb->fcrb.s_frames_rcvd[0], p_ccb->fcrb.s_frames_rcvd[1], p_ccb->fcrb.s_frames_rcvd[2], p_ccb->fcrb.s_frames_rcvd[3]);
+
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ throughput_avg = 0;
+ ack_delay_avg = 0;
+ ack_q_count_avg = 0;
+
+ for (i = 0; i < L2CAP_ERTM_STATS_NUM_AVG; i++ )
+ {
+ if (i == p_ccb->fcrb.ack_delay_avg_index )
+ {
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "[%02u] collecting data ...", i );
+ continue;
+ }
+
+ sprintf(p_str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u",
+ i, p_ccb->fcrb.throughput[i],
+ p_ccb->fcrb.ack_delay_avg[i], p_ccb->fcrb.ack_delay_min[i], p_ccb->fcrb.ack_delay_max[i],
+ p_ccb->fcrb.ack_q_count_avg[i], p_ccb->fcrb.ack_q_count_min[i], p_ccb->fcrb.ack_q_count_max[i] );
+
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ throughput_avg += p_ccb->fcrb.throughput[i];
+ ack_delay_avg += p_ccb->fcrb.ack_delay_avg[i];
+ ack_q_count_avg += p_ccb->fcrb.ack_q_count_avg[i];
+ }
+
+ throughput_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+ ack_delay_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+ ack_q_count_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+
+ BT_TRACE_3(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "throughput_avg: %8u (kbytes/sec), ack_delay_avg: %8u ms, ack_q_count_avg: %8u",
+ throughput_avg, ack_delay_avg, ack_q_count_avg );
+
+ GKI_freebuf(p_str);
+ }
+
+ BT_TRACE_0(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "---");
+ }
+#endif
+
+ memset (p_fcrb, 0, sizeof (tL2C_FCRB));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_clone_buf
+**
+** Description This function allocates and copies requested part of a buffer
+** at a new-offset.
+**
+** Returns pointer to new buffer
+**
+*******************************************************************************/
+BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes, UINT8 pool)
+{
+ BT_HDR *p_buf2;
+
+ /* If using the common pool, should be at least 10% free. */
+ if ( (pool == HCI_ACL_POOL_ID) && (GKI_poolutilization (pool) > 90) )
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - failed to clone buffer on HCI_ACL_POOL_ID Utilization: %u", GKI_poolutilization(pool));
+ return (NULL);
+ }
+
+ if ((p_buf2 = (BT_HDR *)GKI_getpoolbuf(pool)) != NULL)
+ {
+ UINT16 pool_buf_size = GKI_get_pool_bufsize (pool);
+
+ /* Make sure buffer fits into buffer pool */
+ if ((no_of_bytes + sizeof(BT_HDR) + new_offset) > pool_buf_size)
+ {
+ L2CAP_TRACE_ERROR5("##### l2c_fcr_clone_buf (NumBytes %d) -> Exceeds poolsize %d [bytes %d + BT_HDR %d + offset %d]",
+ (no_of_bytes + sizeof(BT_HDR) + new_offset),
+ pool_buf_size, no_of_bytes, sizeof(BT_HDR),
+ new_offset);
+
+ GKI_freebuf(p_buf2);
+ return (NULL);
+ }
+
+ p_buf2->offset = new_offset;
+ p_buf2->len = no_of_bytes;
+
+ memcpy (((UINT8 *)(p_buf2 + 1)) + p_buf2->offset,
+ ((UINT8 *)(p_buf + 1)) + p_buf->offset,
+ no_of_bytes);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR2 ("L2CAP - failed to clone buffer, Pool: %u Count: %u", pool, GKI_poolfreecount(pool));
+ }
+
+ return (p_buf2);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_is_flow_controlled
+**
+** Description This function checks if the CCB is flow controlled by peer.
+**
+** Returns The control word
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb)
+{
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ /* Check if remote side flowed us off or the transmit window is full */
+ if ( (p_ccb->fcrb.remote_busy == TRUE)
+ || (p_ccb->fcrb.waiting_for_ack_q.count >= p_ccb->peer_cfg.fcr.tx_win_sz) )
+ {
+#if (L2CAP_ERTM_STATS == TRUE)
+ if (p_ccb->xmit_hold_q.count != 0)
+ {
+ p_ccb->fcrb.xmit_window_closed++;
+
+ if ((p_ccb->p_lcb->sent_not_acked < 2)&&(l2cb.controller_xmit_window > 0))
+ p_ccb->fcrb.controller_idle++;
+ }
+#endif
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function prepare_I_frame
+**
+** Description This function sets the FCR variables in an I-frame that is
+** about to be sent to HCI for transmission. This may be the
+** first time the I-frame is sent, or a retransmission
+**
+** Returns -
+**
+*******************************************************************************/
+static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 *p;
+ UINT16 fcs;
+ UINT16 ctrl_word;
+ BOOLEAN set_f_bit = p_fcrb->send_f_rsp;
+
+ p_fcrb->send_f_rsp = FALSE;
+
+ if (is_retransmission)
+ {
+ /* Get the old control word and clear out the old req_seq and F bits */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ ctrl_word &= ~(L2CAP_FCR_REQ_SEQ_BITS + L2CAP_FCR_F_BIT);
+ }
+ else
+ {
+ ctrl_word = p_buf->layer_specific & L2CAP_FCR_SEG_BITS; /* SAR bits */
+ ctrl_word |= (p_fcrb->next_tx_seq << L2CAP_FCR_TX_SEQ_BITS_SHIFT); /* Tx Seq */
+
+ p_fcrb->next_tx_seq = (p_fcrb->next_tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+ }
+
+ /* Set the F-bit and reqseq only if using re-transmission mode */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ if (set_f_bit)
+ ctrl_word |= L2CAP_FCR_F_BIT;
+
+ ctrl_word |= (p_fcrb->next_seq_expected) << L2CAP_FCR_REQ_SEQ_BITS_SHIFT;
+
+ p_fcrb->last_ack_sent = p_ccb->fcrb.next_seq_expected;
+
+ if (p_ccb->fcrb.ack_timer.in_use)
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+
+ /* Set the control word */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ UINT16_TO_STREAM (p, ctrl_word);
+
+ /* Compute the FCS and add to the end of the buffer if not bypassed */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ {
+ /* length field in l2cap header has to include FCS length */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset;
+ UINT16_TO_STREAM (p, p_buf->len + L2CAP_FCS_LEN - L2CAP_PKT_OVERHEAD);
+
+ /* Calculate the FCS */
+ fcs = l2c_fcr_tx_get_fcs(p_buf);
+
+ /* Point to the end of the buffer and put the FCS there */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len;
+
+ UINT16_TO_STREAM (p, fcs);
+
+ p_buf->len += L2CAP_FCS_LEN;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ if (is_retransmission)
+ {
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM ReTx I-frame CID: 0x%04x Len: %u SAR: %s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+ else
+ {
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM Tx I-frame CID: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+#endif
+
+ /* Start the retransmission timer if not already running */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ l2c_fcr_start_timer (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_send_S_frame
+**
+** Description This function formats and sends an S-frame for transmission.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 ctrl_word;
+ UINT16 fcs;
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ /* Only used for conformance testing */
+ if (l2c_bypass_sframe_packet (p_ccb))
+ return;
+#endif
+
+ if ((!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN))
+ return;
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.s_frames_sent[function_code]++;
+#endif
+
+ if (pf_bit == L2CAP_FCR_P_BIT)
+ {
+ p_ccb->fcrb.wait_ack = TRUE;
+
+ l2c_fcr_stop_timer (p_ccb); /* Restart the monitor timer */
+ l2c_fcr_start_timer (p_ccb);
+ }
+
+ /* Create the control word to use */
+ ctrl_word = (function_code << L2CAP_FCR_SUP_SHIFT) | L2CAP_FCR_S_FRAME_BIT;
+ ctrl_word |= (p_ccb->fcrb.next_seq_expected << L2CAP_FCR_REQ_SEQ_BITS_SHIFT);
+ ctrl_word |= pf_bit;
+
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID)) != NULL)
+ {
+ p_buf->offset = HCI_DATA_PREAMBLE_SIZE;
+ p_buf->len = L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* Put in the L2CAP header */
+ UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD + L2CAP_FCS_LEN);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, ctrl_word);
+
+ /* Compute the FCS and add to the end of the buffer if not bypassed */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ {
+ fcs = l2c_fcr_tx_get_fcs (p_buf);
+
+ UINT16_TO_STREAM (p, fcs);
+ p_buf->len += L2CAP_FCS_LEN;
+ }
+ else /* rewrite the length without FCS length */
+ {
+ p -= 6;
+ UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD);
+ }
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ /* Get out if packet was dropped */
+ if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, FALSE, ctrl_word))
+ return;
+#endif
+ /* Now, the HCI transport header */
+ p_buf->layer_specific = L2CAP_NON_FLUSHABLE_PKT;
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+
+#if BT_TRACE_VERBOSE == TRUE
+ if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1)
+ || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3))
+ {
+ L2CAP_TRACE_WARNING6 ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, ctrl_word,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ L2CAP_TRACE_WARNING1 (" Buf Len: %u", p_buf->len);
+ }
+ else
+ {
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, ctrl_word,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ L2CAP_TRACE_EVENT1 (" Buf Len: %u", p_buf->len);
+ }
+#endif /* BT_TRACE_VERBOSE */
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+
+ p_ccb->fcrb.last_ack_sent = p_ccb->fcrb.next_seq_expected;
+
+ if (p_ccb->fcrb.ack_timer.in_use)
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR2 ("l2c_fcr_send_S_frame(No Resources) cid 0x%04x, Type: 0x%4x",
+ p_ccb->local_cid, function_code);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_pdu
+**
+** Description This function is the entry point for processing of a
+** received PDU when in flow control and/or retransmission modes.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ UINT8 *p;
+ UINT16 fcs;
+ UINT16 min_pdu_len;
+ UINT16 ctrl_word;
+
+ /* Check the length */
+ min_pdu_len = (p_ccb->bypass_fcs == L2CAP_BYPASS_FCS) ?
+ (UINT16)L2CAP_FCR_OVERHEAD : (UINT16)(L2CAP_FCS_LEN + L2CAP_FCR_OVERHEAD);
+
+ if (p_buf->len < min_pdu_len)
+ {
+ L2CAP_TRACE_WARNING2 ("Rx L2CAP PDU: CID: 0x%04x Len too short: %u", p_ccb->local_cid, p_buf->len);
+ GKI_freebuf (p_buf);
+ return;
+ }
+
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_STREAM_MODE)
+ {
+ process_stream_frame (p_ccb, p_buf);
+ return;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset;
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ {
+ if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1)
+ || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3))
+ {
+ /* REJ or SREJ */
+ L2CAP_TRACE_WARNING6 ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+ else
+ {
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+
+ L2CAP_TRACE_EVENT6 (" eRTM Rx Nxt_tx_seq %u, Lst_rx_ack %u, Nxt_seq_exp %u, Lst_ack_snt %u, wt_q.cnt %u, tries %u",
+ p_ccb->fcrb.next_tx_seq, p_ccb->fcrb.last_rx_ack, p_ccb->fcrb.next_seq_expected,
+ p_ccb->fcrb.last_ack_sent, p_ccb->fcrb.waiting_for_ack_q.count, p_ccb->fcrb.num_tries);
+
+#endif /* BT_TRACE_VERBOSE */
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset;
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ /* Get out if packet was dropped */
+ if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, TRUE, ctrl_word))
+ return;
+#endif /* L2CAP_CORRUPT_ERTM_PKTS */
+
+ /* Verify FCS if using */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ {
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN;
+
+ /* Extract and drop the FCS from the packet */
+ STREAM_TO_UINT16 (fcs, p);
+ p_buf->len -= L2CAP_FCS_LEN;
+
+ if (l2c_fcr_rx_get_fcs(p_buf) != fcs)
+ {
+ L2CAP_TRACE_WARNING1 ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid);
+ GKI_freebuf(p_buf);
+ return;
+ }
+ }
+
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+
+ /* If we had a poll bit outstanding, check if we got a final response */
+ if (p_ccb->fcrb.wait_ack)
+ {
+ /* If final bit not set, ignore the frame unless it is a polled S-frame */
+ if ( !(ctrl_word & L2CAP_FCR_F_BIT) )
+ {
+ if ( (ctrl_word & L2CAP_FCR_P_BIT) && (ctrl_word & L2CAP_FCR_S_FRAME_BIT) )
+ {
+ if (p_ccb->fcrb.srej_sent)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
+ else if (p_ccb->fcrb.local_busy)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
+
+ /* Got a poll while in wait_ack state, so re-start our timer with 1-second */
+ /* This is a small optimization... the monitor timer is 12 secs, but we saw */
+ /* that if the other side sends us a poll when we are waiting for a final, */
+ /* then it speeds up recovery significantly if we poll him back soon after his poll. */
+ btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, QUICK_TIMER_TICKS_PER_SEC);
+ }
+ GKI_freebuf (p_buf);
+ return;
+ }
+
+ p_ccb->fcrb.wait_ack = FALSE;
+
+ /* P and F are mutually exclusive */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ ctrl_word &= ~L2CAP_FCR_P_BIT;
+
+ if (p_ccb->fcrb.waiting_for_ack_q.count == 0)
+ p_ccb->fcrb.num_tries = 0;
+
+ l2c_fcr_stop_timer (p_ccb);
+ }
+ else
+ {
+ /* Otherwise, ensure the final bit is ignored */
+ ctrl_word &= ~L2CAP_FCR_F_BIT;
+ }
+
+ /* Process receive sequence number */
+ if (!process_reqseq (p_ccb, ctrl_word))
+ {
+ GKI_freebuf (p_buf);
+ return;
+ }
+
+ /* Process based on whether it is an S-frame or an I-frame */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ process_s_frame (p_ccb, p_buf, ctrl_word);
+ else
+ process_i_frame (p_ccb, p_buf, ctrl_word, FALSE);
+
+ /* Return if the channel got disconnected by a bad packet or max retransmissions */
+ if ( (!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN) )
+ return;
+
+ /* If we have some buffers held while doing SREJ, and SREJ has cleared, process them now */
+ if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.srej_sent) && (p_ccb->fcrb.srej_rcv_hold_q.count > 0) )
+ {
+ BUFFER_Q temp_q = p_ccb->fcrb.srej_rcv_hold_q;
+
+ GKI_init_q (&p_ccb->fcrb.srej_rcv_hold_q);
+
+ while ((p_buf = (BT_HDR *)GKI_dequeue (&temp_q)) != NULL)
+ {
+ if (p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN))
+ {
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset - L2CAP_FCR_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ L2CAP_TRACE_DEBUG3 ("l2c_fcr_proc_pdu() CID: 0x%04x Process Buffer from SREJ_Hold_Q TxSeq: %u Expected_Seq: %u",
+ p_ccb->local_cid, (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ p_ccb->fcrb.next_seq_expected);
+
+ /* Process the SREJ held I-frame, but do not send an RR for each individual frame */
+ process_i_frame (p_ccb, p_buf, ctrl_word, TRUE);
+ }
+ else
+ GKI_freebuf (p_buf);
+
+ /* If more frames were lost during SREJ, send a REJ */
+ if (p_ccb->fcrb.rej_after_srej)
+ {
+ p_ccb->fcrb.rej_after_srej = FALSE;
+ p_ccb->fcrb.rej_sent = TRUE;
+
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0);
+ }
+ }
+
+ /* Now, if needed, send one RR for the whole held queue */
+ if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.rej_sent) && (!p_ccb->fcrb.srej_sent)
+ && (p_ccb->fcrb.next_seq_expected != p_ccb->fcrb.last_ack_sent) )
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ else
+ {
+ L2CAP_TRACE_DEBUG6 ("l2c_fcr_proc_pdu() not sending RR CID: 0x%04x local_busy:%d rej_sent:%d srej_sent:%d Expected_Seq:%u Last_Ack:%u",
+ p_ccb->local_cid, p_ccb->fcrb.local_busy, p_ccb->fcrb.rej_sent, p_ccb->fcrb.srej_sent, p_ccb->fcrb.next_seq_expected,
+ p_ccb->fcrb.last_ack_sent);
+ }
+ }
+
+ /* If a window has opened, check if we can send any more packets */
+ if ( (p_ccb->fcrb.retrans_q.count || p_ccb->xmit_hold_q.count)
+ && (p_ccb->fcrb.wait_ack == FALSE)
+ && (l2c_fcr_is_flow_controlled (p_ccb) == FALSE) )
+ {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_tout
+**
+** Description Handle a timeout. We should be in error recovery state.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_tout (tL2C_CCB *p_ccb)
+{
+ L2CAP_TRACE_DEBUG5 ("l2c_fcr_proc_tout: CID: 0x%04x num_tries: %u (max: %u) wait_ack: %u ack_q_count: %u",
+ p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit,
+ p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.retrans_touts++;
+#endif
+
+ if ( (p_ccb->peer_cfg.fcr.max_transmit != 0) && (++p_ccb->fcrb.num_tries > p_ccb->peer_cfg.fcr.max_transmit) )
+ {
+ l2cu_disconnect_chnl (p_ccb);
+ }
+ else
+ {
+ if (!p_ccb->fcrb.srej_sent && !p_ccb->fcrb.rej_sent)
+ {
+ if (p_ccb->fcrb.local_busy)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_P_BIT);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_ack_tout
+**
+** Description Send RR/RNR if we have not acked I frame
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb)
+{
+ L2CAP_TRACE_DEBUG5 ("l2c_fcr_proc_ack_tout: CID: 0x%04x State: %u Wack:%u Rq:%d Acked:%d", p_ccb->local_cid,
+ p_ccb->chnl_state, p_ccb->fcrb.wait_ack, p_ccb->fcrb.next_seq_expected, p_ccb->fcrb.last_ack_sent);
+
+ if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack)
+ && (p_ccb->fcrb.last_ack_sent != p_ccb->fcrb.next_seq_expected) )
+ {
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.xmit_ack_touts++;
+#endif
+ if (p_ccb->fcrb.local_busy)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function process_reqseq
+**
+** Description Handle receive sequence number
+**
+** Returns -
+**
+*******************************************************************************/
+static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 req_seq, num_bufs_acked, xx;
+ UINT16 ls;
+ UINT16 full_sdus_xmitted;
+
+ /* Receive sequence number does not ack anything for SREJ with P-bit set to zero */
+ if ( (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ && ((ctrl_word & L2CAP_FCR_SUP_BITS) == (L2CAP_FCR_SUP_SREJ << L2CAP_FCR_SUP_SHIFT))
+ && ((ctrl_word & L2CAP_FCR_P_BIT) == 0) )
+ {
+ /* If anything still waiting for ack, restart the timer if it was stopped */
+ if (p_fcrb->waiting_for_ack_q.count)
+ l2c_fcr_start_timer (p_ccb);
+
+ return (TRUE);
+ }
+
+ /* Extract the receive sequence number from the control word */
+ req_seq = (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT;
+
+ num_bufs_acked = (req_seq - p_fcrb->last_rx_ack) & L2CAP_FCR_SEQ_MODULO;
+
+ /* Verify the request sequence is in range before proceeding */
+ if (num_bufs_acked > p_fcrb->waiting_for_ack_q.count)
+ {
+ /* The channel is closed if ReqSeq is not in range */
+ L2CAP_TRACE_WARNING4 ("L2CAP eRTM Frame BAD Req_Seq - ctrl_word: 0x%04x req_seq 0x%02x last_rx_ack: 0x%02x QCount: %u",
+ ctrl_word, req_seq, p_fcrb->last_rx_ack, p_fcrb->waiting_for_ack_q.count);
+
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+/*
+ L2CAP_TRACE_DEBUG3 ("L2CAP process_reqseq 0x%02x last_rx_ack: 0x%02x QCount: %u",
+ req_seq, p_fcrb->last_rx_ack, p_fcrb->waiting_for_ack_q.count);
+*/
+ p_fcrb->last_rx_ack = req_seq;
+
+ /* Now we can release all acknowledged frames, and restart the retransmission timer if needed */
+ if (num_bufs_acked != 0)
+ {
+ p_fcrb->num_tries = 0;
+ full_sdus_xmitted = 0;
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ l2c_fcr_collect_ack_delay (p_ccb, num_bufs_acked);
+#endif
+
+ for (xx = 0; xx < num_bufs_acked; xx++)
+ {
+ ls = ((BT_HDR *)(p_fcrb->waiting_for_ack_q.p_first))->layer_specific & L2CAP_FCR_SAR_BITS;
+
+ if ( (ls == L2CAP_FCR_UNSEG_SDU) || (ls == L2CAP_FCR_END_SDU) )
+ full_sdus_xmitted++;
+
+ GKI_freebuf (GKI_dequeue (&p_fcrb->waiting_for_ack_q));
+ }
+
+ /* If we are still in a wait_ack state, do not mess with the timer */
+ if (!p_ccb->fcrb.wait_ack)
+ l2c_fcr_stop_timer (p_ccb);
+
+ /* Check if we need to call the "packet_sent" callback */
+ if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (full_sdus_xmitted) )
+ {
+ /* Special case for eRTM, if all packets sent, send 0xFFFF */
+ if ( (p_fcrb->waiting_for_ack_q.count == 0) && (p_ccb->xmit_hold_q.count == 0) )
+ full_sdus_xmitted = 0xFFFF;
+
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, full_sdus_xmitted);
+ }
+ }
+
+ /* If anything still waiting for ack, restart the timer if it was stopped */
+ if (p_fcrb->waiting_for_ack_q.count)
+ l2c_fcr_start_timer (p_ccb);
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function process_s_frame
+**
+** Description Process an S frame
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT16 s_frame_type = (ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT;
+ BOOLEAN remote_was_busy;
+ BOOLEAN all_ok = TRUE;
+
+ if (p_buf->len != 0)
+ {
+ L2CAP_TRACE_WARNING1 ("Incorrect S-frame Length (%d)", p_buf->len);
+ }
+
+ L2CAP_TRACE_DEBUG2 ("process_s_frame ctrl_word 0x%04x fcrb_remote_busy:%d", ctrl_word, p_fcrb->remote_busy);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.s_frames_rcvd[s_frame_type]++;
+#endif
+
+ if (ctrl_word & L2CAP_FCR_P_BIT)
+ {
+ p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send anoher REJ */
+ p_fcrb->send_f_rsp = TRUE; /* Set a flag in case an I-frame is pending */
+ }
+
+ switch (s_frame_type)
+ {
+ case L2CAP_FCR_SUP_RR:
+ remote_was_busy = p_fcrb->remote_busy;
+ p_fcrb->remote_busy = FALSE;
+
+ if ( (ctrl_word & L2CAP_FCR_F_BIT) || (remote_was_busy) )
+ all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS);
+ break;
+
+ case L2CAP_FCR_SUP_REJ:
+ p_fcrb->remote_busy = FALSE;
+ all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS);
+ break;
+
+ case L2CAP_FCR_SUP_RNR:
+ p_fcrb->remote_busy = TRUE;
+ l2c_fcr_stop_timer (p_ccb);
+ break;
+
+ case L2CAP_FCR_SUP_SREJ:
+ p_fcrb->remote_busy = FALSE;
+ all_ok = retransmit_i_frames (p_ccb, (UINT8)((ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT));
+ break;
+ }
+
+ if (all_ok)
+ {
+ /* If polled, we need to respond with F-bit. Note, we may have sent a I-frame with the F-bit */
+ if (p_fcrb->send_f_rsp)
+ {
+ if (p_fcrb->srej_sent)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
+ else if (p_fcrb->local_busy)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
+
+ p_fcrb->send_f_rsp = FALSE;
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG0 ("process_s_frame hit_max_retries");
+ }
+
+ GKI_freebuf (p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function process_i_frame
+**
+** Description Process an I frame
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 tx_seq, num_lost, num_to_ack, next_srej;
+
+ /* If we were doing checkpoint recovery, first retransmit all unacked I-frames */
+ if (ctrl_word & L2CAP_FCR_F_BIT)
+ {
+ if (!retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS))
+ {
+ GKI_freebuf(p_buf);
+ return;
+ }
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.ertm_pkt_counts[1]++;
+ p_ccb->fcrb.ertm_byte_counts[1] += p_buf->len;
+#endif
+
+ /* Extract the sequence number */
+ tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ /* If we have flow controlled the peer, ignore any bad I-frames from him */
+ if ( (tx_seq != p_fcrb->next_seq_expected) && (p_fcrb->local_busy) )
+ {
+ L2CAP_TRACE_WARNING1 ("Dropping bad I-Frame since we flowed off, tx_seq:%u", tx_seq);
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ GKI_freebuf(p_buf);
+ return;
+ }
+
+ /* If there are no free buffers in the user Rx queue, drop the */
+ /* received buffer now before we update any sequence numbers */
+ if (GKI_poolfreecount (p_ccb->ertm_info.user_rx_pool_id) == 0)
+ {
+ L2CAP_TRACE_WARNING4 ("L2CAP CID: 0x%04x Dropping I-Frame seq: %u User RX Pool: %u (Size: %u) has no free buffers!!",
+ p_ccb->local_cid, tx_seq, p_ccb->ertm_info.user_rx_pool_id,
+ GKI_poolcount (p_ccb->ertm_info.user_rx_pool_id));
+ GKI_freebuf(p_buf);
+ return;
+ }
+
+ /* Check if tx-sequence is the expected one */
+ if (tx_seq != p_fcrb->next_seq_expected)
+ {
+ num_lost = (tx_seq - p_fcrb->next_seq_expected) & L2CAP_FCR_SEQ_MODULO;
+
+ /* Is the frame a duplicate ? If so, just drop it */
+ if (num_lost >= p_ccb->our_cfg.fcr.tx_win_sz)
+ {
+ /* Duplicate - simply drop it */
+ L2CAP_TRACE_WARNING2 ("process_i_frame() Dropping Duplicate Frame tx_seq:%u ExpectedTxSeq %u", tx_seq, p_fcrb->next_seq_expected);
+ GKI_freebuf(p_buf);
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING6 ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej: %u",
+ p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent, p_fcrb->srej_sent);
+
+ if (p_fcrb->srej_sent)
+ {
+ /* If SREJ sent, save the frame for later processing as long as it is in sequence */
+ next_srej = (((BT_HDR *)p_fcrb->srej_rcv_hold_q.p_last)->layer_specific + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ if ( (tx_seq == next_srej) && (p_fcrb->srej_rcv_hold_q.count < p_ccb->our_cfg.fcr.tx_win_sz) )
+ {
+ /* If user gave us a pool for held rx buffers, use that */
+ if (p_ccb->ertm_info.fcr_rx_pool_id != HCI_ACL_POOL_ID)
+ {
+ BT_HDR *p_buf2;
+
+ /* Adjust offset and len so that control word is copied */
+ p_buf->offset -= L2CAP_FCR_OVERHEAD;
+ p_buf->len += L2CAP_FCR_OVERHEAD;
+
+ p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len, p_ccb->ertm_info.fcr_rx_pool_id);
+
+ if (p_buf2)
+ {
+ GKI_freebuf (p_buf);
+ p_buf = p_buf2;
+ }
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ }
+ L2CAP_TRACE_DEBUG4 ("process_i_frame() Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej1",
+ num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent);
+
+ p_buf->layer_specific = tx_seq;
+ GKI_enqueue (&p_fcrb->srej_rcv_hold_q, p_buf);
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING4 ("process_i_frame() CID: 0x%04x frame dropped in Srej Sent next_srej:%u hold_q.count:%u win_sz:%u",
+ p_ccb->local_cid, next_srej, p_fcrb->srej_rcv_hold_q.count, p_ccb->our_cfg.fcr.tx_win_sz);
+
+ p_fcrb->rej_after_srej = TRUE;
+ GKI_freebuf (p_buf);
+ }
+ }
+ else if (p_fcrb->rej_sent)
+ {
+ L2CAP_TRACE_WARNING5 ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: 1 SRej: %u",
+ p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->srej_sent);
+
+ /* If REJ sent, just drop the frame */
+ GKI_freebuf (p_buf);
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG4 ("process_i_frame() CID: 0x%04x tx_seq:%u ExpTxSeq %u Rej: %u",
+ p_ccb->local_cid, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent);
+
+ /* If only one lost, we will send SREJ, otherwise we will send REJ */
+ if (num_lost > 1)
+ {
+ GKI_freebuf (p_buf);
+ p_fcrb->rej_sent = TRUE;
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0);
+ }
+ else
+ {
+ if (p_fcrb->srej_rcv_hold_q.count != 0)
+ {
+ L2CAP_TRACE_ERROR3 ("process_i_frame() CID: 0x%04x sending SREJ tx_seq:%d hold_q.count:%u",
+ p_ccb->local_cid, tx_seq, p_fcrb->srej_rcv_hold_q.count);
+ }
+ p_buf->layer_specific = tx_seq;
+ GKI_enqueue (&p_fcrb->srej_rcv_hold_q, p_buf);
+ p_fcrb->srej_sent = TRUE;
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, 0);
+ }
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+ }
+ return;
+ }
+
+ /* Seq number is the next expected. Clear possible reject exception in case it occured */
+ p_fcrb->rej_sent = p_fcrb->srej_sent = FALSE;
+
+ /* Adjust the next_seq, so that if the upper layer sends more data in the callback
+ context, the received frame is acked by an I-frame. */
+ p_fcrb->next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ /* If any SAR problem in eRTM mode, spec says disconnect. */
+ if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word))
+ {
+ L2CAP_TRACE_WARNING1 ("process_i_frame() CID: 0x%04x reassembly failed", p_ccb->local_cid);
+ l2cu_disconnect_chnl (p_ccb);
+ return;
+ }
+
+ /* RR optimization - if peer can still send us more, then start an ACK timer */
+ num_to_ack = (p_fcrb->next_seq_expected - p_fcrb->last_ack_sent) & L2CAP_FCR_SEQ_MODULO;
+
+ if ( (num_to_ack < p_ccb->fcrb.max_held_acks) && (!p_fcrb->local_busy) )
+ delay_ack = TRUE;
+
+ /* We should neve never ack frame if we are not in OPEN state */
+ if ((num_to_ack != 0) && p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN))
+ {
+ /* If no frames are awaiting transmission or are held, send an RR or RNR S-frame for ack */
+ if (delay_ack)
+ {
+ /* If it is the first I frame we did not ack, start ack timer */
+ if (!p_ccb->fcrb.ack_timer.in_use)
+ {
+ btu_start_quick_timer (&p_ccb->fcrb.ack_timer, BTU_TTYPE_L2CAP_FCR_ACK,
+ (L2CAP_FCR_ACK_TOUT*QUICK_TIMER_TICKS_PER_SEC)/1000);
+ }
+ }
+ else if ( ((p_ccb->xmit_hold_q.count == 0) || (l2c_fcr_is_flow_controlled (p_ccb)))
+ && (p_ccb->fcrb.srej_rcv_hold_q.count == 0) )
+ {
+ if (p_fcrb->local_busy)
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ else
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function process_stream_frame
+**
+** Description This function processes frames in streaming mode
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ UINT16 ctrl_word;
+ UINT16 fcs;
+ UINT8 *p;
+ UINT8 tx_seq;
+
+ /* Verify FCS if using */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ {
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN;
+
+ /* Extract and drop the FCS from the packet */
+ STREAM_TO_UINT16 (fcs, p);
+ p_buf->len -= L2CAP_FCS_LEN;
+
+ if (l2c_fcr_rx_get_fcs(p_buf) != fcs)
+ {
+ L2CAP_TRACE_WARNING1 ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid);
+ GKI_freebuf(p_buf);
+ return;
+ }
+ }
+
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf+1)) + p_buf->offset;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+
+ /* Make sure it is an I-frame */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ {
+ L2CAP_TRACE_WARNING2 ("Rx L2CAP PDU: CID: 0x%04x BAD S-frame in streaming mode ctrl_word: 0x%04x", p_ccb->local_cid, ctrl_word);
+ GKI_freebuf (p_buf);
+ return;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+#endif
+
+ /* Extract the sequence number */
+ tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ /* Check if tx-sequence is the expected one */
+ if (tx_seq != p_ccb->fcrb.next_seq_expected)
+ {
+ L2CAP_TRACE_WARNING4 ("Rx L2CAP PDU: CID: 0x%04x Lost frames Exp: %u Got: %u p_rx_sdu: 0x%08x",
+ p_ccb->local_cid, p_ccb->fcrb.next_seq_expected, tx_seq, p_ccb->fcrb.p_rx_sdu);
+
+ /* Lost one or more packets, so flush the SAR queue */
+ if (p_ccb->fcrb.p_rx_sdu != NULL)
+ {
+ GKI_freebuf (p_ccb->fcrb.p_rx_sdu);
+ p_ccb->fcrb.p_rx_sdu = NULL;
+ }
+ }
+
+ p_ccb->fcrb.next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word))
+ {
+ /* Some sort of SAR error, so flush the SAR queue */
+ if (p_ccb->fcrb.p_rx_sdu != NULL)
+ {
+ GKI_freebuf (p_ccb->fcrb.p_rx_sdu);
+ p_ccb->fcrb.p_rx_sdu = NULL;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function do_sar_reassembly
+**
+** Description Process SAR bits and re-assemble frame
+**
+** Returns TRUE if all OK, else FALSE
+**
+*******************************************************************************/
+static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word)
+{
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT16 sar_type = ctrl_word & L2CAP_FCR_SEG_BITS;
+ BOOLEAN packet_ok = TRUE;
+ UINT8 *p;
+
+ /* Check if the SAR state is correct */
+ if ((sar_type == L2CAP_FCR_UNSEG_SDU) || (sar_type == L2CAP_FCR_START_SDU))
+ {
+ if (p_fcrb->p_rx_sdu != NULL)
+ {
+ L2CAP_TRACE_WARNING2 ("SAR - got unexpected unsegmented or start SDU Expected len: %u Got so far: %u",
+ p_fcrb->rx_sdu_len, p_fcrb->p_rx_sdu->len);
+
+ packet_ok = FALSE;
+ }
+ /* Check the length of the packet */
+ if ( (sar_type == L2CAP_FCR_START_SDU) && (p_buf->len < L2CAP_SDU_LEN_OVERHEAD) )
+ {
+ L2CAP_TRACE_WARNING1 ("SAR start packet too short: %u", p_buf->len);
+ packet_ok = FALSE;
+ }
+ }
+ else
+ {
+ if (p_fcrb->p_rx_sdu == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("SAR - got unexpected cont or end SDU");
+ packet_ok = FALSE;
+ }
+ }
+
+ if ( (packet_ok) && (sar_type != L2CAP_FCR_UNSEG_SDU) )
+ {
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+
+ /* For start SDU packet, extract the SDU length */
+ if (sar_type == L2CAP_FCR_START_SDU)
+ {
+ /* 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->max_rx_mtu)
+ {
+ L2CAP_TRACE_WARNING2 ("SAR - SDU len: %u larger than MTU: %u", p_fcrb->rx_sdu_len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ }
+ else if ((p_fcrb->p_rx_sdu = (BT_HDR *)GKI_getpoolbuf (p_ccb->ertm_info.user_rx_pool_id)) == NULL)
+ {
+ L2CAP_TRACE_ERROR1 ("SAR - 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 = 4; /* this is the minimal offset required by OBX to process incoming packets */
+ p_fcrb->p_rx_sdu->len = 0;
+ }
+ }
+
+ if (packet_ok)
+ {
+ if ((p_fcrb->p_rx_sdu->len + p_buf->len) > p_fcrb->rx_sdu_len)
+ {
+ L2CAP_TRACE_ERROR4 ("SAR - SDU len exceeded Type: %u Lengths: %u %u %u",
+ sar_type, p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ }
+ else if ( (sar_type == L2CAP_FCR_END_SDU) && ((p_fcrb->p_rx_sdu->len + p_buf->len) != p_fcrb->rx_sdu_len) )
+ {
+ L2CAP_TRACE_WARNING3 ("SAR - SDU end rcvd but SDU incomplete: %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 (sar_type == L2CAP_FCR_END_SDU)
+ {
+ 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)
+ {
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_ccb->local_cid < L2CAP_BASE_APPL_CID)
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_ccb->p_lcb->remote_bd_addr, p_buf);
+ else
+#endif
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_buf);
+ }
+
+ return (packet_ok);
+}
+
+
+/*******************************************************************************
+**
+** Function retransmit_i_frames
+**
+** Description This function retransmits i-frames awaiting acks.
+**
+** Returns BOOLEAN - TRUE if retransmitted
+**
+*******************************************************************************/
+static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq)
+{
+ BT_HDR *p_buf, *p_buf2;
+ UINT8 *p;
+ UINT8 buf_seq;
+ UINT16 ctrl_word;
+
+ if ( (p_ccb->fcrb.waiting_for_ack_q.p_first)
+ && (p_ccb->peer_cfg.fcr.max_transmit != 0)
+ && (p_ccb->fcrb.num_tries >= p_ccb->peer_cfg.fcr.max_transmit) )
+ {
+ L2CAP_TRACE_EVENT5 ("Max Tries Exceeded: (last_acq: %d CID: 0x%04x num_tries: %u (max: %u) ack_q_count: %u",
+ p_ccb->fcrb.last_rx_ack, p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit,
+ p_ccb->fcrb.waiting_for_ack_q.count);
+
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+ /* tx_seq indicates whether to retransmit a specific sequence or all (if == L2C_FCR_RETX_ALL_PKTS) */
+ if (tx_seq != L2C_FCR_RETX_ALL_PKTS)
+ {
+ /* If sending only one, the sequence number tells us which one. Look for it.
+ */
+ for (p_buf = (BT_HDR *)p_ccb->fcrb.waiting_for_ack_q.p_first; p_buf; p_buf = (BT_HDR *)GKI_getnext (p_buf))
+ {
+ /* Get the old control word */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ buf_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ L2CAP_TRACE_DEBUG2 ("retransmit_i_frames() cur seq: %u looking for: %u", buf_seq, tx_seq);
+
+ if (tx_seq == buf_seq)
+ break;
+ }
+
+ if (!p_buf)
+ {
+ L2CAP_TRACE_ERROR2 ("retransmit_i_frames() UNKNOWN seq: %u q_count: %u", tx_seq, p_ccb->fcrb.waiting_for_ack_q.count);
+ return (TRUE);
+ }
+ }
+ else
+ {
+ /* Retransmitting everything. Flush buffers we already put in the link xmit queue.
+ */
+ p_buf = (BT_HDR *)p_ccb->p_lcb->link_xmit_data_q.p_first;
+
+ while (p_buf != NULL)
+ {
+ /* Do not flush other CIDs or partial segments */
+ if ( (p_buf->layer_specific == 0) && (p_buf->event == p_ccb->local_cid) )
+ {
+ p_buf2 = p_buf;
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+
+ GKI_remove_from_queue (&p_ccb->p_lcb->link_xmit_data_q, p_buf2);
+ GKI_freebuf (p_buf2);
+ }
+ else
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ }
+
+ /* Also flush our retransmission queue */
+ while (p_ccb->fcrb.retrans_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_ccb->fcrb.retrans_q));
+
+ p_buf = (BT_HDR *)p_ccb->fcrb.waiting_for_ack_q.p_first;
+ }
+
+ while (p_buf != NULL)
+ {
+ p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len, p_ccb->ertm_info.fcr_tx_pool_id);
+
+ if (p_buf2)
+ {
+ p_buf2->layer_specific = p_buf->layer_specific;
+
+ GKI_enqueue (&p_ccb->fcrb.retrans_q, p_buf2);
+ }
+
+ if ( (tx_seq != L2C_FCR_RETX_ALL_PKTS) || (p_buf2 == NULL) )
+ break;
+ else
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+
+ if (p_ccb->fcrb.waiting_for_ack_q.count)
+ {
+ p_ccb->fcrb.num_tries++;
+ l2c_fcr_start_timer (p_ccb);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_get_next_xmit_sdu_seg
+**
+** Description Get the next SDU segment to transmit.
+**
+** Returns pointer to buffer with segment or NULL
+**
+*******************************************************************************/
+BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length)
+{
+ BOOLEAN first_seg = FALSE, /* The segment is the first part of data */
+ mid_seg = FALSE, /* The segment is the middle part of data */
+ last_seg = FALSE; /* The segment is the last part of data */
+ UINT16 sdu_len;
+ BT_HDR *p_buf, *p_xmit;
+ UINT8 *p;
+ UINT16 max_pdu = p_ccb->tx_mps /* Needed? - L2CAP_MAX_HEADER_FCS*/;
+
+ /* If there is anything in the retransmit queue, that goes first
+ */
+ if (p_ccb->fcrb.retrans_q.p_first)
+ {
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->fcrb.retrans_q);
+
+ /* Update Rx Seq and FCS if we acked some packets while this one was queued */
+ prepare_I_frame (p_ccb, p_buf, TRUE);
+
+ p_buf->event = p_ccb->local_cid;
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ /* Use sdu_len to hold the control word */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+ STREAM_TO_UINT16 (sdu_len, p);
+
+ /* Get out if packet was dropped; just pretend it went out */
+ if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, FALSE, sdu_len))
+ return (NULL);
+#endif /* L2CAP_CORRUPT_ERTM_PKTS */
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.pkts_retransmitted++;
+ p_ccb->fcrb.ertm_pkt_counts[0]++;
+ p_ccb->fcrb.ertm_byte_counts[0] += (p_buf->len - 8);
+#endif
+ return (p_buf);
+ }
+
+ /* For BD/EDR controller, max_packet_length is set to 0 */
+ /* For AMP controller, max_packet_length is set by available blocks */
+ if ( (max_packet_length > L2CAP_MAX_HEADER_FCS)
+ && (max_pdu + L2CAP_MAX_HEADER_FCS > max_packet_length) )
+ {
+ max_pdu = max_packet_length - L2CAP_MAX_HEADER_FCS;
+ }
+
+ p_buf = (BT_HDR *)p_ccb->xmit_hold_q.p_first;
+
+ /* If there is more data than the MPS, it requires segmentation */
+ if (p_buf->len > max_pdu)
+ {
+ /* We are using the "event" field to tell is if we already started segmentation */
+ if (p_buf->event == 0)
+ {
+ first_seg = TRUE;
+ sdu_len = p_buf->len;
+ }
+ else
+ mid_seg = TRUE;
+
+ /* Get a new buffer and copy the data that can be sent in a PDU */
+ p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET,
+ max_pdu, p_ccb->ertm_info.fcr_tx_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;
+
+ /* copy PBF setting */
+ p_xmit->layer_specific = p_buf->layer_specific;
+ }
+ else /* Should never happen if the application has configured buffers correctly */
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - cannot get buffer, for segmentation, pool: %u", p_ccb->ertm_info.fcr_tx_pool_id);
+ return (NULL);
+ }
+ }
+ else /* Use the original buffer if no segmentation, or the last segment */
+ {
+ p_xmit = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+
+ if (p_xmit->event != 0)
+ last_seg = TRUE;
+
+ p_xmit->event = p_ccb->local_cid;
+ }
+
+ /* Step back to add the L2CAP headers */
+ p_xmit->offset -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD);
+ p_xmit->len += L2CAP_PKT_OVERHEAD + L2CAP_FCR_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 */
+
+ /* Note: if FCS has to be included then the length is recalculated later */
+ UINT16_TO_STREAM (p, p_xmit->len - L2CAP_PKT_OVERHEAD);
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+
+ if (first_seg)
+ {
+ /* Skip control word and add SDU length */
+ p += 2;
+ UINT16_TO_STREAM (p, sdu_len);
+
+ /* We will store the SAR type in layer-specific */
+ /* layer_specific is shared with flushable flag(bits 0-1), don't clear it */
+ p_xmit->layer_specific |= L2CAP_FCR_START_SDU;
+
+ first_seg = FALSE;
+ }
+ else if (mid_seg)
+ p_xmit->layer_specific |= L2CAP_FCR_CONT_SDU;
+ else if (last_seg)
+ p_xmit->layer_specific |= L2CAP_FCR_END_SDU;
+ else
+ p_xmit->layer_specific |= L2CAP_FCR_UNSEG_SDU;
+
+ prepare_I_frame (p_ccb, p_xmit, FALSE);
+
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ BT_HDR *p_wack = l2c_fcr_clone_buf (p_xmit, HCI_DATA_PREAMBLE_SIZE, p_xmit->len, p_ccb->ertm_info.fcr_tx_pool_id);
+
+ if (!p_wack)
+ {
+ L2CAP_TRACE_ERROR3 ("L2CAP - no buffer for xmit cloning, CID: 0x%04x Pool: %u Count: %u",
+ p_ccb->local_cid, p_ccb->ertm_info.fcr_tx_pool_id, GKI_poolfreecount(p_ccb->ertm_info.fcr_tx_pool_id));
+
+ /* We will not save the FCS in case we reconfigure and change options */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ p_xmit->len -= L2CAP_FCS_LEN;
+
+ /* Pretend we sent it and it got lost */
+ GKI_enqueue (&p_ccb->fcrb.waiting_for_ack_q, p_xmit);
+ return (NULL);
+ }
+ else
+ {
+#if (L2CAP_ERTM_STATS == TRUE)
+ /* set timestamp at the end of tx I-frame to get acking delay */
+ p = ((UINT8 *) (p_wack+1)) + p_wack->offset + p_wack->len;
+ UINT32_TO_STREAM (p, GKI_get_os_tick_count());
+#endif
+ /* We will not save the FCS in case we reconfigure and change options */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ p_wack->len -= L2CAP_FCS_LEN;
+
+ p_wack->layer_specific = p_xmit->layer_specific;
+ GKI_enqueue (&p_ccb->fcrb.waiting_for_ack_q, p_wack);
+ }
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ {
+ UINT16 ctrl_word;
+ p = ((UINT8 *) (p_xmit+1)) + p_xmit->offset + L2CAP_PKT_OVERHEAD;
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ /* Get out if packet was dropped; pretend it was sent */
+ if (l2c_corrupt_the_fcr_packet (p_ccb, p_xmit, FALSE, ctrl_word))
+ return (NULL);
+ }
+#endif
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.ertm_pkt_counts[0]++;
+ p_ccb->fcrb.ertm_byte_counts[0] += (p_xmit->len - 8);
+#endif
+
+ }
+
+ return (p_xmit);
+}
+
+
+/*******************************************************************************
+** Configuration negotiation functions
+**
+** The following functions are used in negotiating channel modes during
+** configuration
+********************************************************************************/
+
+/*******************************************************************************
+**
+** Function l2c_fcr_chk_chan_modes
+**
+** Description Validates and adjusts if necessary, the FCR options
+** based on remote EXT features.
+**
+** Note: This assumes peer EXT Features have been received.
+** Basic mode is used if FCR Options have not been received
+**
+** Returns UINT8 - nonzero if can continue, '0' if no compatible channels
+**
+*******************************************************************************/
+UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb)
+{
+ /* Remove nonbasic options that the peer does not support */
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_ENH_RETRANS))
+ p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_ERTM;
+
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_STREAM_MODE))
+ p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_STREAM;
+
+ /* At least one type needs to be set (Basic, ERTM, STM) to continue */
+ if (!p_ccb->ertm_info.allowed_modes)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - Peer does not support our desired channel types");
+ }
+
+ return (p_ccb->ertm_info.allowed_modes);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_our_req_options
+**
+** Description Validates and sets up the FCR options passed in from
+** L2CA_ConfigReq based on remote device's features.
+**
+** Returns TRUE if no errors, Otherwise FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2CAP_FCR_OPTS *p_fcr = &p_cfg->fcr;
+
+ if (p_fcr->mode != p_ccb->ertm_info.preferred_mode)
+ {
+ L2CAP_TRACE_WARNING2 ("l2c_fcr_adj_our_req_options - preferred_mode (%d), does not match mode (%d)",
+ p_ccb->ertm_info.preferred_mode, p_fcr->mode);
+
+ /* The preferred mode is passed in through tL2CAP_ERTM_INFO, so override this one */
+ p_fcr->mode = p_ccb->ertm_info.preferred_mode;
+ }
+
+ /* If upper layer did not request eRTM mode, BASIC must be used */
+ if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC)
+ {
+ if (p_cfg->fcr_present && p_fcr->mode != L2CAP_FCR_BASIC_MODE)
+ {
+ L2CAP_TRACE_WARNING1 ("l2c_fcr_adj_our_req_options (mode %d): ERROR: No FCR options set using BASIC mode", p_fcr->mode);
+ }
+ p_fcr->mode = L2CAP_FCR_BASIC_MODE;
+ }
+
+ /* Process the FCR options if initial channel bring-up (not a reconfig request)
+ ** Determine initial channel mode to try based on our options and remote's features
+ */
+ if (p_cfg->fcr_present && !(p_ccb->config_done & RECONFIG_FLAG))
+ {
+ /* We need to have at least one mode type common with the peer */
+ if (!l2c_fcr_chk_chan_modes(p_ccb))
+ {
+ /* Two channels have incompatible supported types */
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+ /* Basic is the only common channel mode between the two devices */
+ else if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC)
+ {
+ /* We only want to try Basic, so bypass sending the FCR options entirely */
+ p_cfg->fcr_present = FALSE;
+ p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */
+ p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */
+ }
+
+ /* We have at least one non-basic mode available
+ * Override mode from available mode options based on preference, if needed
+ */
+ else
+ {
+ /* If peer does not support STREAMING, try ERTM */
+ if (p_fcr->mode == L2CAP_FCR_STREAM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_STREAM))
+ {
+ L2CAP_TRACE_DEBUG0 ("L2C CFG: mode is STREAM, but peer does not support; Try ERTM");
+ p_fcr->mode = L2CAP_FCR_ERTM_MODE;
+ }
+
+ /* If peer does not support ERTM, try BASIC (will support this if made it here in the code) */
+ if (p_fcr->mode == L2CAP_FCR_ERTM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM))
+ {
+ L2CAP_TRACE_DEBUG0 ("L2C CFG: mode is ERTM, but peer does not support; Try BASIC");
+ p_fcr->mode = L2CAP_FCR_BASIC_MODE;
+ }
+ }
+
+ if (p_fcr->mode != L2CAP_FCR_BASIC_MODE)
+ {
+ /* MTU must be smaller than buffer size */
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > p_ccb->max_rx_mtu) )
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - MTU: %u larger than buf size: %u", p_cfg->mtu, p_ccb->max_rx_mtu);
+ return (FALSE);
+ }
+
+ /* application want to use the default MPS */
+ if (p_fcr->mps == L2CAP_DEFAULT_ERM_MPS)
+ {
+ p_fcr->mps = L2CAP_MPS_OVER_BR_EDR;
+ }
+ /* MPS must be less than MTU */
+ else if (p_fcr->mps > p_ccb->max_rx_mtu)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - MPS %u invalid MTU: %u", p_fcr->mps, p_ccb->max_rx_mtu);
+ return (FALSE);
+ }
+
+ /* We always initially read into the HCI buffer pool, so make sure it fits */
+ if (p_fcr->mps > (L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS))
+ p_fcr->mps = L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS;
+ }
+ else
+ {
+ p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */
+ p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */
+ }
+
+ p_ccb->our_cfg.fcr = *p_fcr;
+ }
+ else /* Not sure how to send a reconfiguration(??) should fcr be included? */
+ {
+ p_ccb->our_cfg.fcr_present = FALSE;
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_monitor_retran_timeout
+**
+** Description Overrides monitor/retrans timer value based on controller
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb)
+{
+ /* adjust our monitor/retran timeout */
+ if (p_ccb->out_cfg_fcr_present)
+ {
+ /*
+ ** if we requestd ERTM or accepted ERTM
+ ** We may accept ERTM even if we didn't request ERTM, in case of requesting STREAM
+ */
+ if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ ||(p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE))
+ {
+ /* upper layer setting is ignored */
+ p_ccb->our_cfg.fcr.mon_tout = L2CAP_MIN_MONITOR_TOUT;
+ p_ccb->our_cfg.fcr.rtrans_tout = L2CAP_MIN_RETRANS_TOUT;
+ }
+ else
+ {
+ p_ccb->our_cfg.fcr.mon_tout = 0;
+ p_ccb->our_cfg.fcr.rtrans_tout = 0;
+ }
+
+ L2CAP_TRACE_DEBUG2 ("l2c_fcr_adj_monitor_retran_timeout: mon_tout:%d, rtrans_tout:%d",
+ p_ccb->our_cfg.fcr.mon_tout, p_ccb->our_cfg.fcr.rtrans_tout);
+ }
+}
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_our_rsp_options
+**
+** Description Overrides any neccesary FCR options passed in from
+** L2CA_ConfigRsp based on our FCR options.
+** Only makes adjustments if channel is in ERTM mode.
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* adjust our monitor/retran timeout */
+ l2c_fcr_adj_monitor_retran_timeout(p_ccb);
+
+ p_cfg->fcr_present = p_ccb->out_cfg_fcr_present;
+
+ if (p_cfg->fcr_present)
+ {
+// btla-specific ++
+ /* Temporary - until a better algorithm is implemented */
+ /* If peer's tx_wnd_sz requires too many buffers for us to support, then adjust it. For now, respond with our own tx_wnd_sz. */
+ /* Note: peer is not guaranteed to obey our adjustment */
+ if (p_ccb->peer_cfg.fcr.tx_win_sz > p_ccb->our_cfg.fcr.tx_win_sz)
+ {
+ L2CAP_TRACE_DEBUG3 ("%s: adjusting requested tx_win_sz from %i to %i", __FUNCTION__, p_ccb->peer_cfg.fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz);
+ p_ccb->peer_cfg.fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz;
+ }
+// btla-specific --
+
+ p_cfg->fcr.mode = p_ccb->peer_cfg.fcr.mode;
+ p_cfg->fcr.tx_win_sz = p_ccb->peer_cfg.fcr.tx_win_sz;
+ p_cfg->fcr.max_transmit = p_ccb->peer_cfg.fcr.max_transmit;
+ p_cfg->fcr.mps = p_ccb->peer_cfg.fcr.mps;
+ p_cfg->fcr.rtrans_tout = p_ccb->our_cfg.fcr.rtrans_tout;
+ p_cfg->fcr.mon_tout = p_ccb->our_cfg.fcr.mon_tout;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_renegotiate_chan
+**
+** Description Called upon unsuccessful peer response to config request.
+** If the error is because of the channel mode, it will try
+** to resend using another supported optional channel.
+**
+** Returns TRUE if resent configuration, False if channel matches or
+** cannot match.
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ UINT8 peer_mode = p_ccb->our_cfg.fcr.mode;
+ BOOLEAN can_renegotiate;
+
+ /* Skip if this is a reconfiguration from OPEN STATE or if FCR is not returned */
+ if (!p_cfg->fcr_present || (p_ccb->config_done & RECONFIG_FLAG))
+ return (FALSE);
+
+ /* Only retry if there are more channel options to try */
+ if (p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS)
+ {
+ peer_mode = (p_cfg->fcr_present) ? p_cfg->fcr.mode : L2CAP_FCR_BASIC_MODE;
+
+ if (p_ccb->our_cfg.fcr.mode != peer_mode)
+ {
+
+ if ((--p_ccb->fcr_cfg_tries) == 0)
+ {
+ p_cfg->result = L2CAP_CFG_FAILED_NO_REASON;
+ L2CAP_TRACE_WARNING0 ("l2c_fcr_renegotiate_chan (Max retries exceeded)");
+ }
+
+ can_renegotiate = FALSE;
+
+ /* Try another supported mode if available based on our last attempted channel */
+ switch (p_ccb->our_cfg.fcr.mode)
+ {
+ /* Our Streaming mode request was unnacceptable; try ERTM or Basic */
+ case L2CAP_FCR_STREAM_MODE:
+ /* Peer wants ERTM and we support it */
+ if ( (peer_mode == L2CAP_FCR_ERTM_MODE) && (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) )
+ {
+ L2CAP_TRACE_DEBUG0 ("l2c_fcr_renegotiate_chan(Trying ERTM)");
+ p_ccb->our_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE;
+ can_renegotiate = TRUE;
+ }
+ else /* Falls through */
+
+ case L2CAP_FCR_ERTM_MODE:
+ {
+ /* We can try basic for any other peer mode if we support it */
+ if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC)
+ {
+ L2CAP_TRACE_DEBUG0 ("l2c_fcr_renegotiate_chan(Trying Basic)");
+ can_renegotiate = TRUE;
+ p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+ }
+ }
+ break;
+
+ default:
+ /* All other scenarios cannot be renegotiated */
+ break;
+ }
+
+ if (can_renegotiate)
+ {
+ p_ccb->our_cfg.fcr_present = TRUE;
+
+ if (p_ccb->our_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
+ {
+ p_ccb->our_cfg.fcs_present = FALSE;
+ p_ccb->our_cfg.ext_flow_spec_present = FALSE;
+
+ /* Basic Mode uses ACL Data Pool, make sure the MTU fits */
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) )
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - adjust MTU: %u too large", p_cfg->mtu);
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+ }
+ }
+
+ l2cu_process_our_cfg_req (p_ccb, &p_ccb->our_cfg);
+ l2cu_send_peer_config_req (p_ccb, &p_ccb->our_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ return (TRUE);
+ }
+ }
+ }
+
+ /* Disconnect if the channels do not match */
+ if (p_ccb->our_cfg.fcr.mode != peer_mode)
+ {
+ L2CAP_TRACE_WARNING2 ("L2C CFG: Channels incompatible (local %d, peer %d)",
+ p_ccb->our_cfg.fcr.mode, peer_mode);
+ l2cu_disconnect_chnl (p_ccb);
+ }
+
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_process_peer_cfg_req
+**
+** Description This function is called to process the FCR options passed
+** in the peer's configuration request.
+**
+** Returns UINT8 - L2CAP_PEER_CFG_OK, L2CAP_PEER_CFG_UNACCEPTABLE,
+** or L2CAP_PEER_CFG_DISCONNECT.
+**
+*******************************************************************************/
+UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ UINT16 max_retrans_size;
+ UINT8 fcr_ok = L2CAP_PEER_CFG_OK;
+
+ p_ccb->p_lcb->w4_info_rsp = FALSE; /* Handles T61x SonyEricsson Bug in Info Request */
+
+ L2CAP_TRACE_EVENT5 ("l2c_fcr_process_peer_cfg_req() CFG fcr_present:%d fcr.mode:%d CCB FCR mode:%d preferred: %u allowed:%u",
+ p_cfg->fcr_present, p_cfg->fcr.mode, p_ccb->our_cfg.fcr.mode, p_ccb->ertm_info.preferred_mode,
+ p_ccb->ertm_info.allowed_modes);
+
+ /* If Peer wants basic, we are done (accept it or disconnect) */
+ if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE)
+ {
+ /* If we do not allow basic, disconnect */
+ if ( !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) )
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ }
+
+ /* Need to negotiate if our modes are not the same */
+ else if (p_cfg->fcr.mode != p_ccb->ertm_info.preferred_mode)
+ {
+ /* If peer wants a mode that we don't support then retry our mode (ex. rtx/flc), OR
+ ** If we want ERTM and they wanted streaming retry our mode.
+ ** Note: If we have already determined they support our mode previously
+ ** from their EXF mask.
+ */
+ if ( (((1 << p_cfg->fcr.mode) & L2CAP_FCR_CHAN_OPT_ALL_MASK) == 0)
+ || (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_ERTM_MODE) )
+ {
+ p_cfg->fcr.mode = p_ccb->our_cfg.fcr.mode;
+ p_cfg->fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz;
+ p_cfg->fcr.max_transmit = p_ccb->our_cfg.fcr.max_transmit;
+ fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE;
+ }
+
+ /* If we wanted basic, then try to renegotiate it */
+ else if (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_BASIC_MODE)
+ {
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0;
+ p_cfg->fcr.rtrans_tout = p_cfg->fcr.mon_tout = p_cfg->fcr.mps = 0;
+ p_ccb->our_cfg.fcr.rtrans_tout = p_ccb->our_cfg.fcr.mon_tout = p_ccb->our_cfg.fcr.mps = 0;
+ fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE;
+ }
+
+ /* Only other valid case is if they want ERTM and we wanted STM which should be
+ accepted if we support it; otherwise the channel should be disconnected */
+ else if ( (p_cfg->fcr.mode != L2CAP_FCR_ERTM_MODE)
+ || !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) )
+ {
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ }
+ }
+
+ /* Configuration for FCR channels so make any adjustments and fwd to upper layer */
+ if (fcr_ok == L2CAP_PEER_CFG_OK)
+ {
+ /* by default don't need to send params in the response */
+ p_ccb->out_cfg_fcr_present = FALSE;
+
+ /* Make any needed adjustments for the response to the peer */
+ if (p_cfg->fcr_present && p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ /* Peer desires to bypass FCS check, and streaming or ERTM mode */
+ if (p_cfg->fcs_present)
+ {
+ p_ccb->peer_cfg.fcs = p_cfg->fcs;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
+ if( p_cfg->fcs == L2CAP_CFG_FCS_BYPASS)
+ p_ccb->bypass_fcs |= L2CAP_CFG_FCS_PEER;
+ }
+
+ max_retrans_size = GKI_get_pool_bufsize (p_ccb->ertm_info.fcr_tx_pool_id) - sizeof(BT_HDR)
+ - L2CAP_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET - L2CAP_FCS_LEN;
+
+ /* Ensure the MPS is not bigger than the MTU */
+ if ( (p_cfg->fcr.mps == 0) || (p_cfg->fcr.mps > p_ccb->peer_cfg.mtu) )
+ {
+ p_cfg->fcr.mps = p_ccb->peer_cfg.mtu;
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+
+ /* Ensure the MPS is not bigger than our retransmission buffer */
+ if (p_cfg->fcr.mps > max_retrans_size)
+ {
+ L2CAP_TRACE_DEBUG2("CFG: Overriding MPS to %d (orig %d)", max_retrans_size, p_cfg->fcr.mps);
+
+ p_cfg->fcr.mps = max_retrans_size;
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+
+ if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ /* Always respond with FCR ERTM parameters */
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+ }
+
+ /* Everything ok, so save the peer's adjusted fcr options */
+ p_ccb->peer_cfg.fcr = p_cfg->fcr;
+
+ if (p_cfg->fcr_present)
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
+ }
+ else if (fcr_ok == L2CAP_PEER_CFG_UNACCEPTABLE)
+ {
+ /* Allow peer only one retry for mode */
+ if (p_ccb->peer_cfg_already_rejected)
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ else
+ p_ccb->peer_cfg_already_rejected = TRUE;
+ }
+
+ return (fcr_ok);
+}
+
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+/*******************************************************************************
+** Functions used for testing ERTM mode
+*/
+/* If FALSE, will also corrupt cid, length, control word, etc. */
+#ifndef L2CAP_CORR_FCS_ONLY
+#define L2CAP_CORR_FCS_ONLY TRUE
+#endif
+
+#define L2C_DISP_FRAME_SIZE 16
+/*******************************************************************************
+**
+** Function l2c_corrupt_the_fcr_packet
+**
+** Description This function is used for testing purposes only.
+** It systematically or randomly corrupts packets used with
+** ERTM channels.
+**
+** Returns BOOLEAN TRUE if packet was dropped,
+** FALSE if fcs corrupted or no corruption
+**
+*******************************************************************************/
+static BOOLEAN l2c_corrupt_the_fcr_packet (tL2C_CCB *p_ccb, BT_HDR *p_buf,
+ BOOLEAN is_rx, UINT16 ctrl_word)
+{
+ tL2C_FCR_TEST_CFG *p_cfg;
+ UINT32 tc;
+ UINT8 *p;
+ UINT32 xx;
+ char buf[L2C_DISP_FRAME_SIZE];
+
+ if (!p_ccb || !p_ccb->fcrb.test_cb.cfg.in_use)
+ return FALSE;
+
+ /* Prepare bad FCS */
+ p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+ tc = GKI_get_os_tick_count();
+ tc ^= p[p_buf->len - 1];
+ xx = tc % 47;
+
+ p_cfg = &p_ccb->fcrb.test_cb.cfg;
+#if 0
+ L2CAP_TRACE_DEBUG4 ("testcfg: type: %d, freq: %d (NRM-0, RDM-1), is_rx: %d, count: %d",
+ p_cfg->type, p_cfg->freq, p_cfg->is_rx, p_cfg->count);
+#endif
+ /* If not time to corrupt get out */
+ if (p_cfg->freq == L2CAP_FCR_FREQ_RANDOM)
+ {
+ if (xx != 0)
+ return FALSE;
+ }
+ else /* check test criteria before corrupting */
+ {
+ if ( (p_cfg->count == 0)
+ || (p_cfg->is_rx != is_rx)
+ || ((UINT8)(ctrl_word & L2CAP_FCR_S_FRAME_BIT) != p_cfg->type) )
+ {
+ return FALSE;
+ }
+
+ /* Turn off if done */
+ if (--(p_cfg->count) == 0)
+ {
+ p_ccb->fcrb.test_cb.cfg.in_use = FALSE;
+ }
+ }
+
+#if L2CAP_CORR_FCS_ONLY == TRUE
+ /* Corrupt the FCS check */
+ p[p_buf->len - 1] = p[p_buf->len - 2] = 0;
+#else
+ /* If made it this far packet needs to be corrupted */
+ xx = tc % p_buf->len;
+
+ /* Make sure the value was changed */
+ if (p[xx + 1] == 0)
+ p[xx + 1] = 0x5A;
+
+ p[xx] = p[xx] ^ p[xx + 1];
+
+#if 1 /* Enable if not wishing to corrupting frame type */
+ {
+ UINT8 *p_temp = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+ p_temp += L2CAP_PKT_OVERHEAD;
+ if ((UINT16)((*p_temp) & 0x01) != (ctrl_word & 0x0001))
+ {
+ (*p_temp) |= (UINT8)(ctrl_word & 0x0001);
+ }
+ }
+#endif
+#endif
+
+ if (is_rx)
+ {
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Rx S-Frame");
+ else
+ BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Rx I-Frame");
+ }
+ else
+ {
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Tx S-Frame");
+ else
+ BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Tx I-Frame");
+ }
+
+ /* Lastly, just drop packet if FCS is not being used or if Tx */
+ if (!is_rx || p_ccb->bypass_fcs == L2CAP_BYPASS_FCS)
+ {
+ L2CAP_TRACE_ERROR6 ("-=-=-=-=-=-=-=- Dropping %s packet (0x%04x) tc: %u Buf Len: %u xx: %u count: %d",
+ buf, (UINT32)p_buf, tc, p_buf->len, xx, p_cfg->count);
+ GKI_freebuf(p_buf);
+ return TRUE;
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR6 ("-=-=-=-=-=-=-=- Corrupting %s packet (0x%04x) tc: %u Buf Len: %u xx: %u count: %d",
+ buf, (UINT32)p_buf, tc, p_buf->len, xx, p_cfg->count);
+ }
+
+ return FALSE;
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetupErtmTest
+**
+** Description This function is used for testing purposes only.
+** It corrupts or drops one or more packets used with ERTM channels.
+**
+** Parameters
+** cid - channel ID (0 uses RFCOMM PSM's CID)
+**
+** type - type of test to run (L2CAP_FCR_TTYPE_CORR_IFRAMES
+** L2CAP_FCR_TTYPE_CORR_SFRAME
+** L2CAP_FCR_TTYPE_STOP_TEST
+** L2CAP_FCR_TTYPE_GET_CID - returns rfcomm cid only)
+**
+** is_rx - TRUE to corrupt Rx packet, FALSE for Tx packet)
+**
+** freq - L2CAP_FCR_FREQ_RANDOM (turns on random corruptions/drops)
+** L2CAP_FCR_FREQ_NORMAL (turns on test with "count" corruptions/drops)
+**
+** count - number of packets in a row to drop or corrupt
+**
+** Returns CID of channel running test
+**
+*******************************************************************************/
+UINT16 L2CA_SetupErtmTest (UINT16 cid, UINT8 type, BOOLEAN is_rx, UINT8 freq, UINT16 count)
+{
+ tL2C_CCB *p_ccb = NULL;
+ tL2C_CCB *p_temp_ccb;
+ tL2C_FCR_TEST_CB *p_test_cb;
+ UINT16 xx;
+
+ /* If '0' tests run on top of RFCOMM CID if active */
+ if (cid == 0)
+ {
+ p_temp_ccb = l2cb.ccb_pool;
+ for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++, p_temp_ccb++)
+ {
+ /* Fixed channels don't have p_rcb's */
+ if (p_temp_ccb->in_use && p_temp_ccb->p_rcb && p_temp_ccb->p_rcb->psm == BT_PSM_RFCOMM)
+ {
+ p_ccb = p_temp_ccb;
+ cid = L2CAP_BASE_APPL_CID + xx;
+ break;
+ }
+ }
+ }
+ else
+ p_ccb = l2cu_find_ccb_by_cid (NULL, cid);
+
+ if (!p_ccb || type == L2CAP_FCR_TTYPE_GET_CID)
+ {
+ if (type == L2CAP_FCR_TTYPE_GET_CID)
+ {
+ L2CAP_TRACE_API1 ("L2CA_SetupErtmTest (GET_CID): cid = 0x%04x", cid);
+ }
+ return (cid);
+ }
+
+ p_test_cb = &p_ccb->fcrb.test_cb;
+
+ /* Turn off the current test */
+ if (type == L2CAP_FCR_TTYPE_STOP_TEST)
+ {
+ if (p_test_cb->cfg.in_use)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_SetupErtmTest (OFF): cid 0x%04x", cid);
+ }
+ p_test_cb->cfg.in_use = FALSE;
+ p_test_cb->cfg.count = 0;
+ }
+
+ /* Process the new request */
+ else if (!p_test_cb->cfg.in_use)
+ {
+ /* count must be positive unless random is used */
+ if (!count && freq != L2CAP_FCR_FREQ_RANDOM)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CA_SetupErtmTest (FAIL): Count = 0, freq = %d", freq);
+ return (cid);
+ }
+
+ L2CAP_TRACE_ERROR5 ("L2CA_SetupErtmTest (START): cid 0x%04x, type %d, is_rx %d, freq %d, count %d",
+ cid, type, is_rx, freq, count);
+
+ p_test_cb->cfg.in_use = TRUE;
+ p_test_cb->cfg.freq = freq;
+ p_test_cb->cfg.type = type;
+ p_test_cb->cfg.is_rx = is_rx;
+ p_test_cb->cfg.count = count;
+ }
+ else /* Test already in progress so ignore */
+ {
+ L2CAP_TRACE_ERROR5 ("L2CA_SetupErtmTest (ignoring): cid 0x%04x, type %d, is_rx %d, freq %d, count %d",
+ cid, type, is_rx, freq, count);
+ }
+
+ return (cid);
+}
+
+
+/*******************************************************************************
+** The following routines are only used in conjunction with Conformance testing
+********************************************************************************/
+
+/*******************************************************************************
+**
+** Function L2CA_SendPolledSFrame
+**
+** Description This function is used for testing purposes only.
+** It Sends a Polled RR or RNR to the peer
+**
+** Parameters
+** cid - channel ID
+**
+** sup_type - (L2CAP_FCR_SUP_RR or L2CAP_FCR_SUP_RNR)
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_SendPolledSFrame (UINT16 cid, UINT16 sup_type)
+{
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, cid);
+
+ if (p_ccb && (sup_type == L2CAP_FCR_SUP_RR || sup_type == L2CAP_FCR_SUP_RNR))
+ {
+ l2c_fcr_send_S_frame (p_ccb, sup_type, L2CAP_FCR_P_BIT);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR2 ("L2CA_SendPolledSFrame(ERROR): sup_type %u, p_ccb 0x%07x",
+ sup_type, (UINT32)p_ccb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_bypass_sframe_packet
+**
+** Description This function is used for testing purposes only.
+** It returns TRUE if S-Frame should be bypassed.
+**
+** Returns void
+**
+*******************************************************************************/
+static BOOLEAN l2c_bypass_sframe_packet (tL2C_CCB *p_ccb)
+{
+ if (p_ccb && p_ccb->fcrb.test_cb.cfm.in_use)
+ {
+ if (p_ccb->fcrb.test_cb.cfm.skip_sframe_count > 0)
+ {
+ L2CAP_TRACE_ERROR1 ("l2c_bypass_sframe_packet (count %d)",
+ p_ccb->fcrb.test_cb.cfm.skip_sframe_count);
+
+ if (--p_ccb->fcrb.test_cb.cfm.skip_sframe_count == 0)
+ p_ccb->fcrb.test_cb.cfm.in_use = FALSE;
+
+ /* Bypass sending S-Frame */
+ return TRUE;
+ }
+ else
+ p_ccb->fcrb.test_cb.cfm.in_use = FALSE;
+ }
+
+ return FALSE;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_BypassSFrame
+**
+** Description This function is used for testing purposes only.
+** It skips sending 'count' S-Frames.
+**
+** Parameters
+** cid - channel ID
+**
+** count - Number of S-Frames to skip sending
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_BypassSFrame (UINT16 cid, UINT8 count)
+{
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, cid);
+ tL2C_FCR_CFM_TEST_CB *p_test_cb;
+
+ if (!p_ccb)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CA_BypassSFrame(ERROR): no p_ccb (0x%07x)", (UINT32)p_ccb);
+ return;
+ }
+
+ p_test_cb = &p_ccb->fcrb.test_cb.cfm;
+
+ /* Initiate test if not active */
+ if (!p_test_cb->in_use)
+ {
+ p_test_cb->in_use = TRUE;
+ p_test_cb->skip_sframe_count = count;
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING0 ("L2CA_BypassSFrame(ERROR): already in use (ignoring...)");
+ }
+}
+
+#endif /* L2CAP_CORRUPT_ERTM_PKTS == TRUE */
+
+#if (L2CAP_ERTM_STATS == TRUE)
+/*******************************************************************************
+**
+** Function l2c_fcr_collect_ack_delay
+**
+** Description collect throughput, delay, queue size of waiting ack
+**
+** Parameters
+** tL2C_CCB
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked)
+{
+ UINT32 index;
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT32 timestamp, delay;
+ UINT8 xx;
+ UINT8 str[120];
+
+ index = p_ccb->fcrb.ack_delay_avg_index;
+
+ /* update sum, max and min of waiting for ack queue size */
+ p_ccb->fcrb.ack_q_count_avg[index] += p_ccb->fcrb.waiting_for_ack_q.count;
+
+ if ( p_ccb->fcrb.waiting_for_ack_q.count > p_ccb->fcrb.ack_q_count_max[index] )
+ p_ccb->fcrb.ack_q_count_max[index] = p_ccb->fcrb.waiting_for_ack_q.count;
+
+ if ( p_ccb->fcrb.waiting_for_ack_q.count < p_ccb->fcrb.ack_q_count_min[index] )
+ p_ccb->fcrb.ack_q_count_min[index] = p_ccb->fcrb.waiting_for_ack_q.count;
+
+ /* update sum, max and min of round trip delay of acking */
+ p_buf = (BT_HDR *)(p_ccb->fcrb.waiting_for_ack_q.p_first);
+ for (xx = 0; (xx < num_bufs_acked)&&(p_buf); xx++)
+ {
+ /* adding up length of acked I-frames to get throughput */
+ p_ccb->fcrb.throughput[index] += p_buf->len - 8;
+
+ if ( xx == num_bufs_acked - 1 )
+ {
+ /* get timestamp from tx I-frame that receiver is acking */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len;
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS)
+ {
+ p += L2CAP_FCS_LEN;
+ }
+
+ STREAM_TO_UINT32 (timestamp, p);
+ delay = GKI_get_os_tick_count() - timestamp;
+
+ p_ccb->fcrb.ack_delay_avg[index] += delay;
+ if ( delay > p_ccb->fcrb.ack_delay_max[index] )
+ p_ccb->fcrb.ack_delay_max[index] = delay;
+ if ( delay < p_ccb->fcrb.ack_delay_min[index] )
+ p_ccb->fcrb.ack_delay_min[index] = delay;
+ }
+
+ p_buf = GKI_getnext(p_buf);
+ }
+
+ p_ccb->fcrb.ack_delay_avg_count++;
+
+ /* calculate average and initialize next avg, min and max */
+ if (p_ccb->fcrb.ack_delay_avg_count > L2CAP_ERTM_STATS_AVG_NUM_SAMPLES)
+ {
+ p_ccb->fcrb.ack_delay_avg_count = 0;
+
+ p_ccb->fcrb.ack_q_count_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES;
+ p_ccb->fcrb.ack_delay_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES;
+
+ /* calculate throughput */
+ timestamp = GKI_get_os_tick_count();
+ if (timestamp - p_ccb->fcrb.throughput_start > 0 )
+ p_ccb->fcrb.throughput[index] /= (timestamp - p_ccb->fcrb.throughput_start);
+
+ p_ccb->fcrb.throughput_start = timestamp;
+
+ sprintf(str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u",
+ index, p_ccb->fcrb.throughput[index],
+ p_ccb->fcrb.ack_delay_avg[index], p_ccb->fcrb.ack_delay_min[index], p_ccb->fcrb.ack_delay_max[index],
+ p_ccb->fcrb.ack_q_count_avg[index], p_ccb->fcrb.ack_q_count_min[index], p_ccb->fcrb.ack_q_count_max[index] );
+
+ BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", str);
+
+ index = (index + 1) % L2CAP_ERTM_STATS_NUM_AVG;
+ p_ccb->fcrb.ack_delay_avg_index = index;
+
+ p_ccb->fcrb.ack_q_count_max[index] = 0;
+ p_ccb->fcrb.ack_q_count_min[index] = 0xFFFFFFFF;
+ p_ccb->fcrb.ack_q_count_avg[index] = 0;
+
+
+ p_ccb->fcrb.ack_delay_max[index] = 0;
+ p_ccb->fcrb.ack_delay_min[index] = 0xFFFFFFFF;
+ p_ccb->fcrb.ack_delay_avg[index] = 0;
+
+ p_ccb->fcrb.throughput[index] = 0;
+ }
+}
+#endif
+
+
+
diff --git a/stack/l2cap/l2c_int.h b/stack/l2cap/l2c_int.h
new file mode 100644
index 0000000..6dd93b0
--- /dev/null
+++ b/stack/l2cap/l2c_int.h
@@ -0,0 +1,757 @@
+/*****************************************************************************
+**
+** Name: l2c_int.h
+**
+** Description: This file contains L2CAP internal definitions
+**
+** Copyright (c) 1999-2012, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+#ifndef L2C_INT_H
+#define L2C_INT_H
+
+#include "l2c_api.h"
+#include "l2cdefs.h"
+#include "gki.h"
+#include "btm_api.h"
+
+#define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */
+
+/* Timeouts. Since L2CAP works off a 1-second list, all are in seconds.
+*/
+#define L2CAP_LINK_ROLE_SWITCH_TOUT 10 /* 10 seconds */
+#define L2CAP_LINK_CONNECT_TOUT 60 /* 30 seconds */
+#define L2CAP_LINK_CONNECT_TOUT_EXT 120 /* 120 seconds */
+#define L2CAP_ECHO_RSP_TOUT 30 /* 30 seconds */
+#define L2CAP_LINK_FLOW_CONTROL_TOUT 2 /* 2 seconds */
+#define L2CAP_LINK_DISCONNECT_TOUT 30 /* 30 seconds */
+
+#ifndef L2CAP_CHNL_CONNECT_TOUT /* BSA needs to override for internal project needs */
+#define L2CAP_CHNL_CONNECT_TOUT 60 /* 60 seconds */
+#endif
+
+#define L2CAP_CHNL_CONNECT_TOUT_EXT 120 /* 120 seconds */
+#define L2CAP_CHNL_CFG_TIMEOUT 30 /* 30 seconds */
+#define L2CAP_CHNL_DISCONNECT_TOUT 10 /* 10 seconds */
+#define L2CAP_DELAY_CHECK_SM4 2 /* 2 seconds */
+#define L2CAP_WAIT_INFO_RSP_TOUT 3 /* 3 seconds */
+#define L2CAP_WAIT_UNPARK_TOUT 2 /* 2 seconds */
+#define L2CAP_LINK_INFO_RESP_TOUT 2 /* 2 seconds */
+#define L2CAP_BLE_LINK_CONNECT_TOUT 30 /* 30 seconds */
+
+/* quick timer uses millisecond unit */
+#define L2CAP_DEFAULT_RETRANS_TOUT 2000 /* 2000 milliseconds */
+#define L2CAP_DEFAULT_MONITOR_TOUT 12000 /* 12000 milliseconds */
+#define L2CAP_FCR_ACK_TOUT 200 /* 200 milliseconds */
+
+/* Define the possible L2CAP channel states. The names of
+** the states may seem a bit strange, but they are taken from
+** the Bluetooth specification.
+*/
+typedef enum
+{
+ CST_CLOSED, /* Channel is in clodes state */
+ CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */
+ CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */
+ CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */
+ CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */
+ CST_CONFIG, /* Negotiating configuration */
+ CST_OPEN, /* Data transfer state */
+ CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */
+ CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */
+} tL2C_CHNL_STATE;
+
+/* Define the possible L2CAP link states
+*/
+typedef enum
+{
+ LST_DISCONNECTED,
+ LST_CONNECT_HOLDING,
+ LST_CONNECTING_WAIT_SWITCH,
+ LST_CONNECTING,
+ LST_CONNECTED,
+ LST_DISCONNECTING
+} tL2C_LINK_STATE;
+
+
+
+/* Define input events to the L2CAP link and channel state machines. The names
+** of the events may seem a bit strange, but they are taken from
+** the Bluetooth specification.
+*/
+#define L2CEVT_LP_CONNECT_CFM 0 /* Lower layer connect confirm */
+#define L2CEVT_LP_CONNECT_CFM_NEG 1 /* Lower layer connect confirm (failed) */
+#define L2CEVT_LP_CONNECT_IND 2 /* Lower layer connect indication */
+#define L2CEVT_LP_DISCONNECT_IND 3 /* Lower layer disconnect indication */
+#define L2CEVT_LP_QOS_CFM 4 /* Lower layer QOS confirmation */
+#define L2CEVT_LP_QOS_CFM_NEG 5 /* Lower layer QOS confirmation (failed)*/
+#define L2CEVT_LP_QOS_VIOLATION_IND 6 /* Lower layer QOS violation indication */
+
+#define L2CEVT_SEC_COMP 7 /* Security cleared successfully */
+#define L2CEVT_SEC_COMP_NEG 8 /* Security procedure failed */
+
+#define L2CEVT_L2CAP_CONNECT_REQ 10 /* Peer connection request */
+#define L2CEVT_L2CAP_CONNECT_RSP 11 /* Peer connection response */
+#define L2CEVT_L2CAP_CONNECT_RSP_PND 12 /* Peer connection response pending */
+#define L2CEVT_L2CAP_CONNECT_RSP_NEG 13 /* Peer connection response (failed) */
+#define L2CEVT_L2CAP_CONFIG_REQ 14 /* Peer configuration request */
+#define L2CEVT_L2CAP_CONFIG_RSP 15 /* Peer configuration response */
+#define L2CEVT_L2CAP_CONFIG_RSP_NEG 16 /* Peer configuration response (failed) */
+#define L2CEVT_L2CAP_DISCONNECT_REQ 17 /* Peer disconnect request */
+#define L2CEVT_L2CAP_DISCONNECT_RSP 18 /* Peer disconnect response */
+#define L2CEVT_L2CAP_INFO_RSP 19 /* Peer information response */
+#define L2CEVT_L2CAP_DATA 20 /* Peer data */
+
+#define L2CEVT_L2CA_CONNECT_REQ 21 /* Upper layer connect request */
+#define L2CEVT_L2CA_CONNECT_RSP 22 /* Upper layer connect response */
+#define L2CEVT_L2CA_CONNECT_RSP_NEG 23 /* Upper layer connect response (failed)*/
+#define L2CEVT_L2CA_CONFIG_REQ 24 /* Upper layer config request */
+#define L2CEVT_L2CA_CONFIG_RSP 25 /* Upper layer config response */
+#define L2CEVT_L2CA_CONFIG_RSP_NEG 26 /* Upper layer config response (failed) */
+#define L2CEVT_L2CA_DISCONNECT_REQ 27 /* Upper layer disconnect request */
+#define L2CEVT_L2CA_DISCONNECT_RSP 28 /* Upper layer disconnect response */
+#define L2CEVT_L2CA_DATA_READ 29 /* Upper layer data read */
+#define L2CEVT_L2CA_DATA_WRITE 30 /* Upper layer data write */
+#define L2CEVT_L2CA_FLUSH_REQ 31 /* Upper layer flush */
+
+#define L2CEVT_TIMEOUT 32 /* Timeout */
+#define L2CEVT_SEC_RE_SEND_CMD 33 /* btm_sec has enough info to proceed */
+
+#define L2CEVT_ACK_TIMEOUT 34 /* RR delay timeout */
+
+
+/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two
+ successive ID values, '0' id only or both */
+#define L2CAP_ADJ_BRCM_ID 0x1
+#define L2CAP_ADJ_ZERO_ID 0x2
+#define L2CAP_ADJ_ID 0x3
+
+/* Return values for l2cu_process_peer_cfg_req() */
+#define L2CAP_PEER_CFG_UNACCEPTABLE 0
+#define L2CAP_PEER_CFG_OK 1
+#define L2CAP_PEER_CFG_DISCONNECT 2
+
+/* eL2CAP option constants */
+#define L2CAP_MIN_RETRANS_TOUT 2000 /* Min retransmission timeout if no flush timeout or PBF */
+#define L2CAP_MIN_MONITOR_TOUT 12000 /* Min monitor timeout if no flush timeout or PBF */
+
+#define L2CAP_MAX_FCR_CFG_TRIES 2 /* Config attempts before disconnecting */
+
+/* Only compiled in when in test mode. Production devices must not include
+*/
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+
+/* These are used for conformance and corruption testing only */
+typedef struct
+{
+ BOOLEAN in_use; /* TRUE if test in progress */
+ UINT8 type; /* Type of test to run or turns off random test */
+ UINT8 freq; /* One-shot or random */
+ BOOLEAN is_rx; /* TRUE if incoming packets */
+ UINT16 count; /* How many I-frames to drop in a row; used only with one-shot tests */
+} tL2C_FCR_TEST_CFG;
+
+typedef struct
+{
+ BOOLEAN in_use; /* TRUE if test in progress */
+ UINT8 skip_sframe_count; /* Number of S-Frames to skip sending */
+} tL2C_FCR_CFM_TEST_CB;
+
+typedef struct
+{
+ tL2C_FCR_TEST_CFG cfg; /* Current corruption test configuration */
+ tL2C_FCR_CFM_TEST_CB cfm; /* Conformance test structure */
+} tL2C_FCR_TEST_CB;
+
+#endif /* L2CAP_CORRUPT_ERTM_PKTS == TRUE */
+
+typedef struct
+{
+ UINT8 next_tx_seq; /* Next sequence number to be Tx'ed */
+ UINT8 last_rx_ack; /* Last sequence number ack'ed by the peer */
+ UINT8 next_seq_expected; /* Next peer sequence number expected */
+ UINT8 last_ack_sent; /* Last peer sequence number ack'ed */
+ UINT8 num_tries; /* Number of retries to send a packet */
+ UINT8 max_held_acks; /* Max acks we can hold before sending */
+
+ BOOLEAN remote_busy; /* TRUE if peer has flowed us off */
+ BOOLEAN local_busy; /* TRUE if we have flowed off the peer */
+
+ BOOLEAN rej_sent; /* Reject was sent */
+ BOOLEAN srej_sent; /* Selective Reject was sent */
+ BOOLEAN wait_ack; /* Transmitter is waiting ack (poll sent) */
+ BOOLEAN rej_after_srej; /* Send a REJ when SREJ clears */
+
+ BOOLEAN send_f_rsp; /* We need to send an F-bit response */
+
+ UINT16 rx_sdu_len; /* Length of the SDU being received */
+ BT_HDR *p_rx_sdu; /* Buffer holding the SDU being received */
+ BUFFER_Q waiting_for_ack_q; /* Buffers sent and waiting for peer to ack */
+ BUFFER_Q srej_rcv_hold_q; /* Buffers rcvd but held pending SREJ rsp */
+ BUFFER_Q retrans_q; /* Buffers being retransmitted */
+
+ TIMER_LIST_ENT ack_timer; /* Timer delaying RR */
+ TIMER_LIST_ENT mon_retrans_timer; /* Timer Monitor or Retransmission */
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ UINT32 connect_tick_count; /* Time channel was established */
+ UINT32 ertm_pkt_counts[2]; /* Packets sent and received */
+ UINT32 ertm_byte_counts[2]; /* Bytes sent and received */
+ UINT32 s_frames_sent[4]; /* S-frames sent (RR, REJ, RNR, SREJ) */
+ UINT32 s_frames_rcvd[4]; /* S-frames rcvd (RR, REJ, RNR, SREJ) */
+ UINT32 xmit_window_closed; /* # of times the xmit window was closed */
+ UINT32 controller_idle; /* # of times less than 2 packets in controller */
+ /* when the xmit window was closed */
+ UINT32 pkts_retransmitted; /* # of packets that were retransmitted */
+ UINT32 retrans_touts; /* # of retransmission timouts */
+ UINT32 xmit_ack_touts; /* # of xmit ack timouts */
+
+#define L2CAP_ERTM_STATS_NUM_AVG 10
+#define L2CAP_ERTM_STATS_AVG_NUM_SAMPLES 100
+ UINT32 ack_delay_avg_count;
+ UINT32 ack_delay_avg_index;
+ UINT32 throughput_start;
+ UINT32 throughput[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_avg[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_min[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_max[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_avg[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_min[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_max[L2CAP_ERTM_STATS_NUM_AVG];
+#endif
+
+#if L2CAP_CORRUPT_ERTM_PKTS == TRUE
+ tL2C_FCR_TEST_CB test_cb; /* Used for SVT and UPF testing */
+#endif
+
+} tL2C_FCRB;
+
+
+/* Define a registration control block. Every application (e.g. RFCOMM, SDP,
+** TCS etc) that registers with L2CAP is assigned one of these.
+*/
+#if (L2CAP_UCD_INCLUDED == TRUE)
+#define L2C_UCD_RCB_ID 0x00
+#define L2C_UCD_STATE_UNUSED 0x00
+#define L2C_UCD_STATE_W4_DATA 0x01
+#define L2C_UCD_STATE_W4_RECEPTION 0x02
+#define L2C_UCD_STATE_W4_MTU 0x04
+
+typedef struct
+{
+ UINT8 state;
+ tL2CAP_UCD_CB_INFO cb_info;
+} tL2C_UCD_REG;
+#endif
+
+typedef struct
+{
+ BOOLEAN in_use;
+ UINT16 psm;
+ UINT16 real_psm; /* This may be a dummy RCB for an o/b connection but */
+ /* this is the real PSM that we need to connect to */
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ tL2C_UCD_REG ucd;
+#endif
+
+ tL2CAP_APPL_INFO api;
+} tL2C_RCB;
+
+
+/* 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).
+** Each CCB has unique local and remote CIDs. All channel control blocks on
+** the same physical link and are chained together.
+*/
+typedef struct t_l2c_ccb
+{
+ BOOLEAN in_use; /* TRUE when in use, FALSE when not */
+ tL2C_CHNL_STATE chnl_state; /* Channel state */
+
+ struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */
+ struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */
+ struct t_l2c_linkcb *p_lcb; /* Link this CCB is assigned to */
+
+ UINT16 local_cid; /* Local CID */
+ UINT16 remote_cid; /* Remote CID */
+
+ TIMER_LIST_ENT timer_entry; /* CCB Timer List Entry */
+
+ tL2C_RCB *p_rcb; /* Registration CB for this Channel */
+
+#define IB_CFG_DONE 0x01
+#define OB_CFG_DONE 0x02
+#define RECONFIG_FLAG 0x04 /* True after initial configuration */
+#define CFG_DONE_MASK (IB_CFG_DONE | OB_CFG_DONE)
+
+ UINT8 config_done; /* Configuration flag word */
+ UINT8 local_id; /* Transaction ID for local trans */
+ UINT8 remote_id; /* Transaction ID for local */
+
+#define CCB_FLAG_NO_RETRY 0x01 /* no more retry */
+#define CCB_FLAG_SENT_PENDING 0x02 /* already sent pending response */
+ UINT8 flags;
+
+ tL2CAP_CFG_INFO our_cfg; /* Our saved configuration options */
+ tL2CAP_CH_CFG_BITS peer_cfg_bits; /* Store what peer wants to configure */
+ tL2CAP_CFG_INFO peer_cfg; /* Peer's saved configuration options */
+
+ BUFFER_Q xmit_hold_q; /* Transmit data hold queue */
+
+ BOOLEAN cong_sent; /* Set when congested status sent */
+ UINT16 buff_quota; /* Buffer quota before sending congestion */
+
+ tL2CAP_CHNL_PRIORITY ccb_priority; /* Channel priority */
+ tL2CAP_CHNL_DATA_RATE tx_data_rate; /* Channel Tx data rate */
+ tL2CAP_CHNL_DATA_RATE rx_data_rate; /* Channel Rx data rate */
+
+ /* Fields used for eL2CAP */
+ tL2CAP_ERTM_INFO ertm_info;
+ tL2C_FCRB fcrb;
+ UINT16 tx_mps; /* TX MPS adjusted based on current controller */
+ UINT16 max_rx_mtu;
+ UINT8 fcr_cfg_tries; /* Max number of negotiation attempts */
+ BOOLEAN peer_cfg_already_rejected; /* If mode rejected once, set to TRUE */
+ BOOLEAN out_cfg_fcr_present; /* TRUE if cfg response shoulkd include fcr options */
+
+#define L2CAP_CFG_FCS_OUR 0x01 /* Our desired config FCS option */
+#define L2CAP_CFG_FCS_PEER 0x02 /* Peer's desired config FCS option */
+#define L2CAP_BYPASS_FCS (L2CAP_CFG_FCS_OUR | L2CAP_CFG_FCS_PEER)
+ UINT8 bypass_fcs;
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ BOOLEAN is_flushable; /* TRUE if channel is flushable */
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0) || (L2CAP_UCD_INCLUDED == TRUE)
+ UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */
+#endif
+
+} tL2C_CCB;
+
+/***********************************************************************
+** Define a queue of linked CCBs.
+*/
+typedef struct
+{
+ tL2C_CCB *p_first_ccb; /* The first channel in this queue */
+ tL2C_CCB *p_last_ccb; /* The last channel in this queue */
+} tL2C_CCB_Q;
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+
+/* Round-Robin service for the same priority channels */
+#define L2CAP_NUM_CHNL_PRIORITY 3 /* Total number of priority group (high, medium, low)*/
+#define L2CAP_CHNL_PRIORITY_WEIGHT 5 /* weight per priority for burst transmission quota */
+#define L2CAP_GET_PRIORITY_QUOTA(pri) ((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT)
+
+/* CCBs within the same LCB are served in round robin with priority */
+/* It will make sure that low priority channel (for example, HF signaling on RFCOMM) */
+/* can be sent to headset even if higher priority channel (for example, AV media channel) */
+/* is congested. */
+
+typedef struct
+{
+ tL2C_CCB *p_serve_ccb; /* current serving ccb within priority group */
+ tL2C_CCB *p_first_ccb; /* first ccb of priority group */
+ UINT8 num_ccb; /* number of channels in priority group */
+ UINT8 quota; /* burst transmission quota */
+} tL2C_RR_SERV;
+
+#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+/* Define a link control block. There is one link control block between
+** this device and any other device (i.e. BD ADDR).
+*/
+typedef struct t_l2c_linkcb
+{
+ BOOLEAN in_use; /* TRUE when in use, FALSE when not */
+ tL2C_LINK_STATE link_state;
+
+ TIMER_LIST_ENT timer_entry; /* Timer list entry for timeout evt */
+ UINT16 handle; /* The handle used with LM */
+
+ tL2C_CCB_Q ccb_queue; /* Queue of CCBs on this LCB */
+
+ tL2C_CCB *p_pending_ccb; /* ccb of waiting channel during link disconnect */
+ TIMER_LIST_ENT info_timer_entry; /* Timer entry for info resp timeout evt */
+ BD_ADDR remote_bd_addr; /* The BD address of the remote */
+
+ UINT8 link_role; /* Master or slave */
+ UINT8 id;
+ tL2CA_ECHO_RSP_CB *p_echo_rsp_cb; /* Echo response callback */
+ UINT16 idle_timeout; /* Idle timeout */
+ BOOLEAN is_bonding; /* True - link active only for bonding */
+
+ UINT16 link_flush_tout; /* Flush timeout used */
+
+ UINT16 link_xmit_quota; /* Num outstanding pkts allowed */
+ UINT16 sent_not_acked; /* Num packets sent but not acked */
+
+ BOOLEAN partial_segment_being_sent; /* Set TRUE when a partial segment */
+ /* is being sent. */
+ BOOLEAN w4_info_rsp; /* TRUE when info request is active */
+ UINT8 info_rx_bits; /* set 1 if received info type */
+ UINT32 peer_ext_fea; /* Peer's extended features mask */
+ BUFFER_Q link_xmit_data_q; /* Transmit data buffer queue */
+
+ UINT8 peer_chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE];
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ UINT16 ucd_mtu; /* peer MTU on UCD */
+ BUFFER_Q ucd_out_sec_pending_q; /* Security pending outgoing UCD packet */
+ BUFFER_Q ucd_in_sec_pending_q; /* Security pending incoming UCD packet */
+#endif
+
+#if (L2CAP_HOST_FLOW_CTRL == TRUE)
+ UINT16 link_pkts_unacked; /* Packets received but not acked */
+ UINT16 link_ack_thresh; /* Threshold at which to ack pkts */
+#endif
+
+ BT_HDR *p_hcit_rcv_acl; /* Current HCIT ACL buf being rcvd */
+ UINT16 idle_timeout_sv; /* Save current Idle timeout */
+ UINT8 acl_priority; /* L2C_PRIORITY_NORMAL or L2C_PRIORITY_HIGH */
+ tL2CA_NOCP_CB *p_nocp_cb; /* Num Cmpl pkts callback */
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2C_CCB *p_fixed_ccbs[L2CAP_NUM_FIXED_CHNLS];
+ UINT16 disc_reason;
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ BOOLEAN is_ble_link;
+ tBLE_ADDR_TYPE ble_addr_type;
+
+#define UPD_ENABLED 0 /* If peer requests update, we will change params */
+#define UPD_DISABLED 1 /* application requested not to update */
+#define UPD_PENDING 2 /* while updates are disabled, peer requested new parameters */
+#define UPD_UPDATED 3 /* peer updated connection parameters */
+ UINT8 upd_disabled;
+
+ UINT16 min_interval; /* parameters as requested by peripheral */
+ UINT16 max_interval;
+ UINT16 latency;
+ UINT16 timeout;
+
+#endif
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* each priority group is limited burst transmission */
+ /* round robin service for the same priority channels */
+ tL2C_RR_SERV rr_serv[L2CAP_NUM_CHNL_PRIORITY];
+ UINT8 rr_pri; /* current serving priority group */
+#endif
+
+} tL2C_LCB;
+
+/* Define the L2CAP control structure
+*/
+typedef struct
+{
+ UINT8 l2cap_trace_level;
+ UINT16 controller_xmit_window; /* Total ACL window for all links */
+
+ UINT16 round_robin_quota; /* Round-robin link quota */
+ UINT16 round_robin_unacked; /* Round-robin unacked */
+ BOOLEAN check_round_robin; /* Do a round robin check */
+
+ BOOLEAN is_cong_cback_context;
+
+ tL2C_LCB lcb_pool[MAX_L2CAP_LINKS]; /* Link Control Block pool */
+ tL2C_CCB ccb_pool[MAX_L2CAP_CHANNELS]; /* Channel Control Block pool */
+ tL2C_RCB rcb_pool[MAX_L2CAP_CLIENTS]; /* Registration info pool */
+
+ tL2C_CCB *p_free_ccb_first; /* Pointer to first free CCB */
+ tL2C_CCB *p_free_ccb_last; /* Pointer to last free CCB */
+
+ UINT8 desire_role; /* desire to be master/slave when accepting a connection */
+ BOOLEAN disallow_switch; /* FALSE, to allow switch at create conn */
+ UINT16 num_lm_acl_bufs; /* # of ACL buffers on controller */
+ UINT16 idle_timeout; /* Idle timeout */
+
+ BUFFER_Q rcv_hold_q; /* Recv pending queue */
+ TIMER_LIST_ENT rcv_hold_tle; /* Timer list entry for rcv hold */
+
+ tL2C_LCB *p_cur_hcit_lcb; /* Current HCI Transport buffer */
+ UINT16 num_links_active; /* Number of links active */
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ UINT16 non_flushable_pbf; /* L2CAP_PKT_START_NON_FLUSHABLE if controller supports */
+ /* Otherwise, L2CAP_PKT_START */
+ BOOLEAN is_flush_active; /* TRUE if an HCI_Enhanced_Flush has been sent */
+#endif
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ UINT32 test_info_resp; /* Conformance testing needs a dynamic response */
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2CAP_FIXED_CHNL_REG fixed_reg[L2CAP_NUM_FIXED_CHNLS]; /* Reg info for fixed channels */
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ BOOLEAN is_ble_connecting;
+ BD_ADDR ble_connecting_bda;
+ UINT16 controller_le_xmit_window; /* Total ACL window for all links */
+ UINT16 num_lm_ble_bufs; /* # of ACL buffers on controller */
+#endif
+
+ tL2CA_ECHO_DATA_CB *p_echo_data_cb; /* Echo data callback */
+
+#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE))
+ UINT16 high_pri_min_xmit_quota; /* Minimum number of ACL credit for high priority link */
+#endif /* (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE) */
+
+ UINT16 dyn_psm;
+} tL2C_CB;
+
+
+
+/* Define a structure that contains the information about a connection.
+** This structure is used to pass between functions, and not all the
+** fields will always be filled in.
+*/
+typedef struct
+{
+ BD_ADDR bd_addr; /* Remote BD address */
+ UINT8 status; /* Connection status */
+ UINT16 psm; /* PSM of the connection */
+ UINT16 l2cap_result; /* L2CAP result */
+ UINT16 l2cap_status; /* L2CAP status */
+ UINT16 remote_cid; /* Remote CID */
+} tL2C_CONN_INFO;
+
+
+typedef void (tL2C_FCR_MGMT_EVT_HDLR) (UINT8, tL2C_CCB *);
+
+/* The offset in a buffer that L2CAP will use when building commands.
+*/
+#define L2CAP_SEND_CMD_OFFSET 0
+
+
+/* Number of ACL buffers to use for high priority channel
+*/
+#if (!defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) || (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == FALSE))
+#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (L2CAP_HIGH_PRI_MIN_XMIT_QUOTA)
+#else
+#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (l2cb.high_pri_min_xmit_quota)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* L2CAP global data
+************************************
+*/
+#if (!defined L2C_DYNAMIC_MEMORY) || (L2C_DYNAMIC_MEMORY == FALSE)
+L2C_API extern tL2C_CB l2cb;
+#else
+L2C_API extern tL2C_CB *l2c_cb_ptr;
+#define l2cb (*l2c_cb_ptr)
+#endif
+
+
+/* Functions provided by l2c_main.c
+************************************
+*/
+extern void l2c_init (void);
+extern void l2c_process_timeout (TIMER_LIST_ENT *p_tle);
+extern UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flag);
+extern void l2c_rcv_acl_data (BT_HDR *p_msg);
+extern void l2c_process_held_packets (BOOLEAN timed_out);
+
+/* Functions provided by l2c_utils.c
+************************************
+*/
+extern tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding);
+extern BOOLEAN l2cu_start_post_bond_timer (UINT16 handle);
+extern void l2cu_release_lcb (tL2C_LCB *p_lcb);
+extern tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr);
+extern tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle);
+extern void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding);
+
+extern UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb);
+extern BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs);
+
+extern void l2cu_enqueue_ccb (tL2C_CCB *p_ccb);
+extern void l2cu_dequeue_ccb (tL2C_CCB *p_ccb);
+extern void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority);
+
+extern tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid);
+extern void l2cu_release_ccb (tL2C_CCB *p_ccb);
+extern tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid);
+extern tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid);
+extern void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask);
+
+extern void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason,
+ UINT8 rem_id,UINT16 p1, UINT16 p2);
+extern void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status);
+extern void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len);
+extern void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, UINT16 remote_cid);
+extern void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT16 info_type);
+extern void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result);
+extern void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type);
+extern void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb);
+extern void l2cu_check_channel_congestion (tL2C_CCB *p_ccb);
+extern void l2cu_disconnect_chnl (tL2C_CCB *p_ccb);
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+extern void l2cu_set_non_flushable_pbf(BOOLEAN);
+#endif
+
+#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);
+#endif
+
+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);
+
+/* Functions provided by l2c_ucd.c
+************************************
+*/
+#if (L2CAP_UCD_INCLUDED == TRUE)
+void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb);
+void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data);
+BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg);
+BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+#endif
+
+#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);
+#endif
+
+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);
+
+
+/* Functions provided for Broadcom Aware
+****************************************
+*/
+extern BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
+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 void l2cu_release_rcb (tL2C_RCB *p_rcb);
+
+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);
+extern void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+
+extern void l2cu_device_reset (void);
+extern tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state);
+extern BOOLEAN l2cu_lcb_disconnecting (void);
+
+extern BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb);
+extern BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb);
+extern BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb);
+extern void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda);
+extern void l2cu_initialize_amp_ccb (tL2C_LCB *p_lcb);
+extern void l2cu_adjust_out_mps (tL2C_CCB *p_ccb);
+
+/* Functions provided by l2c_link.c
+************************************
+*/
+extern BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr);
+extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda);
+extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason);
+extern BOOLEAN l2c_link_hci_qos_violation (UINT16 handle);
+extern void l2c_link_timeout (tL2C_LCB *p_lcb);
+extern void l2c_info_timeout (tL2C_LCB *p_lcb);
+extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf);
+extern void l2c_link_adjust_allocation (void);
+extern void l2c_link_process_num_completed_pkts (UINT8 *p);
+extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len);
+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, void *p_ref_data, UINT8 status);
+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);
+
+#if (BLE_INCLUDED == TRUE)
+extern void l2c_link_processs_ble_num_bufs (UINT16 num_lm_acl_bufs);
+#endif
+
+#if ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE)
+extern BOOLEAN l2c_link_check_power_mode ( tL2C_LCB *p_lcb );
+#define L2C_LINK_CHECK_POWER_MODE(x) l2c_link_check_power_mode ((x))
+#else
+#define L2C_LINK_CHECK_POWER_MODE(x) (FALSE)
+#endif
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+/* Used only for conformance testing */
+L2C_API extern void l2cu_set_info_rsp_mask (UINT32 mask);
+#endif
+
+/* Functions provided by l2c_csm.c
+************************************
+*/
+extern void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+
+L2C_API extern BT_HDR *l2cap_link_chk_pkt_start(BT_HDR *); /* Called at start of rcv to check L2CAP segmentation */
+L2C_API extern BOOLEAN l2cap_link_chk_pkt_end (void); /* Called at end of rcv to check L2CAP segmentation */
+
+L2C_API extern void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+
+
+/* Functions provided by l2c_fcr.c
+************************************
+*/
+extern void l2c_fcr_cleanup (tL2C_CCB *p_ccb);
+extern void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+extern void l2c_fcr_proc_tout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit);
+extern BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes, UINT8 pool);
+extern BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb);
+extern BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length);
+extern void l2c_fcr_start_timer (tL2C_CCB *p_ccb);
+
+/* Configuration negotiation */
+extern UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb);
+extern BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_peer_cfg);
+extern BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_stop_timer (tL2C_CCB *p_ccb);
+
+/* Functions provided by l2c_ble.c
+************************************
+*/
+#if (BLE_INCLUDED == TRUE)
+extern BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb);
+extern void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
+extern void l2cble_conn_comp (UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/stack/l2cap/l2c_link.c b/stack/l2cap/l2c_link.c
new file mode 100644
index 0000000..d0899aa
--- /dev/null
+++ b/stack/l2cap/l2c_link.c
@@ -0,0 +1,1703 @@
+/*****************************************************************************
+**
+** Name: l2c_link.c
+**
+** Description: this file contains the functions relating to link
+** management. A "link" is a connection between this device
+** and another device. Only ACL links are managed.
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "l2c_api.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+
+static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf);
+
+#define L2C_LINK_SEND_ACL_DATA(x) HCI_ACL_DATA_TO_LOWER((x))
+
+#if (BLE_INCLUDED == TRUE)
+#define L2C_LINK_SEND_BLE_ACL_DATA(x) HCI_BLE_ACL_DATA_TO_LOWER((x))
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_conn_req
+**
+** Description This function is called when an HCI Connection Request
+** event is received.
+**
+** Returns TRUE, if accept conn
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_LCB *p_lcb_cur;
+ int xx;
+ BOOLEAN no_links;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb)
+ {
+ p_lcb = l2cu_allocate_lcb (bd_addr, FALSE);
+ if (!p_lcb)
+ {
+ btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_RESOURCES);
+ L2CAP_TRACE_ERROR0 ("L2CAP failed to allocate LCB");
+ return FALSE;
+ }
+
+ no_links = TRUE;
+
+ /* If we already have connection, accept as a master */
+ for (xx = 0, p_lcb_cur = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb_cur++)
+ {
+ if (p_lcb_cur == p_lcb)
+ continue;
+
+ if (p_lcb_cur->in_use)
+ {
+ no_links = FALSE;
+ p_lcb->link_role = HCI_ROLE_MASTER;
+ break;
+ }
+ }
+
+ if (no_links)
+ {
+ if (!btm_dev_support_switch (bd_addr))
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ else
+ p_lcb->link_role = l2cu_get_conn_role(p_lcb);
+ }
+
+ /* Tell the other side we accept the connection */
+ btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role);
+
+ p_lcb->link_state = LST_CONNECTING;
+
+ /* Start a timer waiting for connect complete */
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT);
+ return (TRUE);
+ }
+
+ /* We already had a link control block to the guy. Check what state it is in */
+ if ((p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_CONNECT_HOLDING))
+ {
+ /* Connection collision. Accept the connection anyways. */
+
+ if (!btm_dev_support_switch (bd_addr))
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ else
+ p_lcb->link_role = l2cu_get_conn_role(p_lcb);
+
+ btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role);
+
+ p_lcb->link_state = LST_CONNECTING;
+ return (TRUE);
+ }
+ else if (p_lcb->link_state == LST_DISCONNECTING)
+ {
+ /* In disconnecting state, reject the connection. */
+ btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_DEVICE);
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP got conn_req while connected");
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received.
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
+{
+ tL2C_CONN_INFO ci;
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tBTM_SEC_DEV_REC *p_dev_info = NULL;
+
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ btm_acl_update_busy_level (BTM_BLI_PAGE_DONE_EVT);
+#endif
+
+ /* Save the parameters */
+ ci.status = status;
+ memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN);
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (ci.bd_addr);
+
+ /* If we don't have one, this is an error */
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP got conn_comp for unknown BD_ADDR");
+ return (FALSE);
+ }
+
+ if (p_lcb->link_state != LST_CONNECTING)
+ {
+ L2CAP_TRACE_ERROR2 ("L2CAP got conn_comp in bad state: %d status: 0x%d", p_lcb->link_state, status);
+
+ if (status != HCI_SUCCESS)
+ l2c_link_hci_disc_comp (p_lcb->handle, status);
+
+ return (FALSE);
+ }
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ if (ci.status == HCI_SUCCESS)
+ {
+ /* Connected OK. Change state to connected */
+ p_lcb->link_state = LST_CONNECTED;
+
+ /* Get the peer information if the l2cap flow-control/rtrans is supported */
+ l2cu_send_peer_info_req (p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE);
+
+ /* Tell BTM Acl management about the link */
+ if ((p_dev_info = btm_find_dev (p_bda)) != NULL)
+ btm_acl_created (ci.bd_addr, p_dev_info->dev_class,
+ p_dev_info->sec_bd_name, handle,
+ p_lcb->link_role, FALSE);
+ else
+ btm_acl_created (ci.bd_addr, NULL, NULL, handle, p_lcb->link_role, FALSE);
+
+ /* If dedicated bonding do not process any further */
+ if (p_lcb->is_bonding)
+ {
+ if (l2cu_start_post_bond_timer(handle))
+ return (TRUE);
+ }
+
+ /* Update the timeouts in the hold queue */
+ l2c_process_held_packets(FALSE);
+
+ btu_stop_timer (&p_lcb->timer_entry);
+
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, &ci);
+ }
+
+ if (p_lcb->p_echo_rsp_cb)
+ {
+ l2cu_send_peer_echo_req (p_lcb, NULL, 0);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT);
+ }
+ else if (!p_lcb->ccb_queue.p_first_ccb)
+ {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_STARTUP_TOUT);
+ }
+ }
+ /* Max number of acl connections. */
+ /* If there's an lcb disconnecting set this one to holding */
+ else if ((ci.status == HCI_ERR_MAX_NUM_OF_CONNECTIONS) && l2cu_lcb_disconnecting())
+ {
+ p_lcb->link_state = LST_CONNECT_HOLDING;
+ p_lcb->handle = HCI_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Just in case app decides to try again in the callback context */
+ p_lcb->link_state = LST_DISCONNECTING;
+
+ /* Connection failed. For all channels, send the event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
+ {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, &ci);
+
+ p_ccb = pn;
+ }
+
+ p_lcb->disc_reason = status;
+ /* Release the LCB */
+ if (p_lcb->ccb_queue.p_first_ccb == NULL)
+ l2cu_release_lcb (p_lcb);
+ else /* there are any CCBs remaining */
+ {
+ if (ci.status == HCI_ERR_CONNECTION_EXISTS)
+ {
+ /* we are in collision situation, wait for connecttion request from controller */
+ p_lcb->link_state = LST_CONNECTING;
+ }
+ else
+ {
+ l2cu_create_conn(p_lcb);
+ }
+ }
+ }
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_sec_comp
+**
+** Description This function is called when required security procedures
+** are completed.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_sec_comp (BD_ADDR p_bda, void *p_ref_data, UINT8 status)
+{
+ tL2C_CONN_INFO ci;
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_next_ccb;
+ UINT8 event;
+
+ L2CAP_TRACE_DEBUG2 ("l2c_link_sec_comp: %d, 0x%x", status, p_ref_data);
+
+ if (status == BTM_SUCCESS_NO_SECURITY)
+ status = BTM_SUCCESS;
+
+ /* Save the parameters */
+ ci.status = status;
+ memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN);
+
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda);
+
+ /* If we don't have one, this is an error */
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING0 ("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_SUCCESS:
+ L2CAP_TRACE_DEBUG1 ("ccb timer ticks: %u", p_ccb->timer_entry.ticks);
+ event = L2CEVT_SEC_COMP;
+ break;
+
+ case BTM_DELAY_CHECK:
+ /* start a timer - encryption change not received before L2CAP connect req */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4);
+ return;
+
+ default:
+ event = L2CEVT_SEC_COMP_NEG;
+ }
+ l2c_csm_execute (p_ccb, event, &ci);
+ break;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_disc_comp
+**
+** Description This function is called when an HCI Disconnect Complete
+** event is received.
+**
+** Returns TRUE if the link is known about, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ BOOLEAN status = TRUE;
+ BOOLEAN lcb_is_free = TRUE;
+
+ /* See if we have a link control block for the connection */
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* If we don't have one, maybe an SCO link. Send to MM */
+ if (!p_lcb)
+ {
+ status = FALSE;
+ }
+ else
+ {
+ /* There can be a case when we rejected PIN code authentication */
+ /* otherwise save a new reason */
+ if (btm_cb.acl_disc_reason != HCI_ERR_HOST_REJECT_SECURITY)
+ btm_cb.acl_disc_reason = reason;
+
+ p_lcb->disc_reason = btm_cb.acl_disc_reason;
+
+ /* Just in case app decides to try again in the callback context */
+ p_lcb->link_state = LST_DISCONNECTING;
+
+ /* Link is disconnected. For all channels, send the event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
+ {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ /* Keep connect pending control block (if exists)
+ * 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
+ */
+ if (p_ccb != p_lcb->p_pending_ccb)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason);
+ }
+ p_ccb = pn;
+ }
+
+#if BTM_SCO_INCLUDED == TRUE
+ /* Tell SCO management to drop any SCOs on this ACL */
+ btm_sco_acl_removed (p_lcb->remote_bd_addr);
+#endif
+
+ /* If waiting for disconnect and reconnect is pending start the reconnect now
+ race condition where layer above issued connect request on link that was
+ disconnecting
+ */
+ if (p_lcb->ccb_queue.p_first_ccb != NULL)
+ {
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ /* If we are going to re-use the LCB without dropping it, release all fixed channels here */
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ {
+ if (p_lcb->p_fixed_ccbs[xx])
+ {
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason);
+ l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ }
+ }
+#endif
+ L2CAP_TRACE_DEBUG0("l2c_link_hci_disc_comp: Restarting pending ACL request");
+
+ if (l2cu_create_conn(p_lcb))
+ lcb_is_free = FALSE; /* still using this lcb */
+ }
+
+ p_lcb->p_pending_ccb = NULL;
+
+ /* Release the LCB */
+ if (lcb_is_free)
+ l2cu_release_lcb (p_lcb);
+ }
+
+ /* Now that we have a free acl connection, see if any lcbs are pending */
+ if (lcb_is_free && ((p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING)) != NULL))
+ {
+ /* we found one-- create a connection */
+ l2cu_create_conn(p_lcb);
+ }
+
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_qos_violation
+**
+** Description This function is called when an HCI QOS Violation
+** event is received.
+**
+** Returns TRUE if the link is known about, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_qos_violation (UINT16 handle)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ /* See if we have a link control block for the connection */
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* If we don't have one, maybe an SCO link. */
+ if (!p_lcb)
+ return (FALSE);
+
+ /* For all channels, tell the upper layer about it */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
+ l2c_csm_execute (p_ccb, L2CEVT_LP_QOS_VIOLATION_IND, NULL);
+ }
+
+ return (TRUE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function l2c_link_timeout
+**
+** Description This function is called when a link timer expires
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_timeout (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+ UINT16 timeout;
+ tBTM_STATUS rc;
+
+ L2CAP_TRACE_EVENT3 ("L2CAP - l2c_link_timeout() link state %d first CCB %p is_bonding:%d",
+ p_lcb->link_state, p_lcb->ccb_queue.p_first_ccb, p_lcb->is_bonding);
+
+ /* If link was connecting or disconnecting, clear all channels and drop the LCB */
+ if ((p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH) ||
+ (p_lcb->link_state == LST_CONNECTING) ||
+ (p_lcb->link_state == LST_CONNECT_HOLDING) ||
+ (p_lcb->link_state == LST_DISCONNECTING))
+ {
+ p_lcb->p_pending_ccb = NULL;
+
+ /* For all channels, send a disconnect indication event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
+ {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+
+ p_ccb = pn;
+ }
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->link_state == LST_CONNECTING &&
+ l2cb.is_ble_connecting == TRUE)
+ {
+ L2CA_CancelBleConnectReq(l2cb.ble_connecting_bda);
+ }
+#endif
+ /* Release the LCB */
+ l2cu_release_lcb (p_lcb);
+ }
+
+ /* If link is connected, check for inactivity timeout */
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ /* Check for ping outstanding */
+ if (p_lcb->p_echo_rsp_cb)
+ {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_NO_RESP);
+
+ L2CAP_TRACE_WARNING0 ("L2CAP - ping timeout");
+
+ /* For all channels, send a disconnect indication event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; )
+ {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+
+ p_ccb = pn;
+ }
+ }
+
+ /* If no channels in use, drop the link. */
+ if (!p_lcb->ccb_queue.p_first_ccb)
+ {
+ rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);
+
+ if (rc == BTM_CMD_STORED)
+ {
+ /* Security Manager will take care of disconnecting, state will be updated at that time */
+ timeout = 0xFFFF;
+ }
+ else if (rc == BTM_CMD_STARTED)
+ {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ }
+ else if (rc == BTM_SUCCESS)
+ {
+ /* BTM SEC will make sure that link is release (probably after pairing is done) */
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = 0xFFFF;
+ }
+ else if (rc == BTM_BUSY)
+ {
+ /* BTM is still executing security process. Let lcb stay as connected */
+ timeout = 0xFFFF;
+ }
+ else if ((p_lcb->is_bonding)
+ && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)))
+ {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ }
+ else
+ {
+ /* probably no buffer to send disconnect */
+ timeout = BT_1SEC_TIMEOUT;
+ }
+
+ if (timeout != 0xFFFF)
+ {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+ }
+ }
+ else
+ {
+ /* Check in case we were flow controlled */
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_info_timeout
+**
+** Description This function is called when an info request times out
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_info_timeout (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_CONN_INFO ci;
+
+ /* If we timed out waiting for info response, just continue using basic if allowed */
+ if (p_lcb->w4_info_rsp)
+ {
+ /* If waiting for security complete, restart the info response timer */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ if ( (p_ccb->chnl_state == CST_ORIG_W4_SEC_COMP) || (p_ccb->chnl_state == CST_TERM_W4_SEC_COMP) )
+ {
+ btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT);
+ return;
+ }
+ }
+
+ p_lcb->w4_info_rsp = FALSE;
+
+ /* If link is in process of being brought up */
+ if ((p_lcb->link_state != LST_DISCONNECTED) &&
+ (p_lcb->link_state != LST_DISCONNECTING))
+ {
+ /* Notify active channels that peer info is finished */
+ if (p_lcb->ccb_queue.p_first_ccb)
+ {
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_adjust_allocation
+**
+** Description This function is called when a link is created or removed
+** to calculate the amount of packets each link may send to
+** the HCI without an ack coming back.
+**
+** Currently, this is a simple allocation, dividing the
+** number of Controller Packets by the number of links. In
+** the future, QOS configuration should be examined.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_adjust_allocation (void)
+{
+ UINT16 qq, yy, qq_remainder;
+ tL2C_LCB *p_lcb;
+ UINT16 hi_quota, low_quota;
+ UINT16 num_lowpri_links = 0;
+ UINT16 num_hipri_links = 0;
+ UINT16 controller_xmit_quota = l2cb.num_lm_acl_bufs;
+ UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A;
+
+ /* If no links active, nothing to do. */
+ if (l2cb.num_links_active == 0)
+ {
+ l2cb.round_robin_quota = l2cb.round_robin_unacked = 0;
+ return;
+ }
+
+ /* First, count the links */
+ for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++)
+ {
+ if (p_lcb->in_use)
+ {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
+ num_hipri_links++;
+ else
+ num_lowpri_links++;
+ }
+ }
+
+ /* now adjust high priority link quota */
+ low_quota = num_lowpri_links ? 1 : 0;
+ while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota )
+ high_pri_link_quota--;
+
+ /* Work out the xmit quota and buffer quota high and low priorities */
+ hi_quota = num_hipri_links * high_pri_link_quota;
+ low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1;
+
+ /* Work out and save the HCI xmit quota for each low priority link */
+
+ /* If each low priority link cannot have at least one buffer */
+ if (num_lowpri_links > low_quota)
+ {
+ l2cb.round_robin_quota = low_quota;
+ qq = qq_remainder = 0;
+ }
+ /* If each low priority link can have at least one buffer */
+ else if (num_lowpri_links > 0)
+ {
+ l2cb.round_robin_quota = 0;
+ l2cb.round_robin_unacked = 0;
+ qq = low_quota / num_lowpri_links;
+ qq_remainder = low_quota % num_lowpri_links;
+ }
+ /* If no low priority link */
+ else
+ {
+ l2cb.round_robin_quota = 0;
+ l2cb.round_robin_unacked = 0;
+ qq = qq_remainder = 0;
+ }
+
+ L2CAP_TRACE_EVENT5 ("l2c_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u",
+ num_hipri_links, num_lowpri_links, low_quota,
+ l2cb.round_robin_quota, qq);
+
+ /* Now, assign the quotas to each link */
+ for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++)
+ {
+ if (p_lcb->in_use)
+ {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
+ {
+ p_lcb->link_xmit_quota = high_pri_link_quota;
+ }
+ else
+ {
+ /* Safety check in case we switched to round-robin with something outstanding */
+ /* if sent_not_acked is added into round_robin_unacked then don't add it again */
+ /* l2cap keeps updating sent_not_acked for exiting from round robin */
+ if (( p_lcb->link_xmit_quota > 0 )&&( qq == 0 ))
+ l2cb.round_robin_unacked += p_lcb->sent_not_acked;
+
+ p_lcb->link_xmit_quota = qq;
+ if (qq_remainder > 0)
+ {
+ p_lcb->link_xmit_quota++;
+ qq_remainder--;
+ }
+ }
+
+#if L2CAP_HOST_FLOW_CTRL
+ p_lcb->link_ack_thresh = L2CAP_HOST_FC_ACL_BUFS / l2cb.num_links_active;
+#endif
+ L2CAP_TRACE_EVENT3 ("l2c_link_adjust_allocation LCB %d Priority: %d XmitQuota: %d",
+ yy, p_lcb->acl_priority, p_lcb->link_xmit_quota);
+
+ L2CAP_TRACE_EVENT2 (" SentNotAcked: %d RRUnacked: %d",
+ p_lcb->sent_not_acked, l2cb.round_robin_unacked);
+
+ /* There is a special case where we have readjusted the link quotas and */
+ /* this link may have sent anything but some other link sent packets so */
+ /* so we may need a timer to kick off this link's transmissions. */
+ if ( (p_lcb->link_state == LST_CONNECTED)
+ && (p_lcb->link_xmit_data_q.count)
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) )
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
+ }
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_adjust_chnl_allocation
+**
+** Description This function is called to calculate the amount of packets each
+** non-F&EC channel may have outstanding.
+**
+** Currently, this is a simple allocation, dividing the number
+** of packets allocated to the link by the number of channels. In
+** the future, QOS configuration should be examined.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_adjust_chnl_allocation (void)
+{
+ tL2C_CCB *p_ccb;
+ UINT8 xx;
+
+ UINT16 weighted_chnls[GKI_NUM_TOTAL_BUF_POOLS];
+ UINT16 quota_per_weighted_chnls[GKI_NUM_TOTAL_BUF_POOLS];
+ UINT16 reserved_buff[GKI_NUM_TOTAL_BUF_POOLS];
+
+ L2CAP_TRACE_DEBUG0 ("l2c_link_adjust_chnl_allocation");
+
+ /* initialize variables */
+ for (xx = 0; xx < GKI_NUM_TOTAL_BUF_POOLS; xx++ )
+ {
+ weighted_chnls[xx] = 0;
+ reserved_buff[xx] = 0;
+ }
+
+ /* add up all of tx and rx data rate requirement */
+ /* channel required higher data rate will get more buffer quota */
+ for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++)
+ {
+ p_ccb = l2cb.ccb_pool + xx;
+
+ if (!p_ccb->in_use)
+ continue;
+
+ 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;
+ weighted_chnls[p_ccb->ertm_info.user_rx_pool_id] += p_ccb->rx_data_rate;
+
+ if (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID)
+ {
+ /* reserve buffers only for wait_for_ack_q to maximize throughput */
+ /* retrans_q will work based on buffer status */
+ reserved_buff[HCI_ACL_POOL_ID] += p_ccb->peer_cfg.fcr.tx_win_sz;
+ }
+
+ if (p_ccb->ertm_info.fcr_rx_pool_id == HCI_ACL_POOL_ID)
+ {
+ /* reserve buffers for srej_rcv_hold_q */
+ reserved_buff[HCI_ACL_POOL_ID] += p_ccb->peer_cfg.fcr.tx_win_sz;
+ }
+ }
+ else
+ {
+ /* low data rate is 1, medium is 2, high is 3 and no traffic is 0 */
+ weighted_chnls[HCI_ACL_POOL_ID] += p_ccb->tx_data_rate + p_ccb->rx_data_rate;
+ }
+ }
+
+
+ /* get unit quota per pool */
+ for (xx = 0; xx < GKI_NUM_TOTAL_BUF_POOLS; xx++ )
+ {
+ if ( weighted_chnls[xx] > 0 )
+ {
+ if (GKI_poolcount(xx) > reserved_buff[xx])
+ quota_per_weighted_chnls[xx] = ((GKI_poolcount(xx) - reserved_buff[xx])/weighted_chnls[xx]) + 1;
+ else
+ quota_per_weighted_chnls[xx] = 1;
+
+ L2CAP_TRACE_DEBUG5 ("POOL ID:%d, GKI_poolcount = %d, reserved_buff = %d, weighted_chnls = %d, quota_per_weighted_chnls = %d",
+ xx, GKI_poolcount(xx), reserved_buff[xx], weighted_chnls[xx], quota_per_weighted_chnls[xx] );
+ }
+ else
+ quota_per_weighted_chnls[xx] = 0;
+ }
+
+
+ /* assign buffer quota to each channel based on its data rate requirement */
+ for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++)
+ {
+ p_ccb = l2cb.ccb_pool + xx;
+
+ if (!p_ccb->in_use)
+ continue;
+
+ 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;
+
+ L2CAP_TRACE_EVENT6 ("CID:0x%04x FCR Mode:%u UserTxPool:%u Priority:%u TxDataRate:%u Quota:%u",
+ p_ccb->local_cid, p_ccb->peer_cfg.fcr.mode, p_ccb->ertm_info.user_tx_pool_id,
+ p_ccb->ccb_priority, p_ccb->tx_data_rate, p_ccb->buff_quota);
+
+ }
+ else
+ {
+ p_ccb->buff_quota = quota_per_weighted_chnls[HCI_ACL_POOL_ID] * p_ccb->tx_data_rate;
+
+ L2CAP_TRACE_EVENT4 ("CID:0x%04x Priority:%u TxDataRate:%u Quota:%u",
+ p_ccb->local_cid,
+ p_ccb->ccb_priority, p_ccb->tx_data_rate, p_ccb->buff_quota);
+ }
+
+ /* quota may be change so check congestion */
+ l2cu_check_channel_congestion (p_ccb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_processs_num_bufs
+**
+** Description This function is called when a "controller buffer size"
+** event is first received from the controller. It updates
+** the L2CAP values.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs)
+{
+ l2cb.num_lm_acl_bufs = l2cb.controller_xmit_window = num_lm_acl_bufs;
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_pkts_rcvd
+**
+** Description This function is called from the HCI transport when it is time
+** tto send a "Host ready for packets" command. This is only when
+** host to controller flow control is used. If fills in the arrays
+** of numbers of packets and handles.
+**
+** Returns count of number of entries filled in
+**
+*******************************************************************************/
+UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles)
+{
+ UINT8 num_found = 0;
+
+#if (L2CAP_HOST_FLOW_CTRL == TRUE)
+
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->link_pkts_unacked))
+ {
+ num_pkts[num_found] = p_lcb->link_pkts_unacked;
+ handles[num_found] = p_lcb->handle;
+ p_lcb->link_pkts_unacked = 0;
+ num_found++;
+ }
+ }
+
+#endif
+
+ return (num_found);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_role_changed
+**
+** Description This function is called whan a link's master/slave role change
+** event is received. It simply updates the link control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status)
+{
+ tL2C_LCB *p_lcb;
+ int xx;
+
+ /* Make sure not called from HCI Command Status (bd_addr and new_role are invalid) */
+ if (bd_addr)
+ {
+ /* If here came form hci role change event */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr);
+ if (p_lcb)
+ {
+ p_lcb->link_role = new_role;
+
+ /* Reset high priority link if needed */
+ if (hci_status == HCI_SUCCESS)
+ l2cu_set_acl_priority(bd_addr, p_lcb->acl_priority, TRUE);
+ }
+ }
+
+ /* Check if any LCB was waiting for switch to be completed */
+ for (xx = 0, p_lcb = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH))
+ {
+ l2cu_create_conn_after_switch (p_lcb);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_pin_code_request
+**
+** Description This function is called whan a pin-code request is received
+** on a connection. If there are no channels active yet on the
+** link, it extends the link first connection timer. Make sure
+** that inactivity timer is not extended if PIN code happens
+** to be after last ccb released.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_pin_code_request (BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr);
+
+ if ( (p_lcb) && (!p_lcb->ccb_queue.p_first_ccb) )
+ {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT_EXT);
+ }
+}
+
+#if ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE)
+/*******************************************************************************
+**
+** Function l2c_link_check_power_mode
+**
+** Description This function is called to check power mode.
+**
+** Returns TRUE if link is going to be active from park
+** FALSE if nothing to send or not in park mode
+**
+*******************************************************************************/
+BOOLEAN l2c_link_check_power_mode (tL2C_LCB *p_lcb)
+{
+ tBTM_PM_MODE mode;
+ tBTM_PM_PWR_MD pm;
+ tL2C_CCB *p_ccb;
+ BOOLEAN need_to_active = FALSE;
+
+ /*
+ * We only switch park to active only if we have unsent packets
+ */
+ if ( p_lcb->link_xmit_data_q.count == 0 )
+ {
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ if (p_ccb->xmit_hold_q.count != 0)
+ {
+ need_to_active = TRUE;
+ break;
+ }
+ }
+ }
+ else
+ need_to_active = TRUE;
+
+ /* if we have packets to send */
+ if ( need_to_active )
+ {
+ /* check power mode */
+ if (BTM_ReadPowerMode(p_lcb->remote_bd_addr, &mode) == BTM_SUCCESS)
+ {
+ if ( mode == BTM_PM_MD_PARK )
+ {
+ L2CAP_TRACE_DEBUG1 ("LCB(0x%x) is in park mode", p_lcb->handle);
+/* Coverity:
+// FALSE-POSITIVE error from Coverity test tool. Please do NOT remove following comment.
+// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode
+ the other data members of tBTM_PM_PWR_MD are ignored
+*/
+ memset((void*)&pm, 0, sizeof(pm));
+ pm.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, p_lcb->remote_bd_addr, &pm);
+ btu_start_timer (&p_lcb->timer_entry,
+ BTU_TTYPE_L2CAP_LINK, L2CAP_WAIT_UNPARK_TOUT);
+ return TRUE;
+ }
+ else if ( mode == BTM_PM_STS_PENDING )
+ {
+ L2CAP_TRACE_DEBUG1 ("LCB(0x%x) is in PM pending state", p_lcb->handle);
+
+ btu_start_timer (&p_lcb->timer_entry,
+ BTU_TTYPE_L2CAP_LINK, L2CAP_WAIT_UNPARK_TOUT);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+#endif /* ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE) */
+
+/*******************************************************************************
+**
+** Function l2c_link_check_send_pkts
+**
+** Description This function is called to check if it can send packets
+** to the Host Controller. It may be passed the address of
+** a packet to send.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ int xx;
+ BOOLEAN single_write = FALSE;
+
+ /* Save the channel ID for faster counting */
+ if (p_buf)
+ {
+ if (p_ccb != NULL)
+ {
+ p_buf->event = p_ccb->local_cid;
+ single_write = TRUE;
+ }
+ else
+ p_buf->event = 0;
+
+ p_buf->layer_specific = 0;
+ GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf);
+
+ if (p_lcb->link_xmit_quota == 0)
+ l2cb.check_round_robin = TRUE;
+ }
+
+ /* If this is called from uncongested callback context break recursive calling.
+ ** This LCB will be served when receiving number of completed packet event.
+ */
+ if (l2cb.is_cong_cback_context)
+ return;
+
+ /* If we are in a scenario where there are not enough buffers for each link to
+ ** have at least 1, then do a round-robin for all the LCBs
+ */
+ if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) )
+ {
+ if (p_lcb == NULL)
+ p_lcb = l2cb.lcb_pool;
+ else if (!single_write)
+ p_lcb++;
+
+ /* Loop through, starting at the next */
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ /* If controller window is full, nothing to do */
+ if ( (l2cb.controller_xmit_window == 0
+#if (BLE_INCLUDED == TRUE)
+ && !p_lcb->is_ble_link
+#endif
+ )
+#if (BLE_INCLUDED == TRUE)
+ || (p_lcb->is_ble_link && l2cb.controller_le_xmit_window == 0 )
+#endif
+ || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) )
+ break;
+
+ /* Check for wraparound */
+ if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS])
+ p_lcb = &l2cb.lcb_pool[0];
+
+ if ( (!p_lcb->in_use)
+ || (p_lcb->partial_segment_being_sent)
+ || (p_lcb->link_state != LST_CONNECTED)
+ || (p_lcb->link_xmit_quota != 0)
+ || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
+ continue;
+
+ /* See if we can send anything from the Link Queue */
+ if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL)
+ {
+ l2c_link_send_to_lower (p_lcb, p_buf);
+ }
+ else if (single_write)
+ {
+ /* If only doing one write, break out */
+ break;
+ }
+ /* If nothing on the link queue, check the channel queue */
+ else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL)
+ {
+ l2c_link_send_to_lower (p_lcb, p_buf);
+ }
+ }
+
+ /* If we finished without using up our quota, no need for a safety check */
+#if (BLE_INCLUDED == TRUE)
+ if ( ((l2cb.controller_xmit_window > 0 && !p_lcb->is_ble_link) ||
+ (l2cb.controller_le_xmit_window > 0 && p_lcb->is_ble_link))
+ && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
+#else
+ if ( (l2cb.controller_xmit_window > 0)
+ && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
+
+#endif
+ l2cb.check_round_robin = FALSE;
+ }
+ else /* if this is not round-robin service */
+ {
+ /* If a partial segment is being sent, can't send anything else */
+ if ( (p_lcb->partial_segment_being_sent)
+ || (p_lcb->link_state != LST_CONNECTED)
+ || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) )
+ return;
+
+ /* See if we can send anything from the link queue */
+#if (BLE_INCLUDED == TRUE)
+ while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
+ (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#else
+ while ( (l2cb.controller_xmit_window != 0)
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#endif
+ {
+ if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL)
+ break;
+
+ if (!l2c_link_send_to_lower (p_lcb, p_buf))
+ break;
+ }
+
+ if (!single_write)
+ {
+ /* See if we can send anything for any channel */
+#if (BLE_INCLUDED == TRUE)
+ while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) ||
+ (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#else
+ while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#endif
+ {
+ if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL)
+ break;
+
+ if (!l2c_link_send_to_lower (p_lcb, p_buf))
+ break;
+ }
+ }
+
+ /* There is a special case where we have readjusted the link quotas and */
+ /* this link may have sent anything but some other link sent packets so */
+ /* so we may need a timer to kick off this link's transmissions. */
+ if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) )
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_send_to_lower
+**
+** Description This function queues the buffer for HCI transmission
+**
+** Returns TRUE for success, FALSE for fail
+**
+*******************************************************************************/
+static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
+{
+ UINT16 num_segs;
+ UINT16 xmit_window, acl_data_size;
+
+#if (BLE_INCLUDED == TRUE)
+ if ((!p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_acl_pkt_size)) ||
+ (p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size)))
+#else
+ if (p_buf->len <= btu_cb.hcit_acl_pkt_size)
+#endif
+ {
+ if (p_lcb->link_xmit_quota == 0)
+ l2cb.round_robin_unacked++;
+
+ p_lcb->sent_not_acked++;
+ p_buf->layer_specific = 0;
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->is_ble_link)
+ {
+ l2cb.controller_le_xmit_window--;
+ L2C_LINK_SEND_BLE_ACL_DATA (p_buf);
+ }
+ else
+#endif
+ {
+ l2cb.controller_xmit_window--;
+ L2C_LINK_SEND_ACL_DATA (p_buf);
+ }
+ }
+ else
+ {
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->is_ble_link)
+ {
+ acl_data_size = btu_cb.hcit_ble_acl_data_size;
+ xmit_window = l2cb.controller_le_xmit_window;
+
+ }
+ else
+#endif
+ {
+ acl_data_size = btu_cb.hcit_acl_data_size;
+ xmit_window = l2cb.controller_xmit_window;
+ }
+ num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
+
+
+ /* If doing round-robin, then only 1 segment each time */
+ if (p_lcb->link_xmit_quota == 0)
+ {
+ num_segs = 1;
+ p_lcb->partial_segment_being_sent = TRUE;
+ }
+ else
+ {
+ /* Multi-segment packet. Make sure it can fit */
+ if (num_segs > xmit_window)
+ {
+ num_segs = xmit_window;
+ p_lcb->partial_segment_being_sent = TRUE;
+ }
+
+ if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked))
+ {
+ num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked);
+ p_lcb->partial_segment_being_sent = TRUE;
+ }
+ }
+
+ p_buf->layer_specific = num_segs;
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->is_ble_link)
+ {
+ l2cb.controller_le_xmit_window -= num_segs;
+
+ }
+ else
+#endif
+ l2cb.controller_xmit_window -= num_segs;
+
+ if (p_lcb->link_xmit_quota == 0)
+ l2cb.round_robin_unacked += num_segs;
+
+ p_lcb->sent_not_acked += num_segs;
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->is_ble_link)
+ {
+ L2C_LINK_SEND_BLE_ACL_DATA(p_buf);
+ }
+ else
+#endif
+ {
+ L2C_LINK_SEND_ACL_DATA (p_buf);
+ }
+ }
+
+#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->is_ble_link)
+ {
+ L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
+ l2cb.controller_le_xmit_window,
+ p_lcb->handle,
+ p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
+ l2cb.round_robin_quota, l2cb.round_robin_unacked);
+ }
+ else
+#endif
+ {
+ L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
+ l2cb.controller_xmit_window,
+ p_lcb->handle,
+ p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
+ l2cb.round_robin_quota, l2cb.round_robin_unacked);
+ }
+#endif
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_process_num_completed_pkts
+**
+** Description This function is called when a "number-of-completed-packets"
+** event is received from the controller. It updates all the
+** LCB transmit counts.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_process_num_completed_pkts (UINT8 *p)
+{
+ UINT8 num_handles, xx;
+ UINT16 handle;
+ UINT16 num_sent;
+ tL2C_LCB *p_lcb;
+
+ STREAM_TO_UINT8 (num_handles, p);
+
+ for (xx = 0; xx < num_handles; xx++)
+ {
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (num_sent, p);
+
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* Callback for number of completed packet event */
+ /* Originally designed for [3DSG] */
+ if((p_lcb != NULL) && (p_lcb->p_nocp_cb))
+ {
+ L2CAP_TRACE_DEBUG0 ("L2CAP - calling NoCP callback");
+ (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr);
+ }
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb && p_lcb->is_ble_link)
+ l2cb.controller_le_xmit_window += num_sent;
+ else
+#endif
+ {
+
+ /* Maintain the total window to the controller */
+ l2cb.controller_xmit_window += num_sent;
+ }
+
+ if (p_lcb)
+ {
+ /* If doing round-robin, adjust communal counts */
+ if (p_lcb->link_xmit_quota == 0)
+ {
+ /* Don't go negative */
+ if (l2cb.round_robin_unacked > num_sent)
+ l2cb.round_robin_unacked -= num_sent;
+ else
+ l2cb.round_robin_unacked = 0;
+ }
+
+ /* Don't go negative */
+ if (p_lcb->sent_not_acked > num_sent)
+ p_lcb->sent_not_acked -= num_sent;
+ else
+ p_lcb->sent_not_acked = 0;
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+
+ /* If we were doing round-robin for low priority links, check 'em */
+ if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
+ && (l2cb.check_round_robin)
+ && (l2cb.round_robin_unacked < l2cb.round_robin_quota) )
+ {
+ l2c_link_check_send_pkts (NULL, NULL, NULL);
+ }
+ }
+
+#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
+ if (p_lcb)
+ {
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->is_ble_link)
+ {
+ L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
+ l2cb.controller_le_xmit_window,
+ p_lcb->handle, p_lcb->sent_not_acked,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+ }
+ else
+#endif
+ {
+ L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d",
+ l2cb.controller_xmit_window,
+ p_lcb->handle, p_lcb->sent_not_acked,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+
+ }
+ }
+ else
+ {
+#if (BLE_INCLUDED == TRUE)
+ L2CAP_TRACE_DEBUG5 ("TotalWin=%d LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d",
+ l2cb.controller_xmit_window,
+ l2cb.controller_le_xmit_window,
+ handle,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+#else
+ L2CAP_TRACE_DEBUG4 ("TotalWin=%d Handle=0x%x RRCheck=%d RRUnack=%d",
+ l2cb.controller_xmit_window,
+ handle,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+#endif
+ }
+#endif
+ }
+
+#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
+ /* only full stack can enable sleep mode */
+ btu_check_bt_sleep ();
+#endif
+}
+
+/*******************************************************************************
+**
+** Function l2cap_link_chk_pkt_start
+**
+** Description This function is called from the HCI transport when the first
+** 4 bytes of an HCI ACL packet have been received. It checks if the
+** packet is the next segment of a fragmented L2CAP message. If it
+** is, and the length is OK, it returns the address of the
+** starting L2CAP message segment buffer.
+**
+** Returns the address of the receive buffer HCIT should use
+** (CR419: Modified to return NULL in case of error.)
+**
+** NOTE This assumes that the L2CAP MTU size is less than the size
+** of an HCI ACL buffer, so the maximum L2CAP message will fit
+** into one buffer.
+**
+*******************************************************************************/
+BT_HDR *l2cap_link_chk_pkt_start (BT_HDR *p_cur_buf)
+{
+ UINT8 *p;
+ UINT16 handle;
+ UINT16 hci_len;
+ UINT16 pkt_type;
+ tL2C_LCB *p_lcb;
+ BT_HDR * p_return_buf; /* CR419: To avoid returning from too many places */
+
+
+ if (p_cur_buf)
+ {
+ p = (UINT8 *)(p_cur_buf + 1) + p_cur_buf->offset;
+ }
+ else
+ {
+ return (NULL);
+ }
+
+ /* L2CAP expects all rcvd packets to have a layer-specific value of 0 */
+ p_cur_buf->layer_specific = 0;
+
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (hci_len, p);
+
+ pkt_type = HCID_GET_EVENT (handle);
+ handle = HCID_GET_HANDLE (handle);
+
+ l2cb.p_cur_hcit_lcb = NULL;
+
+ /* Find the link that is associated with this handle */
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* If no link for this handle, nothing to do. */
+ if (!p_lcb)
+ return (p_cur_buf) ;
+
+ if (pkt_type == L2CAP_PKT_START) /*** START PACKET ***/
+ {
+ /* Start of packet. If we were in the middle of receiving */
+ /* a packet, it is incomplete. Drop it. */
+ if (p_lcb->p_hcit_rcv_acl)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - dropping incomplete pkt");
+ GKI_freebuf (p_lcb->p_hcit_rcv_acl);
+ p_lcb->p_hcit_rcv_acl = NULL;
+ }
+
+ /* Save the active buffer address in the LCB */
+ if ((p_return_buf = p_cur_buf) != NULL)
+ {
+ p_lcb->p_hcit_rcv_acl = p_return_buf;
+ l2cb.p_cur_hcit_lcb = p_lcb;
+ }
+ }
+ else /*** CONTINUATION PACKET ***/
+ {
+ /* Packet continuation. Check if we were expecting it */
+ if (p_lcb->p_hcit_rcv_acl)
+ {
+ UINT16 total_len;
+ BT_HDR *p_base_buf = p_lcb->p_hcit_rcv_acl;
+ UINT8 *p_f = (UINT8 *)(p_base_buf + 1) + p_base_buf->offset + 2;
+
+ STREAM_TO_UINT16 (total_len, p_f);
+
+ /* We were expecting the CONTINUATION packet. If length fits, it can go in the */
+ /* current buffer. */
+ if ((total_len + hci_len) <= (L2CAP_MTU_SIZE + HCI_DATA_PREAMBLE_SIZE))
+ {
+ /* GKI_freebuf (p_cur_buf); CR419:Do not free it yet */
+ p_return_buf = p_lcb->p_hcit_rcv_acl; /* CR419: return base buffer */
+ l2cb.p_cur_hcit_lcb = p_lcb;
+
+ if ((p_cur_buf->len > HCI_DATA_PREAMBLE_SIZE))
+ {
+ UINT8 * p = (UINT8 *)(p_cur_buf + 1)
+ + p_cur_buf->offset
+ + HCI_DATA_PREAMBLE_SIZE;
+ UINT8 * p1 = (UINT8 *)(p_return_buf + 1)
+ + p_return_buf->offset
+ + p_return_buf->len;
+
+ /* Copy data from new buffer into base buffer then update the data */
+ /* count in the base buffer accordingly. */
+ memcpy (p1, p, p_cur_buf->len - HCI_DATA_PREAMBLE_SIZE);
+ p_return_buf->len += (p_cur_buf->len - HCI_DATA_PREAMBLE_SIZE);
+ }
+
+ GKI_freebuf (p_cur_buf);
+ p_cur_buf = NULL;
+
+ /* Update HCI header of first segment (base buffer) with new length */
+ total_len += hci_len;
+ p_f = (UINT8 *)(p_base_buf + 1) + p_base_buf->offset + 2;
+ UINT16_TO_STREAM (p_f, total_len);
+ }
+ else
+ {
+ /* Packet too long. Drop the base packet */
+ L2CAP_TRACE_WARNING3 ("L2CAP - dropping too long pkt BufLen: %d total_len: %d hci_len: %d",
+ p_lcb->p_hcit_rcv_acl->len, total_len, hci_len);
+
+ GKI_freebuf (p_lcb->p_hcit_rcv_acl);
+ p_lcb->p_hcit_rcv_acl = NULL;
+ p_return_buf = NULL ; /* Can't hold onto it any more */
+ }
+ }
+ else /*** NEITHER START OR CONTINUATION PACKET ***/
+ {
+ p_return_buf = NULL ;
+ }
+ }
+
+ if (p_return_buf == NULL) /* if error is indicated.. */
+ {
+ if (p_cur_buf != NULL) /* ..drop input buffer */
+ GKI_freebuf(p_cur_buf); /* (if present) */
+ }
+
+ return (p_return_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cap_link_chk_pkt_end
+**
+** Description This function is called from the HCI transport when the last
+** byte of an HCI ACL packet has been received. It checks if the
+** L2CAP message is complete, i.e. no more continuation packets
+** are expected.
+**
+** Returns TRUE if message complete, FALSE if continuation expected
+**
+*******************************************************************************/
+BOOLEAN l2cap_link_chk_pkt_end (void)
+{
+ UINT8 *p;
+ BT_HDR *p_buf;
+ UINT16 l2cap_len;
+ tL2C_LCB *p_lcb;
+
+ /* If link or buffer pointer not set up, let main line handle it */
+ if (((p_lcb = l2cb.p_cur_hcit_lcb) == NULL) || ((p_buf = p_lcb->p_hcit_rcv_acl) == NULL))
+ return (TRUE);
+
+ /* Point to the L2CAP length */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset + HCI_DATA_PREAMBLE_SIZE;
+
+ STREAM_TO_UINT16 (l2cap_len, p);
+
+ /* If the L2CAP length has not been reached, tell HCIT not to send this buffer to BTU */
+ if (l2cap_len > (p_buf->len - (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD)))
+ {
+ return (FALSE);
+ }
+ else
+ {
+ p_lcb->p_hcit_rcv_acl = NULL;
+ return (TRUE);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_segments_xmitted
+**
+** Description This function is called from the HCI Interface when an ACL
+** data packet segment is transmitted.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_segments_xmitted (BT_HDR *p_msg)
+{
+ UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT16 handle;
+ tL2C_LCB *p_lcb;
+
+ /* Extract the handle */
+ STREAM_TO_UINT16 (handle, p);
+ handle = HCID_GET_HANDLE (handle);
+
+ /* Find the LCB based on the handle */
+ if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - rcvd segment complete, unknown handle: %d", handle);
+ GKI_freebuf (p_msg);
+ return;
+ }
+
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ /* Enqueue the buffer to the head of the transmit queue, and see */
+ /* if we can transmit anything more. */
+ GKI_enqueue_head (&p_lcb->link_xmit_data_q, p_msg);
+
+ p_lcb->partial_segment_being_sent = FALSE;
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+ }
+ else
+ GKI_freebuf (p_msg);
+}
diff --git a/stack/l2cap/l2c_main.c b/stack/l2cap/l2c_main.c
new file mode 100644
index 0000000..17b9df1
--- /dev/null
+++ b/stack/l2cap/l2c_main.c
@@ -0,0 +1,979 @@
+/*****************************************************************************
+**
+** Name: l2c_main.c
+**
+** Description: This file contains the main L2CAP entry points
+**
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "l2c_api.h"
+#include "btu.h"
+#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 process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
+
+/********************************************************************************/
+/* G L O B A L L 2 C A P D A T A */
+/********************************************************************************/
+#if L2C_DYNAMIC_MEMORY == FALSE
+tL2C_CB l2cb;
+#endif
+
+/* Temporary - until l2cap implements group management */
+#if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
+extern void tcs_proc_bcst_msg( BD_ADDR addr, BT_HDR *p_msg ) ;
+
+/*******************************************************************************
+**
+** Function l2c_bcst_msg
+**
+** Description
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_bcst_msg( BT_HDR *p_buf, UINT16 psm )
+{
+ UINT8 *p;
+
+ /* Ensure we have enough space in the buffer for the L2CAP and HCI headers */
+ if (p_buf->offset < L2CAP_BCST_MIN_OFFSET)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - cannot send buffer, offset: %d", p_buf->offset);
+ GKI_freebuf (p_buf);
+ return;
+ }
+
+ /* Step back some bytes to add the headers */
+ p_buf->offset -= (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD);
+ p_buf->len += L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* First, the HCI transport header */
+ UINT16_TO_STREAM (p, 0x0050 | (L2CAP_PKT_START << 12) | (2 << 14));
+
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > btu_cb.hcit_acl_data_size)
+ {
+ UINT16_TO_STREAM (p, btu_cb.hcit_acl_data_size);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+
+ /* Now the L2CAP header */
+ UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
+ UINT16_TO_STREAM (p, L2CAP_CONNECTIONLESS_CID);
+ UINT16_TO_STREAM (p, psm);
+
+ p_buf->len += HCI_DATA_PREAMBLE_SIZE;
+
+ if (p_buf->len <= btu_cb.hcit_acl_pkt_size)
+ {
+ HCI_ACL_DATA_TO_LOWER (p_buf);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_rcv_acl_data
+**
+** Description This function is called from the HCI Interface when an ACL
+** data packet is received.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_rcv_acl_data (BT_HDR *p_msg)
+{
+ UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT16 handle, hci_len;
+ UINT8 pkt_type;
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb = NULL;
+ UINT16 l2cap_len, rcv_cid, psm;
+
+ /* Extract the handle */
+ STREAM_TO_UINT16 (handle, p);
+ pkt_type = HCID_GET_EVENT (handle);
+ handle = HCID_GET_HANDLE (handle);
+
+ /* Since the HCI Transport is putting segmented packets back together, we */
+ /* should never get a valid packet with the type set to "continuation" */
+ if (pkt_type != L2CAP_PKT_CONTINUE)
+ {
+ /* Find the LCB based on the handle */
+ if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL)
+ {
+ UINT8 cmd_code;
+
+ /* There is a slight possibility (specifically with USB) that we get an */
+ /* L2CAP connection request before we get the HCI connection complete. */
+ /* So for these types of messages, hold them for up to 2 seconds. */
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (l2cap_len, p);
+ STREAM_TO_UINT16 (rcv_cid, p);
+ STREAM_TO_UINT8 (cmd_code, p);
+
+ if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID)
+ && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ))
+ {
+ L2CAP_TRACE_WARNING5 ("L2CAP - holding ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
+ handle, p_msg->layer_specific, rcv_cid, cmd_code,
+ l2cb.rcv_hold_q.count);
+ p_msg->layer_specific = 2;
+ GKI_enqueue (&l2cb.rcv_hold_q, p_msg);
+
+ if (l2cb.rcv_hold_q.count == 1)
+ btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
+
+ return;
+ }
+ else
+ {
+ L2CAP_TRACE_ERROR5 ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d",
+ handle, p_msg->layer_specific, rcv_cid, cmd_code, l2cb.rcv_hold_q.count);
+ }
+ GKI_freebuf (p_msg);
+ return;
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - expected pkt start or complete, got: %d", pkt_type);
+ GKI_freebuf (p_msg);
+ return;
+ }
+
+ /* Extract the length and update the buffer header */
+ STREAM_TO_UINT16 (hci_len, p);
+ p_msg->offset += 4;
+
+#if (L2CAP_HOST_FLOW_CTRL == TRUE)
+ /* Send ack if we hit the threshold */
+ if (++p_lcb->link_pkts_unacked >= p_lcb->link_ack_thresh)
+ btu_hcif_send_host_rdy_for_data();
+#endif
+
+ /* Extract the length and CID */
+ STREAM_TO_UINT16 (l2cap_len, p);
+ STREAM_TO_UINT16 (rcv_cid, p);
+
+ /* Find the CCB for this CID */
+ if (rcv_cid >= L2CAP_BASE_APPL_CID)
+ {
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - unknown CID: 0x%04x", rcv_cid);
+ GKI_freebuf (p_msg);
+ return;
+ }
+ }
+
+ if (hci_len >= L2CAP_PKT_OVERHEAD) /* Must receive at least the L2CAP length and CID.*/
+ {
+ p_msg->len = hci_len - L2CAP_PKT_OVERHEAD;
+ p_msg->offset += L2CAP_PKT_OVERHEAD;
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - got incorrect hci header" );
+ GKI_freebuf (p_msg);
+ return;
+ }
+
+ if (l2cap_len != p_msg->len)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - bad length in pkt. Exp: %d Act: %d",
+ l2cap_len, p_msg->len);
+
+ GKI_freebuf (p_msg);
+ return;
+ }
+
+ /* Send the data through the channel state machine */
+ if (rcv_cid == L2CAP_SIGNALLING_CID)
+ {
+ process_l2cap_cmd (p_lcb, p, l2cap_len);
+ GKI_freebuf (p_msg);
+ }
+ else if (rcv_cid == L2CAP_CONNECTIONLESS_CID)
+ {
+ /* process_connectionless_data (p_lcb); */
+ STREAM_TO_UINT16 (psm, p);
+ L2CAP_TRACE_DEBUG1( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ;
+#if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
+ if (psm == TCS_PSM_INTERCOM || psm == TCS_PSM_CORDLESS)
+ {
+ p_msg->offset += L2CAP_BCST_OVERHEAD;
+ p_msg->len -= L2CAP_BCST_OVERHEAD;
+ tcs_proc_bcst_msg( p_lcb->remote_bd_addr, p_msg ) ;
+ GKI_freebuf (p_msg);
+ }
+ else
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ /* if it is not broadcast, check UCD registration */
+ if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) )
+ {
+ /* nothing to do */
+ }
+ else
+#endif
+ GKI_freebuf (p_msg);
+ }
+#if (BLE_INCLUDED == TRUE)
+ else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID)
+ {
+ l2cble_process_sig_cmd (p_lcb, p, l2cap_len);
+ GKI_freebuf (p_msg);
+ }
+#endif
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
+ (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) )
+ {
+ /* If no CCB for this channel, allocate one */
+ if (l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts))
+ {
+ p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ l2c_fcr_proc_pdu (p_ccb, p_msg);
+ else
+ (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_lcb->remote_bd_addr, p_msg);
+ }
+ else
+ GKI_freebuf (p_msg);
+ }
+#endif
+
+ else
+ {
+ if (p_ccb == NULL)
+ GKI_freebuf (p_msg);
+ else
+ {
+ /* 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);
+ else
+ {
+ /* eRTM or streaming mode, so we need to validate states first */
+ if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG))
+ l2c_fcr_proc_pdu (p_ccb, p_msg);
+ else
+ GKI_freebuf (p_msg);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function process_l2cap_cmd
+**
+** Description This function is called when a packet is received on the
+** L2CAP signalling CID
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
+{
+ UINT8 *p_pkt_end, *p_next_cmd, *p_cfg_end, *p_cfg_start;
+ UINT8 cmd_code, cfg_code, cfg_len, id;
+ tL2C_CONN_INFO con_info;
+ tL2CAP_CFG_INFO cfg_info;
+ UINT16 rej_reason, rej_mtu, lcid, rcid, info_type;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ BOOLEAN cfg_rej;
+ UINT16 cfg_rej_len, cmd_len;
+ UINT16 result;
+ tL2C_CONN_INFO ci;
+
+#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE)
+ /* if l2cap command received in CID 1 on top of an LE link, ignore this command */
+ if (p_lcb->is_ble_link)
+ return;
+#endif
+ p_next_cmd = p;
+ p_pkt_end = p + pkt_len;
+
+ memset (&cfg_info, 0, sizeof(cfg_info));
+
+ /* An L2CAP packet may contain multiple commands */
+ while (TRUE)
+ {
+ /* Smallest command is 4 bytes */
+ if ((p = p_next_cmd) > (p_pkt_end - 4))
+ break;
+
+ STREAM_TO_UINT8 (cmd_code, p);
+ STREAM_TO_UINT8 (id, p);
+ STREAM_TO_UINT16 (cmd_len, p);
+
+ /* Check command length does not exceed packet length */
+ if ((p_next_cmd = p + cmd_len) > p_pkt_end)
+ {
+ L2CAP_TRACE_WARNING3 ("Command len bad pkt_len: %d cmd_len: %d code: %d",
+ pkt_len, cmd_len, cmd_code);
+ break;
+ }
+
+ switch (cmd_code)
+ {
+ case L2CAP_CMD_REJECT:
+ STREAM_TO_UINT16 (rej_reason, p);
+ if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED)
+ {
+ STREAM_TO_UINT16 (rej_mtu, p);
+ /* What to do with the MTU reject ? We have negotiated an MTU. For now */
+ /* we will ignore it and let a higher protocol timeout take care of it */
+
+ L2CAP_TRACE_WARNING2 ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu);
+ }
+ if (rej_reason == L2CAP_CMD_REJ_INVALID_CID)
+ {
+ STREAM_TO_UINT16 (rcid, p);
+ STREAM_TO_UINT16 (lcid, p);
+
+ L2CAP_TRACE_WARNING2 ("L2CAP - rej with CID invalid, LCID: 0x%04x RCID: 0x%04x", lcid, rcid);
+
+ /* Remote CID invalid. Treat as a disconnect */
+ if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ && (p_ccb->remote_cid == rcid))
+ {
+ /* Fake link disconnect - no reply is generated */
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+ }
+ }
+
+ /* SonyEricsson Info request Bug workaround (Continue connection) */
+ else if (rej_reason == L2CAP_CMD_REJ_NOT_UNDERSTOOD && p_lcb->w4_info_rsp)
+ {
+ btu_stop_timer (&p_lcb->info_timer_entry);
+
+ p_lcb->w4_info_rsp = FALSE;
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ }
+ break;
+
+ case L2CAP_CMD_CONN_REQ:
+ STREAM_TO_UINT16 (con_info.psm, p);
+ STREAM_TO_UINT16 (rcid, p);
+ if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm);
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM);
+ break;
+ }
+ else
+ {
+ if (!p_rcb->api.pL2CA_ConnectInd_Cb)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for outgoing-only connection PSM: %d", con_info.psm);
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM);
+ break;
+ }
+ }
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - unable to allocate CCB");
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_RESOURCES);
+ break;
+ }
+ p_ccb->remote_id = id;
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->remote_cid = rcid;
+
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info);
+ break;
+
+ case L2CAP_CMD_CONN_RSP:
+ STREAM_TO_UINT16 (con_info.remote_cid, p);
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (con_info.l2cap_result, p);
+ STREAM_TO_UINT16 (con_info.l2cap_status, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - no CCB for conn rsp, LCID: %d RCID: %d",
+ lcid, con_info.remote_cid);
+ break;
+ }
+ if (p_ccb->local_id != id)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - con rsp - bad ID. Exp: %d Got: %d",
+ p_ccb->local_id, id);
+ break;
+ }
+
+ if (con_info.l2cap_result == L2CAP_CONN_OK)
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP, &con_info);
+ else if (con_info.l2cap_result == L2CAP_CONN_PENDING)
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_PND, &con_info);
+ else
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_NEG, &con_info);
+
+ break;
+
+ case L2CAP_CMD_CONFIG_REQ:
+ p_cfg_end = p + cmd_len;
+ cfg_rej = FALSE;
+ cfg_rej_len = 0;
+
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (cfg_info.flags, p);
+
+ p_cfg_start = p;
+
+ cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present =
+ cfg_info.fcr_present = cfg_info.fcs_present = FALSE;
+
+ while (p < p_cfg_end)
+ {
+ STREAM_TO_UINT8 (cfg_code, p);
+ STREAM_TO_UINT8 (cfg_len, p);
+
+ switch (cfg_code & 0x7F)
+ {
+ case L2CAP_CFG_TYPE_MTU:
+ cfg_info.mtu_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.mtu, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ cfg_info.flush_to_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.flush_to, p);
+ break;
+
+ case L2CAP_CFG_TYPE_QOS:
+ cfg_info.qos_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p);
+ STREAM_TO_UINT8 (cfg_info.qos.service_type, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_rate, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p);
+ STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p);
+ STREAM_TO_UINT32 (cfg_info.qos.latency, p);
+ STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCR:
+ cfg_info.fcr_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcr.mode, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mps, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCS:
+ cfg_info.fcs_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcs, p);
+ break;
+
+ case L2CAP_CFG_TYPE_EXT_FLOW:
+ cfg_info.ext_flow_spec_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p);
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p);
+ STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p);
+ break;
+
+ default:
+ /* sanity check option length */
+ if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len)
+ {
+ p += cfg_len;
+ if ((cfg_code & 0x80) == 0)
+ {
+ cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ cfg_rej = TRUE;
+ }
+ }
+ /* bad length; force loop exit */
+ else
+ {
+ p = p_cfg_end;
+ cfg_rej = TRUE;
+ }
+ break;
+ }
+ }
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ {
+ p_ccb->remote_id = id;
+ if (cfg_rej)
+ {
+ l2cu_send_peer_config_rej (p_ccb, p_cfg_start, (UINT16) (cmd_len - L2CAP_CONFIG_REQ_LEN), cfg_rej_len);
+ }
+ else
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info);
+ }
+ }
+ else
+ {
+ /* updated spec says send command reject on invalid cid */
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0);
+ }
+ break;
+
+ case L2CAP_CMD_CONFIG_RSP:
+ p_cfg_end = p + cmd_len;
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (cfg_info.flags, p);
+ STREAM_TO_UINT16 (cfg_info.result, p);
+
+ cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present =
+ cfg_info.fcr_present = cfg_info.fcs_present = FALSE;
+
+ while (p < p_cfg_end)
+ {
+ STREAM_TO_UINT8 (cfg_code, p);
+ STREAM_TO_UINT8 (cfg_len, p);
+
+ switch (cfg_code & 0x7F)
+ {
+ case L2CAP_CFG_TYPE_MTU:
+ cfg_info.mtu_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.mtu, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ cfg_info.flush_to_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.flush_to, p);
+ break;
+
+ case L2CAP_CFG_TYPE_QOS:
+ cfg_info.qos_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p);
+ STREAM_TO_UINT8 (cfg_info.qos.service_type, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_rate, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p);
+ STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p);
+ STREAM_TO_UINT32 (cfg_info.qos.latency, p);
+ STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCR:
+ cfg_info.fcr_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcr.mode, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mps, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCS:
+ cfg_info.fcs_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcs, p);
+ break;
+
+ case L2CAP_CFG_TYPE_EXT_FLOW:
+ cfg_info.ext_flow_spec_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p);
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p);
+ STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p);
+ break;
+ }
+ }
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ {
+ if (p_ccb->local_id != id)
+ {
+ L2CAP_TRACE_WARNING2 ("L2CAP - cfg rsp - bad ID. Exp: %d Got: %d",
+ p_ccb->local_id, id);
+ break;
+ }
+ if ( (cfg_info.result == L2CAP_CFG_OK) || (cfg_info.result == L2CAP_CFG_PENDING) )
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info);
+ else
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info);
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - rcvd cfg rsp for unknown CID: 0x%04x", lcid);
+ }
+ 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_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &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_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &con_info);
+ }
+ }
+ break;
+
+ case L2CAP_CMD_ECHO_REQ:
+
+#if (L2CAP_ENHANCED_FEATURES != 0)
+ if (!l2cu_check_feature_req (p_lcb, id, p, cmd_len))
+ {
+ if (cmd_len < (btu_cb.hcit_acl_pkt_size - L2CAP_PKT_OVERHEAD
+ - L2CAP_CMD_OVERHEAD
+ - L2CAP_ECHO_RSP_LEN
+ - HCI_DATA_PREAMBLE_SIZE))
+ {
+ l2cu_send_peer_echo_rsp (p_lcb, id, p, cmd_len);
+ }
+ else
+ {
+ l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0);
+ }
+ }
+#else
+ l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0);
+#endif
+ break;
+
+ case L2CAP_CMD_ECHO_RSP:
+#if (L2CAP_ENHANCED_FEATURES != 0)
+ l2cu_check_feature_rsp (p_lcb, id, p, cmd_len);
+#endif
+ if (p_lcb->p_echo_rsp_cb)
+ {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_OK);
+ }
+ break;
+
+ case L2CAP_CMD_INFO_REQ:
+ STREAM_TO_UINT16 (info_type, p);
+ l2cu_send_peer_info_rsp (p_lcb, id, info_type);
+ break;
+
+ case L2CAP_CMD_INFO_RSP:
+ /* Stop the link connect timer if sent before L2CAP connection is up */
+ if (p_lcb->w4_info_rsp)
+ {
+ btu_stop_timer (&p_lcb->info_timer_entry);
+ p_lcb->w4_info_rsp = FALSE;
+ }
+
+ STREAM_TO_UINT16 (info_type, p);
+ STREAM_TO_UINT16 (result, p);
+
+ p_lcb->info_rx_bits |= (1 << info_type);
+
+ if ( (info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (result == L2CAP_INFO_RESP_RESULT_SUCCESS) )
+ {
+ STREAM_TO_UINT32( p_lcb->peer_ext_fea, p );
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_lcb->peer_ext_fea & L2CAP_EXTFEA_FIXED_CHNLS)
+ {
+ l2cu_send_peer_info_req (p_lcb, L2CAP_FIXED_CHANNELS_INFO_TYPE);
+ break;
+ }
+ else
+ {
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+#endif
+ }
+
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE)
+ {
+ if (result == L2CAP_INFO_RESP_RESULT_SUCCESS)
+ {
+ memcpy (p_lcb->peer_chnl_mask, p, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+ }
+
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+#endif
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE)
+ {
+ if (result == L2CAP_INFO_RESP_RESULT_SUCCESS)
+ {
+ STREAM_TO_UINT16 (p_lcb->ucd_mtu, p);
+ }
+ }
+#endif
+
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING1 ("L2CAP - bad cmd code: %d", cmd_code);
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ return;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_process_held_packets
+**
+** Description This function processes any L2CAP packets that arrived before
+** the HCI connection complete arrived. It is a work around for
+** badly behaved controllers.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_process_held_packets (BOOLEAN timed_out)
+{
+ BT_HDR *p_buf, *p_buf1;
+ BUFFER_Q *p_rcv_hold_q = &l2cb.rcv_hold_q;
+
+ if (!p_rcv_hold_q->count)
+ return;
+
+ if (!timed_out)
+ {
+ btu_stop_timer(&l2cb.rcv_hold_tle);
+ L2CAP_TRACE_WARNING0("L2CAP HOLD CONTINUE");
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING0("L2CAP HOLD TIMEOUT");
+ }
+
+ /* Update the timeouts in the hold queue */
+ for (p_buf = (BT_HDR *)GKI_getfirst (p_rcv_hold_q); p_buf; p_buf = p_buf1)
+ {
+ p_buf1 = (BT_HDR *)GKI_getnext (p_buf);
+ if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0))
+ {
+ GKI_remove_from_queue (p_rcv_hold_q, p_buf);
+ p_buf->layer_specific = 0xFFFF;
+ l2c_rcv_acl_data (p_buf);
+ }
+ }
+
+ /* If anyone still in the queue, restart the timeout */
+ if (p_rcv_hold_q->count)
+ btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_init
+**
+** Description This function is called once at startup to initialize
+** all the L2CAP structures
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_init (void)
+{
+ INT16 xx;
+
+ memset (&l2cb, 0, sizeof (tL2C_CB));
+ /* the psm is increased by 2 before being used */
+ l2cb.dyn_psm = 0xFFF;
+
+ /* Put all the channel control blocks on the free queue */
+ for (xx = 0; xx < MAX_L2CAP_CHANNELS - 1; xx++)
+ {
+ l2cb.ccb_pool[xx].p_next_ccb = &l2cb.ccb_pool[xx + 1];
+ }
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ /* it will be set to L2CAP_PKT_START_NON_FLUSHABLE if controller supports */
+ l2cb.non_flushable_pbf = L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT;
+#endif
+
+
+ l2cb.p_free_ccb_first = &l2cb.ccb_pool[0];
+ l2cb.p_free_ccb_last = &l2cb.ccb_pool[MAX_L2CAP_CHANNELS - 1];
+
+#ifdef L2CAP_DESIRED_LINK_ROLE
+ l2cb.desire_role = L2CAP_DESIRED_LINK_ROLE;
+#else
+ l2cb.desire_role = HCI_ROLE_SLAVE;
+#endif
+
+ /* Set the default idle timeout */
+ l2cb.idle_timeout = L2CAP_LINK_INACTIVITY_TOUT;
+
+#if defined(L2CAP_INITIAL_TRACE_LEVEL)
+ l2cb.l2cap_trace_level = L2CAP_INITIAL_TRACE_LEVEL;
+#else
+ l2cb.l2cap_trace_level = BT_TRACE_LEVEL_NONE; /* No traces */
+#endif
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ /* Conformance testing needs a dynamic response */
+ l2cb.test_info_resp = L2CAP_EXTFEA_SUPPORTED_MASK;
+#endif
+
+ /* Number of ACL buffers to use for high priority channel */
+#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE))
+ l2cb.high_pri_min_xmit_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA;
+#endif
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_process_timeout
+**
+** Description This function is called when an L2CAP-related timeout occurs
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_process_timeout (TIMER_LIST_ENT *p_tle)
+{
+ /* What type of timeout ? */
+ switch (p_tle->event)
+ {
+ case BTU_TTYPE_L2CAP_LINK:
+ l2c_link_timeout ((tL2C_LCB *)p_tle->param);
+ break;
+
+ case BTU_TTYPE_L2CAP_CHNL:
+ l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
+ break;
+
+ case BTU_TTYPE_L2CAP_FCR_ACK:
+ l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_ACK_TIMEOUT, NULL);
+ break;
+
+ case BTU_TTYPE_L2CAP_HOLD:
+ /* Update the timeouts in the hold queue */
+ l2c_process_held_packets(TRUE);
+ break;
+
+ case BTU_TTYPE_L2CAP_INFO:
+ l2c_info_timeout((tL2C_LCB *)p_tle->param);
+ break;
+
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_data_write
+**
+** Description API functions call this function to write data.
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
+{
+ tL2C_CCB *p_ccb;
+
+ /* 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_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid);
+ GKI_freebuf (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+
+#ifndef TESTER /* Tester may send any amount of data. otherwise sending message
+ bigger than mtu size of peer is a violation of protocol */
+ if (p_data->len > p_ccb->peer_cfg.mtu)
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid);
+ GKI_freebuf (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+#endif
+
+ /* channel based, packet based flushable or non-flushable */
+ p_data->layer_specific = flags;
+
+ /* If already congested, do not accept any more packets */
+ if (p_ccb->cong_sent)
+ {
+ L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u",
+ p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
+
+ GKI_freebuf (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
+
+ if (p_ccb->cong_sent)
+ return (L2CAP_DW_CONGESTED);
+
+ return (L2CAP_DW_SUCCESS);
+}
+
diff --git a/stack/l2cap/l2c_ucd.c b/stack/l2cap/l2c_ucd.c
new file mode 100644
index 0000000..375988e
--- /dev/null
+++ b/stack/l2cap/l2c_ucd.c
@@ -0,0 +1,1158 @@
+/*****************************************************************************
+**
+** Name: l2c_ucd.c
+**
+** Description: This file contains the L2CAP UCD code
+**
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda );
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discover_cback
+**
+** Description UCD Discover callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_discover_cback (BD_ADDR rem_bda, UINT8 info_type, UINT32 data)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_discover_cback");
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if (p_rcb->in_use)
+ {
+ /* if this application is waiting UCD reception info */
+ if (( info_type == L2CAP_UCD_INFO_TYPE_RECEPTION )
+ && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION ))
+ {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data);
+ p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_RECEPTION);
+ }
+
+ /* if this application is waiting UCD MTU info */
+ if (( info_type == L2CAP_UCD_INFO_TYPE_MTU )
+ && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU ))
+ {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data);
+ p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_MTU);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_data_ind_cback
+**
+** Description UCD Data callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_data_ind_cback (BD_ADDR rem_bda, BT_HDR *p_buf)
+{
+ UINT8 *p;
+ UINT16 psm;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_data_ind_cback");
+
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ STREAM_TO_UINT16(psm, p)
+
+ p_buf->offset += L2CAP_UCD_OVERHEAD;
+ p_buf->len -= L2CAP_UCD_OVERHEAD;
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for l2c_ucd_data_ind_cback, PSM: 0x%04x", psm);
+ GKI_freebuf (p_buf);
+ }
+ else
+ {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(rem_bda, p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_congestion_status_cback
+**
+** Description UCD Congestion Status callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_congestion_status_cback (BD_ADDR rem_bda, BOOLEAN is_congested)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_congestion_status_cback");
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if (( p_rcb->in_use )
+ &&( p_rcb->ucd.state != L2C_UCD_STATE_UNUSED ))
+ {
+ if ( p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
+ {
+ L2CAP_TRACE_DEBUG4 ("L2CAP - Calling UCDCongestionStatus_Cb (%d), PSM=0x%04x, BDA: %08x%04x,",
+ is_congested, p_rcb->psm,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5]);
+
+ p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ( rem_bda, is_congested );
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_disconnect_ind_cback
+**
+** Description UCD disconnect callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_disconnect_ind_cback (UINT16 cid, BOOLEAN result)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_config_ind_cback
+**
+** Description UCD config callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_config_ind_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_config_cfm_cback
+**
+** Description UCD config callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_config_cfm_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdRegister
+**
+** Description Register PSM on UCD.
+**
+** Parameters: tL2CAP_UCD_CB_INFO
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info )
+{
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API1 ("L2CA_UcdRegister() PSM: 0x%04x", psm);
+
+ if ((!p_cb_info->pL2CA_UCD_Discover_Cb)
+ || (!p_cb_info->pL2CA_UCD_Data_Cb))
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - no callback registering PSM(0x%04x) on UCD", psm);
+ return (FALSE);
+ }
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for L2CA_UcdRegister, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ p_rcb->ucd.state = L2C_UCD_STATE_W4_DATA;
+ p_rcb->ucd.cb_info = *p_cb_info;
+
+ /* check if master rcb is created for UCD */
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL)
+ {
+ if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID)) == NULL)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - no RCB available for L2CA_UcdRegister");
+ return (FALSE);
+ }
+ else
+ {
+ /* these callback functions will forward data to each UCD application */
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb = l2c_ucd_discover_cback;
+ p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb = l2c_ucd_data_ind_cback;
+ p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb = l2c_ucd_congestion_status_cback;
+
+ memset (&p_rcb->api, 0, sizeof(tL2CAP_APPL_INFO));
+ p_rcb->api.pL2CA_DisconnectInd_Cb = l2c_ucd_disconnect_ind_cback;
+
+ /* This will make L2CAP check UCD congestion callback */
+ p_rcb->api.pL2CA_CongestionStatus_Cb = NULL;
+
+ /* do nothing but prevent crash */
+ p_rcb->api.pL2CA_ConfigInd_Cb = l2c_ucd_config_ind_cback;
+ p_rcb->api.pL2CA_ConfigCfm_Cb = l2c_ucd_config_cfm_cback;
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDeregister
+**
+** Description Deregister PSM on UCD.
+**
+** Parameters: PSM
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdDeregister ( UINT16 psm )
+{
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ UINT16 xx;
+
+ L2CAP_TRACE_API1 ("L2CA_UcdDeregister() PSM: 0x%04x", psm);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ {
+ L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for L2CA_UcdDeregister, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ p_rcb->ucd.state = L2C_UCD_STATE_UNUSED;
+
+ /* check this was the last UCD registration */
+ p_rcb = &l2cb.rcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if ((p_rcb->in_use) && (p_rcb->ucd.state != L2C_UCD_STATE_UNUSED))
+ return (TRUE);
+ }
+
+ /* delete master rcb for UCD */
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL)
+ {
+ l2cu_release_rcb (p_rcb);
+ }
+
+ /* delete CCB for UCD */
+ p_ccb = l2cb.ccb_pool;
+ for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ )
+ {
+ if (( p_ccb->in_use )
+ &&( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ))
+ {
+ l2cu_release_ccb (p_ccb);
+ }
+ p_ccb++;
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDiscover
+**
+** Description Discover UCD of remote device.
+**
+** Parameters: PSM
+** BD_ADDR of remote device
+** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION
+** L2CAP_UCD_INFO_TYPE_MTU
+**
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API4 ("L2CA_UcdDiscover() PSM: 0x%04x BDA: %08x%04x, InfoType=0x%02x", psm,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5], info_type);
+
+ /* Fail if the PSM is not registered */
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_UcdDiscover, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block for UCD. */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL))
+ {
+ if ( l2c_ucd_connect (rem_bda) == FALSE )
+ {
+ return (FALSE);
+ }
+ }
+
+ /* set waiting flags in rcb */
+
+ if ( info_type & L2CAP_UCD_INFO_TYPE_RECEPTION )
+ p_rcb->ucd.state |= L2C_UCD_STATE_W4_RECEPTION;
+
+ if ( info_type & L2CAP_UCD_INFO_TYPE_MTU )
+ p_rcb->ucd.state |= L2C_UCD_STATE_W4_MTU;
+
+ /* if link is already established */
+ if ((p_lcb)&&(p_lcb->link_state == LST_CONNECTED))
+ {
+ if (!p_ccb)
+ {
+ p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID);
+ }
+ l2c_ucd_check_pending_info_req(p_ccb);
+ }
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDataWrite
+**
+** Description Send UCD to remote device
+**
+** Parameters: PSM
+** BD Address of remote
+** Pointer to buffer of type BT_HDR
+** flags : L2CAP_FLUSHABLE_CH_BASED
+** L2CAP_FLUSHABLE_PKT
+** L2CAP_NON_FLUSHABLE_PKT
+**
+** Return value L2CAP_DW_SUCCESS, if data accepted
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ UINT8 *p;
+
+ L2CAP_TRACE_API3 ("L2CA_UcdDataWrite() PSM: 0x%04x BDA: %08x%04x", psm,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* Fail if the PSM is not registered */
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED ))
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm);
+ GKI_freebuf (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block for UCD */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL))
+ {
+ if ( l2c_ucd_connect (rem_bda) == FALSE )
+ {
+ GKI_freebuf (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* If we still don't have lcb and ccb after connect attempt, then can't proceed */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL))
+ {
+ GKI_freebuf (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+ }
+
+ /* write PSM */
+ p_buf->offset -= L2CAP_UCD_OVERHEAD;
+ p_buf->len += L2CAP_UCD_OVERHEAD;
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ UINT16_TO_STREAM (p, psm);
+
+ /* UCD MTU check */
+ if ((p_lcb->ucd_mtu) && (p_buf->len > p_lcb->ucd_mtu))
+ {
+ L2CAP_TRACE_WARNING1 ("L2CAP - Handle: 0x%04x UCD bigger than peer's UCD mtu size cannot be sent", p_lcb->handle);
+ GKI_freebuf (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* If already congested, do not accept any more packets */
+ if (p_ccb->cong_sent)
+ {
+ L2CAP_TRACE_ERROR3 ("L2CAP - Handle: 0x%04x UCD cannot be sent, already congested count: %u buff_quota: %u",
+ p_lcb->handle,
+ (p_ccb->xmit_hold_q.count + p_lcb->ucd_out_sec_pending_q.count),
+ p_ccb->buff_quota);
+
+ GKI_freebuf (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* channel based, packet based flushable or non-flushable */
+ p_buf->layer_specific = flags;
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_buf);
+
+ if (p_ccb->cong_sent)
+ return (L2CAP_DW_CONGESTED);
+ else
+ return (L2CAP_DW_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdSetIdleTimeout
+**
+** Description Set UCD Idle timeout.
+**
+** Parameters: BD Addr
+** Timeout in second
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API3 ("L2CA_UcdSetIdleTimeout() Timeout: 0x%04x BDA: %08x%04x", timeout,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block. */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL))
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no UCD channel");
+ return (FALSE);
+ }
+ else
+ {
+ p_ccb->fixed_chnl_idle_tout = timeout;
+ return (TRUE);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UCDSetTxPriority
+**
+** Description Sets the transmission priority for a connectionless channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API3 ("L2CA_UCDSetTxPriority() priority: 0x%02x BDA: %08x%04x", priority,
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5]);
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_UCDSetTxPriority");
+ return (FALSE);
+ }
+
+ /* Find the channel control block */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_UCDSetTxPriority");
+ return (FALSE);
+ }
+
+ /* it will update the order of CCB in LCB by priority and update round robin service variables */
+ l2cu_change_pri_ccb (p_ccb, priority);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_connect
+**
+** Description Connect UCD to remote device.
+**
+** Parameters: BD_ADDR of remote device
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_DEBUG2 ("l2c_ucd_connect() BDA: %08x%04x",
+ (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3],
+ (rem_bda[4]<<8)+rem_bda[5]);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING0 ("l2c_ucd_connect - BTU not ready");
+ return (FALSE);
+ }
+
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ if ( ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE)) == NULL)
+ || (l2cu_create_conn(p_lcb) == FALSE) )
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - conn not started l2c_ucd_connect");
+ return (FALSE);
+ }
+ }
+ else if ( p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) )
+ {
+ if (!(p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION))
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - UCD is not supported by peer, l2c_ucd_connect");
+ return (FALSE);
+ }
+ }
+
+ /* Find the channel control block. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)
+ {
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for l2c_ucd_connect");
+ return (FALSE);
+ }
+ else
+ {
+ /* Set CID for the connection */
+ p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID;
+ p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID;
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT;
+
+ /* Set the default channel priority value to use */
+ l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no UCD registered, l2c_ucd_connect");
+ return (FALSE);
+ }
+ /* Save UCD registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ /* There is no configuration, so if the link is up, the channel is up */
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_delete_sec_pending_q
+**
+** Description discard all of UCD packets in security pending queue
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb)
+{
+ /* clean up any security pending UCD */
+ while (p_lcb->ucd_out_sec_pending_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_lcb->ucd_out_sec_pending_q));
+
+ while (p_lcb->ucd_in_sec_pending_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_lcb->ucd_in_sec_pending_q));
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_info_req
+**
+** Description check if any application is waiting for UCD information
+**
+** Return TRUE if any pending UCD info request
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+ BOOLEAN pending = FALSE;
+
+ if (p_ccb == NULL)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - NULL p_ccb in l2c_ucd_check_pending_info_req");
+ return (FALSE);
+ }
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if (p_rcb->in_use)
+ {
+ /* if application is waiting UCD reception info */
+ if (p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION)
+ {
+ /* if this information is available */
+ if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) )
+ {
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION))
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - UCD is not supported by peer, l2c_ucd_check_pending_info_req");
+
+ l2c_ucd_delete_sec_pending_q(p_ccb->p_lcb);
+ l2cu_release_ccb (p_ccb);
+ }
+
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr,
+ L2CAP_UCD_INFO_TYPE_RECEPTION,
+ p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION);
+ }
+ else
+ {
+ pending = TRUE;
+ if (p_ccb->p_lcb->w4_info_rsp == FALSE)
+ {
+ l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE);
+ }
+ }
+ }
+
+ /* if application is waiting for UCD MTU */
+ if (p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU)
+ {
+ /* if this information is available */
+ if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_CONNLESS_MTU_INFO_TYPE))
+ {
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr,
+ L2CAP_UCD_INFO_TYPE_MTU,
+ p_ccb->p_lcb->ucd_mtu);
+ }
+ else
+ {
+ pending = TRUE;
+ if (p_ccb->p_lcb->w4_info_rsp == FALSE)
+ {
+ l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_CONNLESS_MTU_INFO_TYPE);
+ }
+ }
+ }
+ }
+ }
+ return (pending);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_enqueue_pending_out_sec_q
+**
+** Description enqueue outgoing UCD packet into security pending queue
+** and check congestion
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data)
+{
+ GKI_enqueue (&p_ccb->p_lcb->ucd_out_sec_pending_q, p_data);
+ l2cu_check_channel_congestion (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_out_sec_q
+**
+** Description check outgoing security
+**
+** Return TRUE if any UCD packet for security
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ UINT8 *p;
+ UINT16 psm;
+ BT_HDR *p_buf;
+
+ if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count )
+ {
+ p_buf = (BT_HDR*)(p_ccb->p_lcb->ucd_out_sec_pending_q.p_first);
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ STREAM_TO_UINT16(psm, p)
+
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm,
+ p_ccb->p_lcb->handle, CONNLESS_ORIG, &l2c_link_sec_comp, p_ccb);
+
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_send_pending_out_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** enqueue it into CCB
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+
+ if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count )
+ {
+ p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_out_sec_pending_q);
+
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_buf);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discard_pending_out_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** discard it.
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+
+ p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_out_sec_pending_q);
+
+ /* we may need to report to application */
+
+ if (p_buf)
+ {
+ GKI_freebuf (p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_in_sec_q
+**
+** Description check incoming security
+**
+** Return TRUE if any UCD packet for security
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ UINT8 *p;
+ UINT16 psm;
+ BT_HDR *p_buf;
+
+ if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count )
+ {
+ p_buf = (BT_HDR*)(p_ccb->p_lcb->ucd_in_sec_pending_q.p_first);
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ STREAM_TO_UINT16(psm, p)
+
+ p_ccb->chnl_state = CST_TERM_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm,
+ p_ccb->p_lcb->handle, CONNLESS_TERM, &l2c_link_sec_comp, p_ccb);
+
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_send_pending_in_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** send it to application
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+
+ if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count )
+ {
+ p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_in_sec_pending_q);
+
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discard_pending_in_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** discard it.
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+
+ p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_in_sec_pending_q);
+
+ if (p_buf)
+ {
+ GKI_freebuf (p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_rx_pkts
+**
+** Description Check if UCD reception is registered.
+** Process received UCD packet if application is expecting.
+**
+** Return TRUE if UCD reception is registered
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ 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))
+ {
+ if (p_ccb == NULL)
+ {
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for UCD reception");
+ GKI_freebuf (p_msg);
+ return TRUE;
+ }
+ else
+ {
+ /* Set CID for the connection */
+ p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID;
+ p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID;
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT;
+
+ /* Set the default channel priority value to use */
+ l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY);
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_msg);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_process_event
+**
+** Description This is called from main state machine when LCID is connectionless
+** Process the event if it is for UCD.
+**
+** Return TRUE if the event is consumed by UCD
+** FALSE if the event needs to be processed by main state machine
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ /* if the event is not processed by this function, this variable will be set to FALSE */
+ BOOLEAN done = TRUE;
+
+ switch (p_ccb->chnl_state)
+ {
+ case CST_CLOSED:
+ switch (event)
+ {
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ /* check if waiting for UCD info */
+ if (!l2c_ucd_check_pending_info_req (p_ccb))
+ {
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb))
+ {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ if (!l2c_ucd_check_pending_info_req (p_ccb))
+ {
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb))
+ {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ case CST_ORIG_W4_SEC_COMP:
+ switch (event)
+ {
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb))
+ {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ break;
+
+ case L2CEVT_SEC_COMP: /* Security completed success */
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_send_pending_out_sec_q(p_ccb);
+
+ if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count )
+ {
+ /* start a timer to send next UCD packet in OPEN state */
+ /* it will prevent stack overflow */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0);
+ }
+ else
+ {
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_discard_pending_out_sec_q(p_ccb);
+
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+
+ case CST_TERM_W4_SEC_COMP:
+ switch (event)
+ {
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_send_pending_in_sec_q (p_ccb);
+
+ if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count )
+ {
+ /* start a timer to check next UCD packet in OPEN state */
+ /* it will prevent stack overflow */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0);
+ }
+ else
+ {
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK)
+ {
+ done = FALSE;
+ break;
+ }
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_discard_pending_in_sec_q (p_ccb);
+
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data);
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ /* check if any incoming UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_in_sec_q(p_ccb))
+ {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ case CST_OPEN:
+ switch (event)
+ {
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ /* stop idle timer of UCD */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data);
+ l2c_ucd_check_pending_in_sec_q (p_ccb);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ /* stop idle timer of UCD */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+
+ /* coverity[check_return] */ /* coverity[unchecked_value] */
+ /* success changes state, failure stays in current state */
+ l2c_ucd_check_pending_out_sec_q (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* check if any UCD packet is waiting security check */
+ if ((!l2c_ucd_check_pending_in_sec_q(p_ccb))
+ &&(!l2c_ucd_check_pending_out_sec_q(p_ccb)))
+ {
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+
+ return done;
+}
+#endif /* (L2CAP_UCD_INCLUDED == TRUE) */
diff --git a/stack/l2cap/l2c_utils.c b/stack/l2cap/l2c_utils.c
new file mode 100644
index 0000000..0c1fb60
--- /dev/null
+++ b/stack/l2cap/l2c_utils.c
@@ -0,0 +1,3397 @@
+/*****************************************************************************
+**
+** Name: l2c_utils.c
+**
+**
+** Description: This file contains L2CAP utility functions
+**
+**
+**
+** Copyright (c) 1999-2012, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "hcimsgs.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include "hcidefs.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "hcidefs.h"
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_lcb
+**
+** Description Look for an unused LCB
+**
+** Returns LCB address or NULL if none found
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding)
+{
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if (!p_lcb->in_use)
+ {
+ memset (p_lcb, 0, sizeof (tL2C_LCB));
+
+ memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN);
+
+ p_lcb->in_use = TRUE;
+ p_lcb->link_state = LST_DISCONNECTED;
+ p_lcb->handle = HCI_INVALID_HANDLE;
+ p_lcb->link_flush_tout = 0xFFFF;
+ p_lcb->timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
+ p_lcb->info_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
+ p_lcb->idle_timeout = l2cb.idle_timeout;
+ p_lcb->id = 1; /* spec does not allow '0' */
+ p_lcb->is_bonding = is_bonding;
+
+ l2cb.num_links_active++;
+
+ l2c_link_adjust_allocation();
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no free LCB found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_update_lcb_4_bonding
+**
+** Description Mark the lcb for bonding. Used when bonding takes place on
+** an existing ACL connection. (Pre-Lisbon devices)
+**
+** Returns Nothing
+**
+*******************************************************************************/
+void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr);
+
+ if (p_lcb)
+ {
+ L2CAP_TRACE_DEBUG3 ("l2cu_update_lcb_4_bonding BDA: %08x%04x is_bonding: %d",
+ (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], is_bonding);
+ p_lcb->is_bonding = is_bonding;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_release_lcb
+**
+** Description Release an LCB. All timers will be stopped, channels
+** dropped, buffers returned etc.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_lcb (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+
+ p_lcb->in_use = FALSE;
+ p_lcb->is_bonding = FALSE;
+
+ /* Stop timers */
+ btu_stop_timer (&p_lcb->timer_entry);
+ btu_stop_timer (&p_lcb->info_timer_entry);
+
+ /* Release any unfinished L2CAP packet on this link */
+ if (p_lcb->p_hcit_rcv_acl)
+ {
+ GKI_freebuf(p_lcb->p_hcit_rcv_acl);
+ p_lcb->p_hcit_rcv_acl = NULL;
+ }
+
+#if BTM_SCO_INCLUDED == TRUE
+ /* Release all SCO links */
+ btm_remove_sco_links(p_lcb->remote_bd_addr);
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ p_lcb->is_ble_link = FALSE;
+ l2cb.is_ble_connecting = FALSE;
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ {
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ {
+ if (p_lcb->p_fixed_ccbs[xx])
+ {
+ l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason);
+ }
+ else if ( (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
+ && (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) )
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason);
+ }
+ }
+#endif
+
+ /* Ensure no CCBs left on this LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_lcb->ccb_queue.p_first_ccb)
+ {
+ l2cu_release_ccb (p_ccb);
+ }
+
+ /* Tell BTM Acl management the link was removed */
+ if ((p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_DISCONNECTING))
+ btm_acl_removed (p_lcb->remote_bd_addr);
+
+ /* Release any held buffers */
+ while (p_lcb->link_xmit_data_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_lcb->link_xmit_data_q));
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ /* clean up any security pending UCD */
+ l2c_ucd_delete_sec_pending_q(p_lcb);
+#endif
+
+ /* Re-adjust flow control windows make sure it does not go negative */
+ if (l2cb.num_links_active >= 1)
+ l2cb.num_links_active--;
+
+ l2c_link_adjust_allocation();
+
+ /* Check for ping outstanding */
+ if (p_lcb->p_echo_rsp_cb)
+ {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_NO_LINK);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_bd_addr
+**
+** Description Look through all active LCBs for a match based on the
+** remote BD address.
+**
+** Returns pointer to matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr)
+{
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (!memcmp (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN)))
+ {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_get_conn_role
+**
+** Description Determine the desired role (master or slave) of a link.
+** If already got a slave link, this one must be a master. If
+** already got at least 1 link where we are the master, make this
+** also a master.
+**
+** Returns HCI_ROLE_MASTER or HCI_ROLE_SLAVE
+**
+*******************************************************************************/
+UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb)
+{
+ return l2cb.desire_role;
+}
+
+/*******************************************************************************
+**
+** Function l2cu_build_header
+**
+** Description Builds the L2CAP command packet header
+**
+** Returns Pointer to allocated packet or NULL if no resources
+**
+*******************************************************************************/
+BT_HDR *l2cu_build_header (tL2C_LCB *p_lcb, UINT16 len, UINT8 cmd, UINT8 id)
+{
+ BT_HDR *p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID);
+ UINT8 *p;
+
+ if (!p_buf)
+ {
+ L2CAP_TRACE_ERROR0 ("l2cu_build_header - no buffer");
+ return (NULL);
+ }
+
+ p_buf->offset = L2CAP_SEND_CMD_OFFSET;
+ p_buf->len = len + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET;
+
+ /* Put in HCI header - handle + pkt boundary */
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ UINT16_TO_STREAM (p, p_lcb->handle | l2cb.non_flushable_pbf);
+#else
+ UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)));
+#endif
+
+ UINT16_TO_STREAM (p, len + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD);
+ UINT16_TO_STREAM (p, len + L2CAP_CMD_OVERHEAD);
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->is_ble_link)
+ {
+ UINT16_TO_STREAM (p, L2CAP_BLE_SIGNALLING_CID);
+ }
+ else
+#endif
+ {
+ UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID);
+ }
+
+ /* Put in L2CAP command header */
+ UINT8_TO_STREAM (p, cmd);
+ UINT8_TO_STREAM (p, id);
+ UINT16_TO_STREAM (p, len);
+
+ return (p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_adj_id
+**
+** Description Checks for valid ID based on specified mask
+** and adjusts the id if invalid.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask)
+{
+#if (L2CAP_ENHANCED_FEATURES != 0)
+ if ((adj_mask & L2CAP_ADJ_BRCM_ID) && p_lcb->id == L2CAP_FEATURE_REQ_ID)
+ {
+ p_lcb->id++;
+ }
+#endif
+
+ if ((adj_mask & L2CAP_ADJ_ZERO_ID) && !p_lcb->id)
+ {
+ p_lcb->id++;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_cmd_reject
+**
+** Description Build and send an L2CAP "command reject" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id,
+ UINT16 p1, UINT16 p2)
+{
+ UINT16 param_len;
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Put in L2CAP packet header */
+ if (reason == L2CAP_CMD_REJ_MTU_EXCEEDED)
+ param_len = 2;
+ else if (reason == L2CAP_CMD_REJ_INVALID_CID)
+ param_len = 4;
+ else
+ param_len = 0;
+
+ if ((p_buf = l2cu_build_header (p_lcb, (UINT16) (L2CAP_CMD_REJECT_LEN + param_len), L2CAP_CMD_REJECT, rem_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer cmd_rej");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, reason);
+
+ if (param_len >= 2)
+ UINT16_TO_STREAM (p, p1);
+
+ if (param_len >= 4)
+ UINT16_TO_STREAM (p, p2);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_connect_req
+**
+** Description Build and send an L2CAP "connection request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_connect_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, L2CAP_CONN_REQ_LEN, L2CAP_CMD_CONN_REQ,
+ p_ccb->local_id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req");
+ 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->p_rcb->real_psm);
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_connect_rsp
+**
+** Description Build and send an L2CAP "connection response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if (result == L2CAP_CONN_PENDING)
+ {
+ /* if we already sent pending response */
+ if (p_ccb->flags & CCB_FLAG_SENT_PENDING)
+ return;
+ else
+ p_ccb->flags |= CCB_FLAG_SENT_PENDING;
+ }
+
+ if ((p_buf=l2cu_build_header(p_ccb->p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, p_ccb->remote_id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_rsp");
+ 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->remote_cid);
+ UINT16_TO_STREAM (p, result);
+ UINT16_TO_STREAM (p, status);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_reject_connection
+**
+** Description Build and send an 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_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, rem_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req");
+ 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, remote_cid);
+ UINT16_TO_STREAM (p, result);
+ UINT16_TO_STREAM (p, 0); /* Status of 0 */
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_req
+**
+** Description Build and send an L2CAP "configuration request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BT_HDR *p_buf;
+ UINT16 cfg_len=0;
+ 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_cfg->mtu_present)
+ cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->flush_to_present)
+ cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->qos_present)
+ cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->fcr_present)
+ cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->fcs_present)
+ cfg_len += L2CAP_CFG_FCS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->ext_flow_spec_present)
+ cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16) (L2CAP_CONFIG_REQ_LEN + cfg_len),
+ L2CAP_CMD_CONFIG_REQ, p_ccb->local_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req");
+ 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_cfg->flags); /* Flags (continuation) */
+
+ /* Now, put the options */
+ if (p_cfg->mtu_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU);
+ UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->mtu);
+ }
+ if (p_cfg->flush_to_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->flush_to);
+ }
+ if (p_cfg->qos_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->qos.qos_flags);
+ UINT8_TO_STREAM (p, p_cfg->qos.service_type);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_rate);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size);
+ UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth);
+ UINT32_TO_STREAM (p, p_cfg->qos.latency);
+ UINT32_TO_STREAM (p, p_cfg->qos.delay_variation);
+ }
+ if (p_cfg->fcr_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcr.mode);
+ UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz);
+ UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit);
+ UINT16_TO_STREAM (p, p_cfg->fcr.rtrans_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mon_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mps);
+ }
+
+ if (p_cfg->fcs_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcs);
+ }
+
+ if (p_cfg->ext_flow_spec_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW);
+ UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype);
+ UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout);
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_rsp
+**
+** Description Build and send an L2CAP "configuration response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BT_HDR *p_buf;
+ UINT16 cfg_len = 0;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ if (p_cfg->mtu_present)
+ cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->flush_to_present)
+ cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->qos_present)
+ cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->fcr_present)
+ cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ if (p_cfg->ext_flow_spec_present)
+ cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16)(L2CAP_CONFIG_RSP_LEN + cfg_len),
+ L2CAP_CMD_CONFIG_RSP, p_ccb->remote_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req");
+ 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_cfg->flags); /* Flags (continuation) Must match request */
+ UINT16_TO_STREAM (p, p_cfg->result);
+
+ /* Now, put the options */
+ if (p_cfg->mtu_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU);
+ UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->mtu);
+ }
+ if (p_cfg->flush_to_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->flush_to);
+ }
+ if (p_cfg->qos_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->qos.qos_flags);
+ UINT8_TO_STREAM (p, p_cfg->qos.service_type);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_rate);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size);
+ UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth);
+ UINT32_TO_STREAM (p, p_cfg->qos.latency);
+ UINT32_TO_STREAM (p, p_cfg->qos.delay_variation);
+ }
+ if (p_cfg->fcr_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcr.mode);
+ UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz);
+ UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit);
+ UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.rtrans_tout);
+ UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.mon_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mps);
+ }
+
+ if (p_cfg->ext_flow_spec_present)
+ {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW);
+ UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype);
+ UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout);
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_rej
+**
+** Description Build and send an L2CAP "configuration reject" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len)
+{
+ BT_HDR *p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID);
+ UINT16 len, cfg_len;
+ UINT8 *p, *p_hci_len, *p_data_end;
+ UINT8 cfg_code;
+
+ if (!p_buf)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - no buffer for cfg_rej");
+ return;
+ }
+
+ p_buf->offset = L2CAP_SEND_CMD_OFFSET;
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET;
+
+ /* Put in HCI header - handle + pkt boundary */
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ if (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ()))
+ {
+ UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT)));
+ }
+ else
+#endif
+ {
+ UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)));
+ }
+
+ /* Remember the HCI header length position, and save space for it */
+ p_hci_len = p;
+ p += 2;
+
+ /* Put in L2CAP packet header */
+ UINT16_TO_STREAM (p, L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len);
+ UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID);
+
+ /* Put in L2CAP command header */
+ UINT8_TO_STREAM (p, L2CAP_CMD_CONFIG_RSP);
+ UINT8_TO_STREAM (p, p_ccb->remote_id);
+
+ UINT16_TO_STREAM (p, L2CAP_CONFIG_RSP_LEN + rej_len);
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, 0); /* Flags = 0 (no continuation) */
+ UINT16_TO_STREAM (p, L2CAP_CFG_UNKNOWN_OPTIONS);
+
+ /* Now, put the rejected options */
+ p_data_end = p_data + data_len;
+ while (p_data < p_data_end)
+ {
+ cfg_code = *p_data;
+ cfg_len = *(p_data + 1);
+
+ switch (cfg_code & 0x7F)
+ {
+ /* skip known options */
+ case L2CAP_CFG_TYPE_MTU:
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ case L2CAP_CFG_TYPE_QOS:
+ p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ break;
+
+ /* unknown options; copy into rsp if not hints */
+ default:
+ /* sanity check option length */
+ if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= data_len)
+ {
+ if ((cfg_code & 0x80) == 0)
+ {
+ memcpy(p, p_data, cfg_len + L2CAP_CFG_OPTION_OVERHEAD);
+ p += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ /* bad length; force loop exit */
+ else
+ {
+ p_data = p_data_end;
+ }
+ break;
+ }
+ }
+
+ len = (UINT16) (p - p_hci_len - 2);
+ UINT16_TO_STREAM (p_hci_len, len);
+
+ p_buf->len = len + 4;
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_disc_req
+**
+** Description Build and send an L2CAP "disconnect request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf, *p_buf2;
+ 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, L2CAP_DISC_REQ_LEN, L2CAP_CMD_DISC_REQ, p_ccb->local_id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for disc_req");
+ 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);
+
+ /* Move all queued data packets to the LCB. In FCR mode, assume the higher
+ layer checks that all buffers are sent before disconnecting.
+ */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
+ {
+ while (p_ccb->xmit_hold_q.p_first)
+ {
+ p_buf2 = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+ l2cu_set_acl_hci_header (p_buf2, p_ccb);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, p_ccb, p_buf2);
+ }
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_disc_rsp
+**
+** Description Build and send an L2CAP "disconnect response" message
+** to the peer.
+**
+** This function is passed the parameters for the disconnect
+** response instead of the CCB address, as it may be called
+** to send a disconnect response when there is no CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid,
+ UINT16 remote_cid)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf=l2cu_build_header(p_lcb, L2CAP_DISC_RSP_LEN, L2CAP_CMD_DISC_RSP, remote_id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for disc_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, local_cid);
+ UINT16_TO_STREAM (p, remote_cid);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_echo_req
+**
+** Description Build and send an L2CAP "echo request" message
+** to the peer. Note that we do not currently allow
+** data in the echo request.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ p_lcb->id++;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_ZERO_ID); /* check for wrap to '0' */
+
+ if ((p_buf = l2cu_build_header(p_lcb, (UINT16) (L2CAP_ECHO_REQ_LEN + data_len), L2CAP_CMD_ECHO_REQ, p_lcb->id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for echo_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ if (data_len)
+ {
+ ARRAY_TO_STREAM (p, p_data, data_len);
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_echo_rsp
+**
+** Description Build and send an L2CAP "echo response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 maxlen;
+
+ /* Don't return data if it does not fit in ACL and L2CAP MTU */
+ maxlen = (GKI_get_pool_bufsize(L2CAP_CMD_POOL_ID) > btu_cb.hcit_acl_pkt_size) ?
+ btu_cb.hcit_acl_data_size : (UINT16)GKI_get_pool_bufsize(L2CAP_CMD_POOL_ID);
+ maxlen -= (UINT16)(BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD +
+ L2CAP_CMD_OVERHEAD + L2CAP_ECHO_RSP_LEN);
+
+ if (data_len > maxlen)
+ data_len = 0;
+
+ if ((p_buf = l2cu_build_header (p_lcb, (UINT16)(L2CAP_ECHO_RSP_LEN + data_len), L2CAP_CMD_ECHO_RSP, id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for echo_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ if (data_len)
+ {
+ ARRAY_TO_STREAM (p, p_data, data_len);
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_info_req
+**
+** Description Build and send an L2CAP "info request" message
+** to the peer.
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* check for wrap and/or BRCM ID */
+ p_lcb->id++;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_ID);
+
+ if ((p_buf = l2cu_build_header(p_lcb, 2, L2CAP_CMD_INFO_REQ, p_lcb->id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for info_req");
+ return;
+ }
+
+ L2CAP_TRACE_EVENT1 ("l2cu_send_peer_info_req: type 0x%04x", info_type);
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, info_type);
+
+ p_lcb->w4_info_rsp = TRUE;
+ btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_info_rsp
+**
+** Description Build and send an L2CAP "info response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 info_type)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 len = L2CAP_INFO_RSP_LEN;
+
+#if (L2CAP_CONFORMANCE_TESTING == TRUE)
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (l2cb.test_info_resp & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE |
+ L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_EXT_FLOW_SPEC |
+ L2CAP_EXTFEA_FIXED_CHNLS | L2CAP_EXTFEA_EXT_WINDOW |
+ L2CAP_EXTFEA_UCD_RECEPTION )) )
+#else
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (L2CAP_EXTFEA_SUPPORTED_MASK & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE |
+ L2CAP_EXTFEA_NO_CRC |L2CAP_EXTFEA_FIXED_CHNLS |
+ L2CAP_EXTFEA_UCD_RECEPTION )) )
+#endif
+ {
+ len += L2CAP_EXTENDED_FEATURES_ARRAY_SIZE;
+ }
+ else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE)
+ {
+ len += L2CAP_FIXED_CHNL_ARRAY_SIZE;
+ }
+ else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE)
+ {
+ len += L2CAP_CONNLESS_MTU_INFO_SIZE;
+ }
+
+ if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_INFO_RSP, remote_id)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for info_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, info_type);
+
+#if (L2CAP_CONFORMANCE_TESTING == TRUE)
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (l2cb.test_info_resp & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE
+ | L2CAP_EXTFEA_UCD_RECEPTION )) )
+#else
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (L2CAP_EXTFEA_SUPPORTED_MASK & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE
+ | L2CAP_EXTFEA_UCD_RECEPTION )) )
+#endif
+ {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->is_ble_link)
+ {
+ /* optional data are not added for now */
+ UINT32_TO_STREAM (p, L2CAP_BLE_EXTFEA_MASK);
+ }
+ else
+#endif
+ {
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ UINT32_TO_STREAM (p, l2cb.test_info_resp);
+#else
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK | L2CAP_EXTFEA_FIXED_CHNLS);
+#else
+ UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK);
+#endif
+#endif
+ }
+ }
+ else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE)
+ {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+ memset (p, 0, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+
+ p[0] = L2CAP_FIXED_CHNL_SIG_BIT;
+
+ if ( L2CAP_EXTFEA_SUPPORTED_MASK & L2CAP_EXTFEA_UCD_RECEPTION )
+ p[0] |= L2CAP_FIXED_CHNL_CNCTLESS_BIT;
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ {
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL)
+ p[0] |= 1 << (xx + L2CAP_FIRST_FIXED_CHNL);
+ }
+#endif
+ }
+ else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE)
+ {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+ UINT16_TO_STREAM (p, L2CAP_UCD_MTU);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED); /* 'not supported' */
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/******************************************************************************
+**
+** Function l2cu_enqueue_ccb
+**
+** Description queue CCB by priority. The first CCB is highest priority and
+** is served at first. The CCB is queued to an LLCB or an LCB.
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_enqueue_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_CCB *p_ccb1;
+ tL2C_CCB_Q *p_q = NULL;
+
+ /* Find out which queue the channel is on
+ */
+ if (p_ccb->p_lcb != NULL)
+ p_q = &p_ccb->p_lcb->ccb_queue;
+
+ if ( (!p_ccb->in_use) || (p_q == NULL) )
+ {
+ L2CAP_TRACE_ERROR3 ("l2cu_enqueue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: 0x%08x",
+ p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb);
+ return;
+ }
+
+ L2CAP_TRACE_DEBUG2 ("l2cu_enqueue_ccb CID: 0x%04x priority: %d",
+ p_ccb->local_cid, p_ccb->ccb_priority);
+
+ /* If the queue is empty, we go at the front */
+ if (!p_q->p_first_ccb)
+ {
+ p_q->p_first_ccb = p_q->p_last_ccb = p_ccb;
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+ }
+ else
+ {
+ p_ccb1 = p_q->p_first_ccb;
+
+ while (p_ccb1 != NULL)
+ {
+ /* Insert new ccb at the end of the same priority. Lower number, higher priority */
+ if (p_ccb->ccb_priority < p_ccb1->ccb_priority)
+ {
+ /* Are we at the head of the queue ? */
+ if (p_ccb1 == p_q->p_first_ccb)
+ p_q->p_first_ccb = p_ccb;
+ else
+ p_ccb1->p_prev_ccb->p_next_ccb = p_ccb;
+
+ p_ccb->p_next_ccb = p_ccb1;
+ p_ccb->p_prev_ccb = p_ccb1->p_prev_ccb;
+ p_ccb1->p_prev_ccb = p_ccb;
+ break;
+ }
+
+ p_ccb1 = p_ccb1->p_next_ccb;
+ }
+
+ /* If we are lower then anyone in the list, we go at the end */
+ if (!p_ccb1)
+ {
+ /* add new ccb at the end of the list */
+ p_q->p_last_ccb->p_next_ccb = p_ccb;
+
+ p_ccb->p_next_ccb = NULL;
+ p_ccb->p_prev_ccb = p_q->p_last_ccb;
+ p_q->p_last_ccb = p_ccb;
+ }
+ }
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* Adding CCB into round robin service table of its LCB */
+ if (p_ccb->p_lcb != NULL)
+ {
+ /* if this is the first channel in this priority group */
+ if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 )
+ {
+ /* Set the first channel to this CCB */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb;
+ /* Set the next serving channel in this group to this CCB */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb;
+ /* Initialize quota of this priority group based on its priority */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority);
+ }
+ /* increase number of channels in this group */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb++;
+ }
+#endif
+
+}
+
+/******************************************************************************
+**
+** Function l2cu_dequeue_ccb
+**
+** Description dequeue CCB from a queue
+**
+** Returns -
+**
+*******************************************************************************/
+void l2cu_dequeue_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_CCB_Q *p_q = NULL;
+
+ L2CAP_TRACE_DEBUG1 ("l2cu_dequeue_ccb CID: 0x%04x", p_ccb->local_cid);
+
+ /* Find out which queue the channel is on
+ */
+ if (p_ccb->p_lcb != NULL)
+ p_q = &p_ccb->p_lcb->ccb_queue;
+
+ if ( (!p_ccb->in_use) || (p_q == NULL) || (p_q->p_first_ccb == NULL) )
+ {
+ L2CAP_TRACE_ERROR5 ("l2cu_dequeue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: 0x%08x p_q: 0x%08x p_q->p_first_ccb: 0x%08x",
+ p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb, p_q, p_q ? p_q->p_first_ccb : 0);
+ return;
+ }
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* Removing CCB from round robin service table of its LCB */
+ if (p_ccb->p_lcb != NULL)
+ {
+ /* decrease number of channels in this priority group */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb--;
+
+ /* if it was the last channel in the priority group */
+ if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 )
+ {
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL;
+ }
+ else
+ {
+ /* if it is the first channel of this group */
+ if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb == p_ccb )
+ {
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb->p_next_ccb;
+ }
+ /* if it is the next serving channel of this group */
+ if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb == p_ccb )
+ {
+ /* simply, start serving from the first channel */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb
+ = p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb;
+ }
+ }
+ }
+#endif
+
+ if (p_ccb == p_q->p_first_ccb)
+ {
+ /* We are removing the first in a queue */
+ p_q->p_first_ccb = p_ccb->p_next_ccb;
+
+ if (p_q->p_first_ccb)
+ p_q->p_first_ccb->p_prev_ccb = NULL;
+ else
+ p_q->p_last_ccb = NULL;
+ }
+ else if (p_ccb == p_q->p_last_ccb)
+ {
+ /* We are removing the last in a queue */
+ p_q->p_last_ccb = p_ccb->p_prev_ccb;
+ p_q->p_last_ccb->p_next_ccb = NULL;
+ }
+ else
+ {
+ /* In the middle of a chain. */
+ p_ccb->p_prev_ccb->p_next_ccb = p_ccb->p_next_ccb;
+ p_ccb->p_next_ccb->p_prev_ccb = p_ccb->p_prev_ccb;
+ }
+
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+}
+
+/******************************************************************************
+**
+** Function l2cu_change_pri_ccb
+**
+** Description
+**
+** Returns -
+**
+*******************************************************************************/
+void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority)
+{
+ if (p_ccb->ccb_priority != priority)
+ {
+ /* If CCB is not the only guy on the queue */
+ if ( (p_ccb->p_next_ccb != NULL) || (p_ccb->p_prev_ccb != NULL) )
+ {
+ L2CAP_TRACE_DEBUG0 ("Update CCB list in logical link");
+
+ /* Remove CCB from queue and re-queue it at new priority */
+ l2cu_dequeue_ccb (p_ccb);
+
+ p_ccb->ccb_priority = priority;
+ l2cu_enqueue_ccb (p_ccb);
+ }
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ else
+ {
+ /* If CCB is the only guy on the queue, no need to re-enqueue */
+ /* update only round robin service data */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 0;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL;
+
+ p_ccb->ccb_priority = priority;
+
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority);
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 1;
+ }
+#endif
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_ccb
+**
+** Description This function allocates a Channel Control Block and
+** attaches it to a link control block. The local CID
+** is also assigned.
+**
+** Returns pointer to CCB, or NULL if none
+**
+*******************************************************************************/
+tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_prev;
+
+ L2CAP_TRACE_DEBUG1 ("l2cu_allocate_ccb: cid 0x%04x", cid);
+
+ if (!l2cb.p_free_ccb_first)
+ return (NULL);
+
+ /* If a CID was passed in, use that, else take the first free one */
+ if (cid == 0)
+ {
+ p_ccb = l2cb.p_free_ccb_first;
+ l2cb.p_free_ccb_first = p_ccb->p_next_ccb;
+ }
+ else
+ {
+ p_prev = NULL;
+
+ p_ccb = &l2cb.ccb_pool[cid - L2CAP_BASE_APPL_CID];
+
+ if (p_ccb == l2cb.p_free_ccb_first)
+ l2cb.p_free_ccb_first = p_ccb->p_next_ccb;
+ else
+ {
+ for (p_prev = l2cb.p_free_ccb_first; p_prev != NULL; p_prev = p_prev->p_next_ccb)
+ {
+ if (p_prev->p_next_ccb == p_ccb)
+ {
+ p_prev->p_next_ccb = p_ccb->p_next_ccb;
+
+ if (p_ccb == l2cb.p_free_ccb_last)
+ l2cb.p_free_ccb_last = p_prev;
+
+ break;
+ }
+ }
+ if (p_prev == NULL)
+ {
+ L2CAP_TRACE_ERROR1 ("l2cu_allocate_ccb: could not find CCB for CID 0x%04x in the free list", cid);
+ return NULL;
+ }
+ }
+ }
+
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+
+ p_ccb->in_use = TRUE;
+
+ /* Get a CID for the connection */
+ p_ccb->local_cid = L2CAP_BASE_APPL_CID + (UINT16)(p_ccb - l2cb.ccb_pool);
+
+ p_ccb->p_lcb = p_lcb;
+ p_ccb->p_rcb = NULL;
+
+ /* Set priority then insert ccb into LCB queue (if we have an LCB) */
+ p_ccb->ccb_priority = L2CAP_CHNL_PRIORITY_LOW;
+
+ if (p_lcb)
+ l2cu_enqueue_ccb (p_ccb);
+
+ /* clear what peer wants to configure */
+ p_ccb->peer_cfg_bits = 0;
+
+ /* Put in default values for configuration */
+ memset (&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ memset (&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ /* 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;
+ p_ccb->our_cfg.qos.service_type = p_ccb->peer_cfg.qos.service_type = L2CAP_DEFAULT_SERV_TYPE;
+ p_ccb->our_cfg.qos.token_rate = p_ccb->peer_cfg.qos.token_rate = L2CAP_DEFAULT_TOKEN_RATE;
+ p_ccb->our_cfg.qos.token_bucket_size = p_ccb->peer_cfg.qos.token_bucket_size = L2CAP_DEFAULT_BUCKET_SIZE;
+ p_ccb->our_cfg.qos.peak_bandwidth = p_ccb->peer_cfg.qos.peak_bandwidth = L2CAP_DEFAULT_PEAK_BANDWIDTH;
+ p_ccb->our_cfg.qos.latency = p_ccb->peer_cfg.qos.latency = L2CAP_DEFAULT_LATENCY;
+ p_ccb->our_cfg.qos.delay_variation = p_ccb->peer_cfg.qos.delay_variation = L2CAP_DEFAULT_DELAY;
+
+ p_ccb->bypass_fcs = 0;
+ memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO));
+ p_ccb->peer_cfg_already_rejected = FALSE;
+ p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES;
+ p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb;
+
+ /* if timer is running, remove it from timer list */
+ if (p_ccb->fcrb.ack_timer.in_use)
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+
+ p_ccb->fcrb.mon_retrans_timer.param = (TIMER_PARAM_TYPE)p_ccb;
+
+// btla-specific ++
+ /* CSP408639 Fix: When L2CAP send amp move channel request or receive
+ * L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move
+ * request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */
+ if (p_ccb->fcrb.mon_retrans_timer.in_use)
+ btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+// btla-specific --
+
+ l2c_fcr_stop_timer (p_ccb);
+
+ p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */
+ p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC; /* Default mode for channel is basic mode */
+ p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID;
+ p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID;
+ p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID;
+ p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID;
+ p_ccb->max_rx_mtu = L2CAP_MTU_SIZE;
+ p_ccb->tx_mps = GKI_get_pool_bufsize(HCI_ACL_POOL_ID) - 32;
+
+ GKI_init_q (&p_ccb->xmit_hold_q);
+
+ p_ccb->cong_sent = FALSE;
+ p_ccb->buff_quota = 2; /* This gets set after config */
+
+ /* If CCB was reserved Config_Done can already have some value */
+ if (cid == 0)
+ p_ccb->config_done = 0;
+ else
+ {
+ L2CAP_TRACE_DEBUG2 ("l2cu_allocate_ccb: cid 0x%04x config_done:0x%x", cid, p_ccb->config_done);
+ }
+
+ p_ccb->chnl_state = CST_CLOSED;
+ p_ccb->flags = 0;
+ p_ccb->tx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
+ p_ccb->rx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ p_ccb->is_flushable = FALSE;
+#endif
+
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
+ p_ccb->timer_entry.in_use = 0;
+
+ l2c_link_adjust_chnl_allocation ();
+
+ return (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_start_post_bond_timer
+**
+** Description This function starts the ACL Link inactivity timer after
+** dedicated bonding
+** This timer can be longer than the normal link inactivity
+** timer for some platforms.
+**
+** Returns BOOLEAN - TRUE if idle timer started or disconnect initiated
+** FALSE if there's one or more pending CCB's exist
+**
+*******************************************************************************/
+BOOLEAN l2cu_start_post_bond_timer (UINT16 handle)
+{
+ UINT16 timeout;
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle);
+
+ if (!p_lcb)
+ return (TRUE);
+
+ p_lcb->is_bonding = FALSE;
+
+ /* Only start timer if no control blocks allocated */
+ if (p_lcb->ccb_queue.p_first_ccb != NULL)
+ return (FALSE);
+
+ /* If no channels on the connection, start idle timeout */
+ if ( (p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_DISCONNECTING) )
+ {
+ if (p_lcb->idle_timeout == 0)
+ {
+ if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER))
+ {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ }
+ else
+ timeout = BT_1SEC_TIMEOUT;
+ }
+ else
+ {
+ timeout = L2CAP_BONDING_TIMEOUT;
+ }
+
+ if (timeout != 0xFFFF)
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_release_ccb
+**
+** Description This function releases a Channel Control Block. The timer
+** is stopped, any attached buffers freed, and the CCB is removed
+** from the link control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_LCB *p_lcb = p_ccb->p_lcb;
+ tL2C_RCB *p_rcb = p_ccb->p_rcb;
+
+ L2CAP_TRACE_DEBUG2 ("l2cu_release_ccb: cid 0x%04x in_use: %u", p_ccb->local_cid, p_ccb->in_use);
+
+ /* If already released, could be race condition */
+ if (!p_ccb->in_use)
+ return;
+
+ if (p_rcb && (p_rcb->psm != p_rcb->real_psm))
+ {
+ btm_sec_clr_service_by_psm(p_rcb->psm);
+ }
+ /* Stop the timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ while (p_ccb->xmit_hold_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_ccb->xmit_hold_q));
+
+ l2c_fcr_cleanup (p_ccb);
+
+ /* Channel may not be assigned to any LCB if it was just pre-reserved */
+ if ( (p_lcb) &&
+ ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID)
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ ||(p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID)
+#endif
+ )
+ )
+ {
+ l2cu_dequeue_ccb (p_ccb);
+
+ /* Delink the CCB from the LCB */
+ p_ccb->p_lcb = NULL;
+ }
+
+ /* Put the CCB back on the free pool */
+ if (!l2cb.p_free_ccb_first)
+ {
+ l2cb.p_free_ccb_first = p_ccb;
+ l2cb.p_free_ccb_last = p_ccb;
+ p_ccb->p_next_ccb = NULL;
+ p_ccb->p_prev_ccb = NULL;
+ }
+ else
+ {
+ p_ccb->p_next_ccb = NULL;
+ p_ccb->p_prev_ccb = l2cb.p_free_ccb_last;
+ l2cb.p_free_ccb_last->p_next_ccb = p_ccb;
+ l2cb.p_free_ccb_last = p_ccb;
+ }
+
+ /* Flag as not in use */
+ p_ccb->in_use = FALSE;
+
+ /* If no channels on the connection, start idle timeout */
+ if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED))
+ {
+ if (!p_lcb->ccb_queue.p_first_ccb)
+ {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+ else
+ {
+ /* Link is still active, adjust channel quotas. */
+ l2c_link_adjust_chnl_allocation ();
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_find_ccb_by_remote_cid
+**
+** Description Look through all active CCBs on a link for a match based
+** on the remote CID.
+**
+** Returns pointer to matched CCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid)
+{
+ 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->remote_cid == remote_cid))
+ return (p_ccb);
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_rcb
+**
+** Description Look through the Registration Control Blocks for a free
+** one.
+**
+** Returns Pointer to the RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_allocate_rcb (UINT16 psm)
+{
+ 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->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);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_release_rcb
+**
+** Description Mark an RCB as no longet in use
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_rcb (tL2C_RCB *p_rcb)
+{
+ p_rcb->in_use = FALSE;
+ p_rcb->psm = 0;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_disconnect_chnl
+**
+** Description Disconnect a channel. Typically, this is due to either
+** receiving a bad configuration, bad packet or max_retries expiring.
+**
+*******************************************************************************/
+void l2cu_disconnect_chnl (tL2C_CCB *p_ccb)
+{
+ UINT16 local_cid = p_ccb->local_cid;
+
+ if (local_cid >= L2CAP_BASE_APPL_CID)
+ {
+ tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+
+ L2CAP_TRACE_WARNING1 ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid);
+
+ l2cu_send_peer_disc_req (p_ccb);
+
+ l2cu_release_ccb (p_ccb);
+
+ (*p_disc_cb)(local_cid, FALSE);
+ }
+ else
+ {
+ /* failure on the AMP channel, probably need to disconnect ACL */
+ L2CAP_TRACE_ERROR1 ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_rcb_by_psm
+**
+** Description Look through the Registration Control Blocks to see if
+** anyone registered to handle the PSM in question
+**
+** Returns Pointer to the RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm)
+{
+ 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))
+ return (p_rcb);
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_peer_cfg_req
+**
+** Description This function is called when the peer sends us a "config request"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Note: Negotiation of the FCR channel type is handled internally,
+** all others are passed to the upper layer.
+**
+** Returns UINT8 - L2CAP_PEER_CFG_OK if passed to upper layer,
+** L2CAP_PEER_CFG_UNACCEPTABLE if automatically responded to
+** because parameters are unnacceptable from a specification
+** point of view.
+** L2CAP_PEER_CFG_DISCONNECT if no compatible channel modes
+** between the two devices, and shall be closed.
+**
+*******************************************************************************/
+UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BOOLEAN mtu_ok = TRUE;
+ BOOLEAN qos_type_ok = TRUE;
+ BOOLEAN flush_to_ok = TRUE;
+ BOOLEAN fcr_ok = TRUE;
+ UINT8 fcr_status;
+
+ /* Ignore FCR parameters for basic mode */
+ if (!p_cfg->fcr_present)
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+
+ /* Save the MTU that our peer can receive */
+ if (p_cfg->mtu_present)
+ {
+ /* Make sure MTU is at least the minimum */
+ if (p_cfg->mtu >= L2CAP_MIN_MTU)
+ {
+ /* In basic mode, limit the MTU to our buffer size */
+ if ( (p_cfg->fcr_present == FALSE) && (p_cfg->mtu > L2CAP_MTU_SIZE) )
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+
+ /* Save the accepted value in case of renegotiation */
+ p_ccb->peer_cfg.mtu = p_cfg->mtu;
+ p_ccb->peer_cfg.mtu_present = TRUE;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
+ }
+ else /* Illegal MTU value */
+ {
+ p_cfg->mtu = L2CAP_MIN_MTU;
+ mtu_ok = FALSE;
+ }
+ }
+ /* Reload mtu from a previously accepted config request */
+ else if (p_ccb->peer_cfg.mtu_present)
+ {
+ p_cfg->mtu_present = TRUE;
+ p_cfg->mtu = p_ccb->peer_cfg.mtu;
+ }
+
+ /* Verify that the flush timeout is a valid value (0 is illegal) */
+ if (p_cfg->flush_to_present)
+ {
+ if (!p_cfg->flush_to)
+ {
+ p_cfg->flush_to = 0xFFFF; /* Infinite retransmissions (spec default) */
+ flush_to_ok = FALSE;
+ }
+ else /* Save the accepted value in case of renegotiation */
+ {
+ p_ccb->peer_cfg.flush_to_present = TRUE;
+ p_ccb->peer_cfg.flush_to = p_cfg->flush_to;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
+ }
+ }
+ /* Reload flush_to from a previously accepted config request */
+ else if (p_ccb->peer_cfg.flush_to_present)
+ {
+ p_cfg->flush_to_present = TRUE;
+ p_cfg->flush_to = p_ccb->peer_cfg.flush_to;
+ }
+
+ /* Save the QOS settings the the peer is using */
+ if (p_cfg->qos_present)
+ {
+ /* Make sure service type is not a reserved value; otherwise let upper
+ layer decide if acceptable
+ */
+ if (p_cfg->qos.service_type <= GUARANTEED)
+ {
+ p_ccb->peer_cfg.qos = p_cfg->qos;
+ p_ccb->peer_cfg.qos_present = TRUE;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
+ }
+ else /* Illegal service type value */
+ {
+ p_cfg->qos.service_type = BEST_EFFORT;
+ qos_type_ok = FALSE;
+ }
+ }
+ /* Reload QOS from a previously accepted config request */
+ else if (p_ccb->peer_cfg.qos_present)
+ {
+ p_cfg->qos_present = TRUE;
+ p_cfg->qos = p_ccb->peer_cfg.qos;
+ }
+
+ if ((fcr_status = l2c_fcr_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_DISCONNECT)
+ {
+ /* Notify caller to disconnect the channel (incompatible modes) */
+ p_cfg->result = L2CAP_CFG_FAILED_NO_REASON;
+ p_cfg->mtu_present = p_cfg->qos_present = p_cfg->flush_to_present = 0;
+
+ return (L2CAP_PEER_CFG_DISCONNECT);
+ }
+
+ fcr_ok = (fcr_status == L2CAP_PEER_CFG_OK);
+
+ /* Return any unacceptable parameters */
+ if (mtu_ok && flush_to_ok && qos_type_ok && fcr_ok)
+ {
+ l2cu_adjust_out_mps (p_ccb);
+ return (L2CAP_PEER_CFG_OK);
+ }
+ else
+ {
+ p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+
+ if (mtu_ok)
+ p_cfg->mtu_present = FALSE;
+ if (flush_to_ok)
+ p_cfg->flush_to_present = FALSE;
+ if (qos_type_ok)
+ p_cfg->qos_present = FALSE;
+ if (fcr_ok)
+ p_cfg->fcr_present = FALSE;
+
+ return (L2CAP_PEER_CFG_UNACCEPTABLE);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_peer_cfg_rsp
+**
+** Description This function is called when the peer sends us a "config response"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* If we wanted QoS and the peer sends us a positive response with QoS, use his values */
+ if ( (p_cfg->qos_present) && (p_ccb->our_cfg.qos_present) )
+ p_ccb->our_cfg.qos = p_cfg->qos;
+
+ if (p_cfg->fcr_present)
+ {
+ /* Save the retransmission and monitor timeout values */
+ if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ p_ccb->peer_cfg.fcr.rtrans_tout = p_cfg->fcr.rtrans_tout;
+ p_ccb->peer_cfg.fcr.mon_tout = p_cfg->fcr.mon_tout;
+ }
+
+ /* Calculate the max number of packets for which we can delay sending an ack */
+ if (p_cfg->fcr.tx_win_sz < p_ccb->our_cfg.fcr.tx_win_sz)
+ p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3;
+ else
+ p_ccb->fcrb.max_held_acks = p_ccb->our_cfg.fcr.tx_win_sz / 3;
+
+ L2CAP_TRACE_DEBUG3 ("l2cu_process_peer_cfg_rsp(): peer tx_win_sz: %d, our tx_win_sz: %d, max_held_acks: %d",
+ p_cfg->fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz, p_ccb->fcrb.max_held_acks);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_process_our_cfg_req
+**
+** Description This function is called when we send a "config request"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 hci_flush_to;
+
+ /* Save the QOS settings we are using for transmit */
+ if (p_cfg->qos_present)
+ {
+ p_ccb->our_cfg.qos_present = TRUE;
+ p_ccb->our_cfg.qos = p_cfg->qos;
+ }
+
+ if (p_cfg->fcr_present)
+ {
+ /* Override FCR options if attempting streaming or basic */
+ if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE)
+ memset(&p_cfg->fcr, 0, sizeof(tL2CAP_FCR_OPTS));
+ else
+ {
+ /* On BR/EDR, timer values are zero in config request */
+ /* On class 2 AMP, timer value in config request shall be non-0 processing time */
+ /* timer value in config response shall be greater than received processing time */
+ p_cfg->fcr.mon_tout = p_cfg->fcr.rtrans_tout = 0;
+
+ if (p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE)
+ p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0;
+ }
+
+ /* Set the threshold to send acks (may be updated in the cfg response) */
+ p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3;
+
+ /* Include FCS option only if peer can handle it */
+ if (p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_NO_CRC)
+ {
+ /* FCS check can be bypassed if peer also desires to bypass */
+ if (p_cfg->fcs_present && p_cfg->fcs == L2CAP_CFG_FCS_BYPASS)
+ p_ccb->bypass_fcs |= L2CAP_CFG_FCS_OUR;
+ }
+ else
+ p_cfg->fcs_present = FALSE;
+ }
+ else
+ {
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ }
+
+ p_ccb->our_cfg.fcr.mode = p_cfg->fcr.mode;
+ p_ccb->our_cfg.fcr_present = p_cfg->fcr_present;
+
+ /* Check the flush timeout. If it is lower than the current one used */
+ /* then we need to adjust the flush timeout sent to the controller */
+ if (p_cfg->flush_to_present)
+ {
+ if ((p_cfg->flush_to == 0)||(p_cfg->flush_to == L2CAP_NO_AUTOMATIC_FLUSH))
+ {
+ /* don't send invalid flush timeout */
+ /* SPEC: The sender of the Request shall specify its flush timeout value */
+ /* if it differs from the default value of 0xFFFF */
+ p_cfg->flush_to_present = FALSE;
+ }
+ else
+ {
+ p_ccb->our_cfg.flush_to = p_cfg->flush_to;
+ p_lcb = p_ccb->p_lcb;
+
+ if (p_cfg->flush_to < p_lcb->link_flush_tout)
+ {
+ p_lcb->link_flush_tout = p_cfg->flush_to;
+
+ /* If the timeout is within range of HCI, set the flush timeout */
+ if (p_cfg->flush_to <= ((HCI_MAX_AUTO_FLUSH_TOUT * 5) / 8))
+ {
+ /* Convert flush timeout to 0.625 ms units, with round */
+ hci_flush_to = ((p_cfg->flush_to * 8) + 3) / 5;
+ btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to);
+ }
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_our_cfg_rsp
+**
+** Description This function is called when we send the peer a "config response"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* If peer wants QoS, we are allowed to change the values in a positive response */
+ if ( (p_cfg->qos_present) && (p_ccb->peer_cfg.qos_present) )
+ p_ccb->peer_cfg.qos = p_cfg->qos;
+ else
+ p_cfg->qos_present = FALSE;
+
+ l2c_fcr_adj_our_rsp_options (p_ccb, p_cfg);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_device_reset
+**
+** Description This function is called when reset of the device is
+** completed. For all active connection simulate HCI_DISC
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_device_reset (void)
+{
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->handle != HCI_INVALID_HANDLE))
+ {
+ l2c_link_hci_disc_comp (p_lcb->handle, (UINT8) -1);
+ }
+ }
+}
+
+#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
+extern UINT16 tcs_wug_get_clk_offset( BD_ADDR addr ) ;
+#endif
+
+/*******************************************************************************
+**
+** Function l2cu_create_conn
+**
+** Description This function initiates an acl connection via HCI
+**
+** Returns TRUE if successful, FALSE if gki get buffer fails.
+**
+*******************************************************************************/
+BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb)
+{
+ int xx;
+ tL2C_LCB *p_lcb_cur = &l2cb.lcb_pool[0];
+#if BTM_SCO_INCLUDED == TRUE
+ BOOLEAN is_sco_active;
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type;
+
+ BTM_ReadDevInfo(p_lcb->remote_bd_addr, &dev_type, &addr_type);
+
+ if (dev_type == BT_DEVICE_TYPE_BLE)
+ {
+ p_lcb->ble_addr_type = addr_type;
+ p_lcb->is_ble_link = TRUE;
+
+ return (l2cble_create_conn(p_lcb));
+ }
+#endif
+
+ /* If there is a connection where we perform as a slave, try to switch roles
+ for this connection */
+ for (xx = 0, p_lcb_cur = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb_cur++)
+ {
+ if (p_lcb_cur == p_lcb)
+ continue;
+
+ if ((p_lcb_cur->in_use) && (p_lcb_cur->link_role == HCI_ROLE_SLAVE))
+ {
+
+#if BTM_SCO_INCLUDED == TRUE
+ /* The LMP_switch_req shall be sent only if the ACL logical transport
+ is in active mode, when encryption is disabled, and all synchronous
+ logical transports on the same physical link are disabled." */
+
+ /* Check if there is any SCO Active on this BD Address */
+ is_sco_active = btm_is_sco_active_by_bdaddr(p_lcb_cur->remote_bd_addr);
+
+ L2CAP_TRACE_API1 ("l2cu_create_conn - btm_is_sco_active_by_bdaddr() is_sco_active = %s", \
+ (is_sco_active == TRUE) ? "TRUE":"FALSE");
+
+ if (is_sco_active == TRUE)
+ continue; /* No Master Slave switch not allowed when SCO Active */
+#endif
+
+ if (HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features))
+ {
+ /* mark this lcb waiting for switch to be completed and
+ start switch on the other one */
+ p_lcb->link_state = LST_CONNECTING_WAIT_SWITCH;
+ p_lcb->link_role = HCI_ROLE_MASTER;
+
+ if (BTM_SwitchRole (p_lcb_cur->remote_bd_addr, HCI_ROLE_MASTER, NULL) == BTM_CMD_STARTED)
+ {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_ROLE_SWITCH_TOUT);
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ p_lcb->link_state = LST_CONNECTING;
+
+ return (l2cu_create_conn_after_switch (p_lcb));
+}
+
+/*******************************************************************************
+**
+** Function l2cu_get_num_hi_priority
+**
+** Description Gets the number of high priority channels.
+**
+** Returns
+**
+*******************************************************************************/
+UINT8 l2cu_get_num_hi_priority (void)
+{
+ UINT8 no_hi = 0;
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH))
+ {
+ no_hi++;
+ }
+ }
+ return no_hi;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_create_conn_after_switch
+**
+** Description This function initiates an acl connection via HCI
+** If switch required to create connection it is already done.
+**
+** Returns TRUE if successful, FALSE if gki get buffer fails.
+**
+*******************************************************************************/
+
+BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb)
+{
+ UINT8 allow_switch = HCI_CR_CONN_ALLOW_SWITCH;
+ tBTM_INQ_INFO *p_inq_info;
+ UINT8 page_scan_rep_mode;
+ UINT8 page_scan_mode;
+ UINT16 clock_offset;
+ UINT8 *p_features;
+ UINT16 num_acl = BTM_GetNumAclLinks();
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_lcb->remote_bd_addr);
+ UINT8 no_hi_prio_chs = l2cu_get_num_hi_priority();
+
+ p_features = BTM_ReadLocalFeatures();
+
+ L2CAP_TRACE_DEBUG4 ("l2cu_create_conn_after_switch :%d num_acl:%d no_hi: %d is_bonding:%d",
+ l2cb.disallow_switch, num_acl, no_hi_prio_chs, p_lcb->is_bonding);
+ /* FW team says that we can participant in 4 piconets
+ * typically 3 piconet + 1 for scanning.
+ * We can enhance the code to count the number of piconets later. */
+ if ( ((!l2cb.disallow_switch && (num_acl < 3)) || (p_lcb->is_bonding && (no_hi_prio_chs==0)))
+ && HCI_SWITCH_SUPPORTED(p_features))
+ allow_switch = HCI_CR_CONN_ALLOW_SWITCH;
+ else
+ allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH;
+
+ p_lcb->link_state = LST_CONNECTING;
+
+
+#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
+ if ( (clock_offset = tcs_wug_get_clk_offset( p_lcb->remote_bd_addr )) != 0 )
+ {
+ page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R0;
+ page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE;
+ }
+ else
+ {
+#endif
+
+ /* Check with the BT manager if details about remote device are known */
+ if ((p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr)) != NULL)
+ {
+ page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode;
+ page_scan_mode = p_inq_info->results.page_scan_mode;
+ clock_offset = (UINT16)(p_inq_info->results.clock_offset);
+ }
+ else
+ {
+ /* No info known. Use default settings */
+ page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R1;
+ page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE;
+
+ clock_offset = (p_dev_rec) ? p_dev_rec->clock_offset : 0;
+ }
+#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE)
+ }
+#endif
+
+ if (!btsnd_hcic_create_conn (p_lcb->remote_bd_addr,
+ HCI_PKT_TYPES_MASK_DM1 + HCI_PKT_TYPES_MASK_DH1,
+ page_scan_rep_mode,
+ page_scan_mode,
+ clock_offset,
+ allow_switch))
+
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - no buffer for l2cu_create_conn");
+ l2cu_release_lcb (p_lcb);
+ return (FALSE);
+ }
+
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ btm_acl_update_busy_level (BTM_BLI_PAGE_EVT);
+#endif
+
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK,
+ L2CAP_LINK_CONNECT_TOUT);
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_state
+**
+** Description Look through all active LCBs for a match based on the
+** LCB state.
+**
+** Returns pointer to first matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state)
+{
+ UINT16 i;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (i = 0; i < MAX_L2CAP_LINKS; i++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->link_state == state))
+ {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_lcb_disconnecting
+**
+** Description On each active lcb, check if the lcb is in disconnecting
+** state, or if there are no ccb's on the lcb (implying
+ idle timeout is running), or if last ccb on the link
+ is in disconnecting state.
+**
+** Returns TRUE if any of above conditions met, FALSE otherwise
+**
+*******************************************************************************/
+BOOLEAN l2cu_lcb_disconnecting (void)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ UINT16 i;
+ BOOLEAN status = FALSE;
+
+ p_lcb = &l2cb.lcb_pool[0];
+
+ for (i = 0; i < MAX_L2CAP_LINKS; i++, p_lcb++)
+ {
+ if (p_lcb->in_use)
+ {
+ /* no ccbs on lcb, or lcb is in disconnecting state */
+ if ((!p_lcb->ccb_queue.p_first_ccb) || (p_lcb->link_state == LST_DISCONNECTING))
+ {
+ status = TRUE;
+ break;
+ }
+ /* only one ccb left on lcb */
+ else if (p_lcb->ccb_queue.p_first_ccb == p_lcb->ccb_queue.p_last_ccb)
+ {
+ p_ccb = p_lcb->ccb_queue.p_first_ccb;
+
+ if ((p_ccb->in_use) &&
+ ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) ||
+ (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP)))
+ {
+ status = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_set_acl_priority
+**
+** Description Sets the transmission priority for a channel.
+** (For initial implementation only two values are valid.
+** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH).
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs)
+{
+ tL2C_LCB *p_lcb;
+#ifdef BRCM_VS
+ UINT8 *pp;
+ UINT8 command[HCI_BRCM_ACL_PRIORITY_PARAM_SIZE];
+ UINT8 vs_param;
+#endif
+
+ /* Find the link control block for the acl channel */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr)) == NULL)
+ {
+ L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_SetAclPriority");
+ return (FALSE);
+ }
+
+#ifdef BRCM_VS
+ if (BTM_IS_BRCM_CONTROLLER())
+ {
+ /* Called from above L2CAP through API; send VSC if changed */
+ if ((!reset_after_rs && (priority != p_lcb->acl_priority)) ||
+
+ /* Called because of a master/slave role switch; if high resend VSC */
+ ( reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH))
+ {
+ pp = command;
+
+ vs_param = (priority == L2CAP_PRIORITY_HIGH) ? HCI_BRCM_ACL_PRIORITY_HIGH : HCI_BRCM_ACL_PRIORITY_LOW;
+
+ UINT16_TO_STREAM (pp, p_lcb->handle);
+ UINT8_TO_STREAM (pp, vs_param);
+
+ BTM_VendorSpecificCommand (HCI_BRCM_SET_ACL_PRIORITY, HCI_BRCM_ACL_PRIORITY_PARAM_SIZE, command, NULL);
+
+ /* Adjust lmp buffer allocation for this channel if priority changed */
+ if (p_lcb->acl_priority != priority)
+ {
+ p_lcb->acl_priority = priority;
+ l2c_link_adjust_allocation();
+ }
+ }
+ }
+#endif
+ return(TRUE);
+}
+
+#if (L2CAP_ENHANCED_FEATURES != 0)
+/*******************************************************************************
+**
+** Function l2cu_send_feature_req
+**
+** Description Called at connection establishment by the originator
+** of the connection to send an L2CAP Echo request message
+** to the peer to determine if he supports Widcomm proprietary
+** features.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_feature_req (tL2C_CCB *p_ccb)
+{
+ UINT8 saved_id;
+ UINT8 buff[100], *p = buff;
+
+ UINT8_TO_STREAM (p, 'R');
+ UINT8_TO_STREAM (p, 'Q');
+
+ UINT8_TO_STREAM (p, 'r');
+ UINT8_TO_STREAM (p, 'q');
+
+ /* save current ID to be restored after feature request */
+ saved_id = p_ccb->p_lcb->id;
+
+ /* Set appropriate ID */
+ p_ccb->p_lcb->id = L2CAP_FEATURE_REQ_ID - 1;
+
+ l2cu_send_peer_echo_req (p_ccb->p_lcb, buff, (UINT16)(p - buff));
+
+ /* packet has been built so we can restore the control block id */
+ p_ccb->p_lcb->id = saved_id;
+}
+
+
+
+/*******************************************************************************
+**
+** Function l2cu_check_feature_req
+**
+** Description Called when an echo request is received to check if the
+** other side is doing a proprietary feature request. If so,
+** extract the values and reply with a features response.
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len)
+{
+ UINT8 buff[100];
+ UINT8 *p_out = buff;
+ UINT8 *p_end = p_data + data_len - 2;
+ UINT8 pe_type, pe_len;
+
+ if ((data_len <= 4)
+ || (p_data[0] != 'R')
+ || (p_data[1] != 'Q')
+ || (p_data[data_len - 2] != 'r')
+ || (p_data[data_len - 1] != 'q')
+ || (id != L2CAP_FEATURE_REQ_ID))
+ return (FALSE);
+
+ /* Skip leading frame characters */
+ p_data += 2;
+
+ UINT8_TO_STREAM (p_out, 'R');
+ UINT8_TO_STREAM (p_out, 'S');
+
+ while (p_data < p_end)
+ {
+ pe_type = *p_data++;
+ pe_len = *p_data++;
+
+ switch (pe_type)
+ {
+ default:
+ p_data += pe_len;
+ break;
+ }
+ }
+
+ /* Sanity check - we should not overrun the input */
+ if (p_data != p_end)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - badly formatted feature req");
+ return (FALSE);
+ }
+
+ UINT8_TO_STREAM (p_out, 'r');
+ UINT8_TO_STREAM (p_out, 's');
+
+ l2cu_send_peer_echo_rsp (p_lcb, L2CAP_FEATURE_RSP_ID, buff, (UINT16)(p_out - buff));
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_check_feature_rsp
+**
+** Description Called when an echo response is received to check if the
+** other side is suports proprietary feature(s). If so,
+** extract the values.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len)
+{
+ UINT8 *p_end = p_data + data_len - 2;
+
+ if ((data_len <= 4)
+ || (p_data[0] != 'R')
+ || (p_data[1] != 'S')
+ || (p_data[data_len - 2] != 'r')
+ || (p_data[data_len - 1] != 's')
+ || (id != L2CAP_FEATURE_RSP_ID))
+ {
+ return;
+ }
+
+ /* Skip leading frame characters */
+ p_data += 2;
+
+ while (p_data < p_end)
+ {
+ UINT8 pe_id = *p_data++;
+ UINT8 pe_len = *p_data++;
+
+ switch (pe_id)
+ {
+ default:
+ p_data += pe_len;
+ break;
+ }
+ }
+
+ /* Sanity check - we should not overrun the input */
+ if (p_data != p_end)
+ {
+ L2CAP_TRACE_ERROR0 ("L2CAP - badly formatted feature rsp");
+ }
+}
+#endif /* L2CAP_ENHANCED_FEATURES != 0 */
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+/******************************************************************************
+**
+** Function l2cu_set_non_flushable_pbf
+**
+** Description set L2CAP_PKT_START_NON_FLUSHABLE if controller supoorts
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_set_non_flushable_pbf (BOOLEAN is_supported)
+{
+ if (is_supported)
+ l2cb.non_flushable_pbf = (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT);
+ else
+ l2cb.non_flushable_pbf = (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function l2cu_resubmit_pending_sec_req
+**
+** Description This function is called when required security procedures
+** are completed and any pending requests can be re-submitted.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_next_ccb;
+ int xx;
+
+ L2CAP_TRACE_DEBUG1 ("l2cu_resubmit_pending_sec_req p_bda: 0x%08x", p_bda);
+
+ /* If we are called with a BDA, only resubmit for that BDA */
+ if (p_bda)
+ {
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda);
+
+ /* If we don't have one, this is an error */
+ if (p_lcb)
+ {
+ /* For all channels, send the event through their FSMs */
+ 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;
+ l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING0 ("l2cu_resubmit_pending_sec_req - unknown BD_ADDR");
+ }
+ }
+ else
+ {
+ /* No BDA pasesed in, so check all links */
+ for (xx = 0, p_lcb = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if (p_lcb->in_use)
+ {
+ /* For all channels, send the event through their FSMs */
+ 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;
+ l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL);
+ }
+ }
+ }
+ }
+}
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+/*******************************************************************************
+**
+** Function l2cu_set_info_rsp_mask
+**
+** Description This function allows the script wrapper to change the
+** info resp mask for conformance testing.
+**
+** Returns pointer to CCB, or NULL if none
+**
+*******************************************************************************/
+void l2cu_set_info_rsp_mask (UINT32 mask)
+{
+ l2cb.test_info_resp = mask;
+}
+#endif /* L2CAP_CONFORMANCE_TESTING */
+
+/*******************************************************************************
+**
+** Function l2cu_adjust_out_mps
+**
+** Description Sets our MPS based on current controller capabilities
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_adjust_out_mps (tL2C_CCB *p_ccb)
+{
+ UINT16 packet_size;
+
+ /* on the tx side MTU is selected based on packet size of the controller */
+ packet_size = btm_get_max_packet_size (p_ccb->p_lcb->remote_bd_addr);
+
+ if (packet_size <= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN))
+ {
+ /* something is very wrong */
+ L2CAP_TRACE_ERROR2 ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps);
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps;
+ }
+ else
+ {
+ packet_size -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN);
+
+ /* We try to negotiate MTU that each packet can be split into whole
+ number of max packets. For example if link is 1.2 max packet size is 339 bytes.
+ At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead.
+ 1695, that will be 5 Dh5 packets. Now maximum L2CAP packet is
+ 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691.
+
+ For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet
+ 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. */
+ if (p_ccb->peer_cfg.fcr.mps >= packet_size)
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps / packet_size * packet_size;
+ else
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps;
+
+ L2CAP_TRACE_DEBUG3 ("l2cu_adjust_out_mps use %d Based on peer_cfg.fcr.mps: %u packet_size: %u",
+ p_ccb->tx_mps, p_ccb->peer_cfg.fcr.mps, packet_size);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_initialize_fixed_ccb
+**
+** Description Initialize a fixed channel's CCB
+**
+** Returns TRUE or FALSE
+**
+*******************************************************************************/
+BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr)
+{
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2C_CCB *p_ccb;
+
+ /* If we already have a CCB, then simply return */
+ if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL)
+ return (TRUE);
+
+ if ((p_ccb = l2cu_allocate_ccb (NULL, 0)) == NULL)
+ return (FALSE);
+
+ /* Set CID for the connection */
+ p_ccb->local_cid = fixed_cid;
+ p_ccb->remote_cid = fixed_cid;
+
+ GKI_init_q (&p_ccb->xmit_hold_q);
+
+ p_ccb->is_flushable = FALSE;
+
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
+
+
+ if (p_fcr)
+ {
+ /* Set the FCR parameters. For now, we will use default pools */
+ p_ccb->our_cfg.fcr = p_ccb->peer_cfg.fcr = *p_fcr;
+
+ p_ccb->ertm_info.fcr_rx_pool_id = HCI_ACL_POOL_ID;
+ p_ccb->ertm_info.fcr_tx_pool_id = HCI_ACL_POOL_ID;
+ p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID;
+ p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID;
+
+ p_ccb->fcrb.max_held_acks = p_fcr->tx_win_sz / 3;
+ }
+
+ /* Link ccb to lcb and lcb to ccb */
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = p_ccb;
+ p_ccb->p_lcb = p_lcb;
+
+ /* There is no configuration, so if the link is up, the channel is up */
+ if (p_lcb->link_state == LST_CONNECTED)
+ p_ccb->chnl_state = CST_OPEN;
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].default_idle_tout;
+#endif
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_no_dynamic_ccbs
+**
+** Description Handles the case when there are no more dynamic CCBs. If there
+** are any fixed CCBs, start the longest of the fixed CCB timeouts,
+** otherwise start the default link idle timeout or disconnect.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb)
+{
+ tBTM_STATUS rc;
+ UINT16 timeout = p_lcb->idle_timeout;
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ {
+ if ( (p_lcb->p_fixed_ccbs[xx] != NULL) && (p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout > timeout) )
+ timeout = p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout;
+ }
+#endif
+
+ /* If the link is pairing, do not mess with the timeouts */
+ if (p_lcb->is_bonding)
+ return;
+
+ if (timeout == 0)
+ {
+ L2CAP_TRACE_DEBUG0 ("l2cu_no_dynamic_ccbs() IDLE timer 0, disconnecting link");
+
+ rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);
+ if (rc == BTM_CMD_STARTED)
+ {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ }
+ else if (rc == BTM_SUCCESS)
+ {
+ /* BTM SEC will make sure that link is release (probably after pairing is done) */
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = 0xFFFF;
+ }
+ else if ( (p_lcb->is_bonding)
+ && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) )
+ {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ }
+ else
+ {
+ /* probably no buffer to send disconnect */
+ timeout = BT_1SEC_TIMEOUT;
+ }
+ }
+
+ if (timeout != 0xFFFF)
+ {
+ L2CAP_TRACE_DEBUG1 ("l2cu_no_dynamic_ccbs() starting IDLE timeout: %d", timeout);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+ }
+ else
+ {
+ btu_stop_timer(&p_lcb->timer_entry);
+ }
+}
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+/*******************************************************************************
+**
+** Function l2cu_process_fixed_chnl_resp
+**
+** Description handle a fixed channel response (or lack thereof)
+** if the link failed, or a fixed channel response was
+** not received, the bitfield is all zeros.
+**
+*******************************************************************************/
+void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb)
+{
+ int xx;
+#if BLE_INCLUDED == TRUE
+ UINT16 reason = (p_lcb->is_ble_link ) ? 1 : 0;
+#else
+ UINT16 reason =0;
+#endif
+
+ /* Tell all registered fixed channels about the connection */
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ {
+ if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL)
+ {
+ if (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
+ {
+ if (p_lcb->p_fixed_ccbs[xx])
+ p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN;
+
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, reason);
+ }
+ else if (p_lcb->p_fixed_ccbs[xx])
+ {
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason);
+ l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ }
+ }
+ }
+}
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_par_req
+**
+** Description Build and send a BLE parameter update request message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_lcb->id++;
+ l2cu_adj_id (p_lcb, L2CAP_ADJ_ID);
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_REQ_LEN, L2CAP_CMD_BLE_UPDATE_REQ, p_lcb->id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("l2cu_send_peer_ble_par_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, min_int);
+ UINT16_TO_STREAM (p, max_int);
+ UINT16_TO_STREAM (p, latency);
+ UINT16_TO_STREAM (p, timeout);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_par_rsp
+**
+** Description Build and send a BLE parameter update response message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_RSP_LEN, L2CAP_CMD_BLE_UPDATE_RSP, rem_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING0 ("l2cu_send_peer_ble_par_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, reason);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+#endif /* BLE_INCLUDED == TRUE */
+
+
+/*******************************************************************************
+** Functions used by both Full and Light Stack
+********************************************************************************/
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_handle
+**
+** Description Look through all active LCBs for a match based on the
+** HCI handle.
+**
+** Returns pointer to matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle)
+{
+ int xx;
+ tL2C_LCB *p_lcb = &l2cb.lcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++)
+ {
+ if ((p_lcb->in_use) && (p_lcb->handle == handle))
+ {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_find_ccb_by_cid
+**
+** Description Look through all active CCBs on a link for a match based
+** on the local CID. If passed the link pointer is NULL, all
+** active links are searched.
+**
+** Returns pointer to matched CCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid)
+{
+ tL2C_CCB *p_ccb = NULL;
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ UINT8 xx;
+#endif
+
+ if (local_cid >= L2CAP_BASE_APPL_CID)
+ {
+ /* find the associated CCB by "index" */
+ local_cid -= L2CAP_BASE_APPL_CID;
+
+ if (local_cid >= MAX_L2CAP_CHANNELS)
+ return NULL;
+
+ p_ccb = l2cb.ccb_pool + local_cid;
+
+ /* make sure the CCB is in use */
+ if (!p_ccb->in_use)
+ {
+ p_ccb = NULL;
+ }
+ /* make sure it's for the same LCB */
+ else if (p_lcb && p_lcb != p_ccb->p_lcb)
+ {
+ p_ccb = NULL;
+ }
+ }
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else
+ {
+ /* searching fixed channel */
+ p_ccb = l2cb.ccb_pool;
+ for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ )
+ {
+ if ((p_ccb->local_cid == local_cid)
+ &&(p_ccb->in_use)
+ &&(p_lcb == p_ccb->p_lcb))
+ break;
+ else
+ p_ccb++;
+ }
+ if ( xx >= MAX_L2CAP_CHANNELS )
+ return NULL;
+ }
+#endif
+
+ return (p_ccb);
+}
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+
+/******************************************************************************
+**
+** Function l2cu_get_next_channel_in_rr
+**
+** Description get the next channel to send on a link. It also adjusts the
+** CCB queue to do a basic priority and round-robin scheduling.
+**
+** Returns pointer to CCB or NULL
+**
+*******************************************************************************/
+static tL2C_CCB *l2cu_get_next_channel_in_rr(tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_serve_ccb = NULL;
+ tL2C_CCB *p_ccb;
+
+ int i, j;
+
+ /* scan all of priority until finding a channel to serve */
+ for ( i = 0; (i < L2CAP_NUM_CHNL_PRIORITY)&&(!p_serve_ccb); i++ )
+ {
+ /* scan all channel within serving priority group until finding a channel to serve */
+ for ( j = 0; (j < p_lcb->rr_serv[p_lcb->rr_pri].num_ccb)&&(!p_serve_ccb); j++)
+ {
+ /* scaning from next serving channel */
+ p_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb;
+
+ if (!p_ccb)
+ {
+ L2CAP_TRACE_ERROR1("p_serve_ccb is NULL, rr_pri=%d", p_lcb->rr_pri);
+ return NULL;
+ }
+
+ L2CAP_TRACE_DEBUG3("RR scan pri=%d, lcid=0x%04x, q_cout=%d",
+ p_ccb->ccb_priority, p_ccb->local_cid, p_ccb->xmit_hold_q.count );
+
+ /* store the next serving channel */
+ /* this channel is the last channel of its priority group */
+ if (( p_ccb->p_next_ccb == NULL )
+ ||( p_ccb->p_next_ccb->ccb_priority != p_ccb->ccb_priority ))
+ {
+ /* next serving channel is set to the first channel in the group */
+ p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_first_ccb;
+ }
+ else
+ {
+ /* next serving channel is set to the next channel in the group */
+ p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_ccb->p_next_ccb;
+ }
+
+ if (p_ccb->chnl_state != CST_OPEN)
+ continue;
+
+ /* eL2CAP option in use */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy)
+ continue;
+
+ if ( p_ccb->fcrb.retrans_q.count == 0 )
+ {
+ if ( p_ccb->xmit_hold_q.count == 0 )
+ continue;
+
+ /* If using the common pool, should be at least 10% free. */
+ if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) )
+ continue;
+
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) )
+ continue;
+ }
+ }
+ else
+ {
+ if (p_ccb->xmit_hold_q.count == 0)
+ continue;
+ }
+
+ /* found a channel to serve */
+ p_serve_ccb = p_ccb;
+ /* decrease quota of its priority group */
+ p_lcb->rr_serv[p_lcb->rr_pri].quota--;
+ }
+
+ /* if there is no more quota of the priority group or no channel to have data to send */
+ if ((p_lcb->rr_serv[p_lcb->rr_pri].quota == 0)||(!p_serve_ccb))
+ {
+ /* serve next priority group */
+ p_lcb->rr_pri = (p_lcb->rr_pri + 1) % L2CAP_NUM_CHNL_PRIORITY;
+ /* initialize its quota */
+ p_lcb->rr_serv[p_lcb->rr_pri].quota = L2CAP_GET_PRIORITY_QUOTA(p_lcb->rr_pri);
+ }
+ }
+
+ if (p_serve_ccb)
+ {
+ L2CAP_TRACE_DEBUG3("RR service pri=%d, quota=%d, lcid=0x%04x",
+ p_serve_ccb->ccb_priority,
+ p_lcb->rr_serv[p_serve_ccb->ccb_priority].quota,
+ p_serve_ccb->local_cid );
+ }
+
+ return p_serve_ccb;
+}
+
+#else /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+/******************************************************************************
+**
+** Function l2cu_get_next_channel
+**
+** Description get the next channel to send on a link bassed on priority
+** scheduling.
+**
+** Returns pointer to CCB or NULL
+**
+*******************************************************************************/
+static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+
+ /* Get the first CCB with data to send.
+ */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ {
+ if (p_ccb->chnl_state != CST_OPEN)
+ continue;
+
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy)
+ continue;
+
+ if (p_ccb->fcrb.retrans_q.count != 0)
+ return p_ccb;
+
+ if (p_ccb->xmit_hold_q.count == 0)
+ continue;
+
+ /* If using the common pool, should be at least 10% free. */
+ if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) )
+ continue;
+
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) )
+ continue;
+
+ /* If here, we found someone */
+ return p_ccb;
+ }
+
+ return NULL;
+}
+#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+/******************************************************************************
+**
+** Function l2cu_get_next_buffer_to_send
+**
+** Description get the next buffer to send on a link. It also adjusts the
+** CCB queue to do a basic priority and round-robin scheduling.
+**
+** Returns pointer to buffer or NULL
+**
+*******************************************************************************/
+BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+ BT_HDR *p_buf;
+
+ /* Highest priority are fixed channels */
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ {
+ if ((p_ccb = p_lcb->p_fixed_ccbs[xx]) == NULL)
+ continue;
+
+ /* eL2CAP option in use */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy)
+ continue;
+
+ /* No more checks needed if sending from the reatransmit queue */
+ if (p_ccb->fcrb.retrans_q.count == 0)
+ {
+ if (p_ccb->xmit_hold_q.count == 0)
+ continue;
+
+ /* If using the common pool, should be at least 10% free. */
+ if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) )
+ continue;
+
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) )
+ continue;
+ }
+
+ if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) != NULL)
+ {
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+ return (p_buf);
+ }
+ }
+ else
+ {
+ if (p_ccb->xmit_hold_q.count != 0)
+ {
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+ return (p_buf);
+ }
+ }
+ }
+#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 );
+#else
+ p_ccb = l2cu_get_next_channel( p_lcb );
+#endif
+
+ /* Return if no buffer */
+ if (p_ccb == NULL)
+ return (NULL);
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) == NULL)
+ return (NULL);
+ }
+ else
+ {
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q);
+ }
+
+ if ( p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_TxComplete_Cb && (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) )
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, 1);
+
+
+ l2cu_check_channel_congestion (p_ccb);
+
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+
+ return (p_buf);
+}
+
+/******************************************************************************
+**
+** Function l2cu_set_acl_hci_header
+**
+** Description Set HCI handle for ACL packet
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb)
+{
+ UINT8 *p;
+
+ /* Set the pointer to the beginning of the data minus 4 bytes for the packet header */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset - HCI_DATA_PREAMBLE_SIZE;
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ if ( (((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_CH_BASED) && (p_ccb->is_flushable))
+ || ((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_PKT) )
+ {
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT));
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | l2cb.non_flushable_pbf);
+ }
+#else
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT));
+#endif
+#if (BLE_INCLUDED == TRUE)
+ if (p_ccb->p_lcb->is_ble_link)
+ {
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > btu_cb.hcit_ble_acl_data_size)
+ {
+ UINT16_TO_STREAM (p, btu_cb.hcit_ble_acl_data_size);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+ } /* (BLE_INCLUDED == TRUE) */
+ else
+#endif
+ {
+
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > btu_cb.hcit_acl_data_size)
+ {
+ UINT16_TO_STREAM (p, btu_cb.hcit_acl_data_size);
+ }
+ else
+ {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+ }
+ p_buf->offset -= HCI_DATA_PREAMBLE_SIZE;
+ p_buf->len += HCI_DATA_PREAMBLE_SIZE;
+}
+
+/******************************************************************************
+**
+** Function l2cu_check_channel_congestion
+**
+** Description check if any change in congestion status
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
+{
+ UINT16 q_count = p_ccb->xmit_hold_q.count;
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ q_count += p_ccb->p_lcb->ucd_out_sec_pending_q.count;
+ }
+#endif
+
+ /* If the CCB queue limit is subject to a quota, check for congestion */
+
+ /* if this channel has outgoing traffic */
+ if ((p_ccb->p_rcb)&&(p_ccb->buff_quota != 0))
+ {
+ /* If this channel was congested */
+ if ( p_ccb->cong_sent )
+ {
+ /* If the channel is not congested now, tell the app */
+ if (q_count <= (p_ccb->buff_quota / 2))
+ {
+ p_ccb->cong_sent = FALSE;
+ if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
+ {
+ L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x xmit_hold_q.count: %u buff_quota: %u",
+ p_ccb->local_cid, q_count, p_ccb->buff_quota);
+
+ /* Prevent recursive calling */
+ l2cb.is_cong_cback_context = TRUE;
+ (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE);
+ l2cb.is_cong_cback_context = FALSE;
+ }
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
+ {
+ L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
+ p_ccb->p_lcb->ucd_out_sec_pending_q.count,
+ p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE );
+ }
+ }
+#endif
+ }
+ }
+ else
+ {
+ /* If this channel was not congested but it is congested now, tell the app */
+ if (q_count > p_ccb->buff_quota)
+ {
+ p_ccb->cong_sent = TRUE;
+ if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)
+ {
+ L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u",
+ p_ccb->local_cid, q_count, p_ccb->buff_quota);
+
+ (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE);
+ }
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )
+ {
+ if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb )
+ {
+ L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
+ p_ccb->p_lcb->ucd_out_sec_pending_q.count,
+ p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE );
+ }
+ }
+#endif
+ }
+ }
+ }
+}
+