summaryrefslogtreecommitdiffstats
path: root/stack/avct
diff options
context:
space:
mode:
Diffstat (limited to 'stack/avct')
-rw-r--r--stack/avct/avct_api.c465
-rw-r--r--stack/avct/avct_ccb.c150
-rw-r--r--stack/avct/avct_defs.h62
-rw-r--r--stack/avct/avct_int.h239
-rw-r--r--stack/avct/avct_l2c.c434
-rw-r--r--stack/avct/avct_lcb.c471
-rw-r--r--stack/avct/avct_lcb_act.c702
7 files changed, 2523 insertions, 0 deletions
diff --git a/stack/avct/avct_api.c b/stack/avct/avct_api.c
new file mode 100644
index 0000000..7e37a90
--- /dev/null
+++ b/stack/avct/avct_api.c
@@ -0,0 +1,465 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains API of the audio/video control transport protocol.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "gki.h"
+#include "l2c_api.h"
+#include "l2cdefs.h"
+#include "btm_api.h"
+#include "avct_api.h"
+#include "avct_int.h"
+
+/* Control block for AVCT */
+#if AVCT_DYNAMIC_MEMORY == FALSE
+tAVCT_CB avct_cb;
+#endif
+
+/*******************************************************************************
+**
+** Function AVCT_Register
+**
+** Description This is the system level registration function for the
+** AVCTP protocol. This function initializes AVCTP and
+** prepares the protocol stack for its use. This function
+** must be called once by the system or platform using AVCTP
+** before the other functions of the API an be used.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask)
+{
+ AVCT_TRACE_API0("AVCT_Register");
+
+ /* register PSM with L2CAP */
+ L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_appl);
+
+ /* set security level */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0);
+
+ /* initialize AVCTP data structures */
+ memset(&avct_cb, 0, sizeof(tAVCT_CB));
+
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ /* Include the browsing channel which uses eFCR */
+ L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_br_appl);
+
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0);
+
+ if (mtu_br < AVCT_MIN_BROWSE_MTU)
+ mtu_br = AVCT_MIN_BROWSE_MTU;
+ avct_cb.mtu_br = mtu_br;
+#endif
+
+#if defined(AVCT_INITIAL_TRACE_LEVEL)
+ avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL;
+#else
+ avct_cb.trace_level = BT_TRACE_LEVEL_NONE;
+#endif
+
+ if (mtu < AVCT_MIN_CONTROL_MTU)
+ mtu = AVCT_MIN_CONTROL_MTU;
+ /* store mtu */
+ avct_cb.mtu = mtu;
+}
+
+/*******************************************************************************
+**
+** Function AVCT_Deregister
+**
+** Description This function is called to deregister use AVCTP protocol.
+** It is called when AVCTP is no longer being used by any
+** application in the system. Before this function can be
+** called, all connections must be removed with
+** AVCT_RemoveConn().
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void AVCT_Deregister(void)
+{
+ AVCT_TRACE_API0("AVCT_Deregister");
+
+ /* deregister PSM with L2CAP */
+ L2CA_Deregister(AVCT_PSM);
+}
+
+/*******************************************************************************
+**
+** Function AVCT_CreateConn
+**
+** Description Create an AVCTP connection. There are two types of
+** connections, initiator and acceptor, as determined by
+** the p_cc->role parameter. When this function is called to
+** create an initiator connection, an AVCTP connection to
+** the peer device is initiated if one does not already exist.
+** If an acceptor connection is created, the connection waits
+** passively for an incoming AVCTP connection from a peer device.
+**
+**
+** Returns AVCT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, BD_ADDR peer_addr)
+{
+ UINT16 result = AVCT_SUCCESS;
+ tAVCT_CCB *p_ccb;
+ tAVCT_LCB *p_lcb;
+
+ AVCT_TRACE_API2("AVCT_CreateConn: %d, control:%d", p_cc->role, p_cc->control);
+
+ /* Allocate ccb; if no ccbs, return failure */
+ if ((p_ccb = avct_ccb_alloc(p_cc)) == NULL)
+ {
+ result = AVCT_NO_RESOURCES;
+ }
+ else
+ {
+ /* get handle */
+ *p_handle = avct_ccb_to_idx(p_ccb);
+
+ /* if initiator connection */
+ if (p_cc->role == AVCT_INT)
+ {
+ /* find link; if none allocate a new one */
+ if ((p_lcb = avct_lcb_by_bd(peer_addr)) == NULL)
+ {
+ if ((p_lcb = avct_lcb_alloc(peer_addr)) == NULL)
+ {
+ /* no link resources; free ccb as well */
+ avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL);
+ result = AVCT_NO_RESOURCES;
+ }
+ }
+ /* check if PID already in use */
+ else if (avct_lcb_has_pid(p_lcb, p_cc->pid))
+ {
+ avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL);
+ result = AVCT_PID_IN_USE;
+ }
+
+ if (result == AVCT_SUCCESS)
+ {
+ /* bind lcb to ccb */
+ p_ccb->p_lcb = p_lcb;
+ AVCT_TRACE_DEBUG1("ch_state: %d", p_lcb->ch_state);
+ avct_lcb_event(p_lcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb);
+ }
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVCT_RemoveConn
+**
+** Description Remove an AVCTP connection. This function is called when
+** the application is no longer using a connection. If this
+** is the last connection to a peer the L2CAP channel for AVCTP
+** will be closed.
+**
+**
+** Returns AVCT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVCT_RemoveConn(UINT8 handle)
+{
+ UINT16 result = AVCT_SUCCESS;
+ tAVCT_CCB *p_ccb;
+
+ AVCT_TRACE_API0("AVCT_RemoveConn");
+
+ /* map handle to ccb */
+ if ((p_ccb = avct_ccb_by_idx(handle)) == NULL)
+ {
+ result = AVCT_BAD_HANDLE;
+ }
+ /* if connection not bound to lcb, dealloc */
+ else if (p_ccb->p_lcb == NULL)
+ {
+ avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL);
+ }
+ /* send unbind event to lcb */
+ else
+ {
+ avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVCT_CreateBrowse
+**
+** Description Create an AVCTP Browse channel. There are two types of
+** connections, initiator and acceptor, as determined by
+** the role parameter. When this function is called to
+** create an initiator connection, the Browse channel to
+** the peer device is initiated if one does not already exist.
+** If an acceptor connection is created, the connection waits
+** passively for an incoming AVCTP connection from a peer device.
+**
+**
+** Returns AVCT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVCT_CreateBrowse (UINT8 handle, UINT8 role)
+{
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ UINT16 result = AVCT_SUCCESS;
+ tAVCT_CCB *p_ccb;
+ tAVCT_BCB *p_bcb;
+ int index;
+
+ AVCT_TRACE_API1("AVCT_CreateBrowse: %d", role);
+
+ /* map handle to ccb */
+ if ((p_ccb = avct_ccb_by_idx(handle)) == NULL)
+ {
+ return AVCT_BAD_HANDLE;
+ }
+ else
+ {
+ /* mark this CCB as supporting browsing channel */
+ if ((p_ccb->allocated & AVCT_ALOC_BCB) == 0)
+ {
+ p_ccb->allocated |= AVCT_ALOC_BCB;
+ }
+ }
+
+ /* if initiator connection */
+ if (role == AVCT_INT)
+ {
+ /* the link control block must exist before this function is called as INT. */
+ if (p_ccb->p_lcb == NULL)
+ {
+ result = AVCT_NOT_OPEN;
+ }
+ else
+ {
+ /* find link; if none allocate a new one */
+ index = p_ccb->p_lcb->allocated;
+ if (index > AVCT_NUM_LINKS)
+ {
+ result = AVCT_BAD_HANDLE;
+ }
+ else
+ {
+ p_bcb = &avct_cb.bcb[index - 1];
+ p_bcb->allocated = index;
+ }
+ }
+
+ if (result == AVCT_SUCCESS)
+ {
+ /* bind bcb to ccb */
+ p_ccb->p_bcb = p_bcb;
+ AVCT_TRACE_DEBUG1("ch_state: %d", p_bcb->ch_state);
+ avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb);
+ }
+ }
+
+ return result;
+#else
+ return AVCT_NO_RESOURCES;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function AVCT_RemoveBrowse
+**
+** Description Remove an AVCTP Browse channel. This function is called when
+** the application is no longer using a connection. If this
+** is the last connection to a peer the L2CAP channel for AVCTP
+** will be closed.
+**
+**
+** Returns AVCT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVCT_RemoveBrowse (UINT8 handle)
+{
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ UINT16 result = AVCT_SUCCESS;
+ tAVCT_CCB *p_ccb;
+
+ AVCT_TRACE_API0("AVCT_RemoveBrowse");
+
+ /* map handle to ccb */
+ if ((p_ccb = avct_ccb_by_idx(handle)) == NULL)
+ {
+ result = AVCT_BAD_HANDLE;
+ }
+ else if (p_ccb->p_bcb != NULL)
+ /* send unbind event to bcb */
+ {
+ avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb);
+ }
+ return result;
+#else
+ return AVCT_NO_RESOURCES;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function AVCT_GetBrowseMtu
+**
+** Description Get the peer_mtu for the AVCTP Browse channel of the given
+** connection.
+**
+** Returns the peer browsing channel MTU.
+**
+*******************************************************************************/
+UINT16 AVCT_GetBrowseMtu (UINT8 handle)
+{
+ UINT16 peer_mtu = AVCT_MIN_BROWSE_MTU;
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ tAVCT_CCB *p_ccb;
+
+ if ((p_ccb = avct_ccb_by_idx(handle)) != NULL && p_ccb->p_bcb != NULL)
+ {
+ peer_mtu = p_ccb->p_bcb->peer_mtu;
+ }
+#endif
+ return peer_mtu;
+}
+
+/*******************************************************************************
+**
+** Function AVCT_GetPeerMtu
+**
+** Description Get the peer_mtu for the AVCTP channel of the given
+** connection.
+**
+** Returns the peer MTU size.
+**
+*******************************************************************************/
+UINT16 AVCT_GetPeerMtu (UINT8 handle)
+{
+ UINT16 peer_mtu = L2CAP_DEFAULT_MTU;
+ tAVCT_CCB *p_ccb;
+
+ /* map handle to ccb */
+ if ((p_ccb = avct_ccb_by_idx(handle)) != NULL)
+ {
+ if (p_ccb->p_lcb)
+ {
+ peer_mtu = p_ccb->p_lcb->peer_mtu;
+ }
+ }
+
+ return peer_mtu;
+}
+
+/*******************************************************************************
+**
+** Function AVCT_MsgReq
+**
+** Description Send an AVCTP message to a peer device. In calling
+** AVCT_MsgReq(), the application should keep track of the
+** congestion state of AVCTP as communicated with events
+** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the
+** application calls AVCT_MsgReq() when AVCTP is congested
+** the message may be discarded. The application may make its
+** first call to AVCT_MsgReq() after it receives an
+** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or
+** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel.
+**
+** p_msg->layer_specific must be set to
+** AVCT_DATA_CTRL for control channel traffic;
+** AVCT_DATA_BROWSE for for browse channel traffic.
+**
+** Returns AVCT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg)
+{
+ UINT16 result = AVCT_SUCCESS;
+ tAVCT_CCB *p_ccb;
+ tAVCT_UL_MSG ul_msg;
+
+ AVCT_TRACE_API0("AVCT_MsgReq");
+
+ /* verify p_msg parameter */
+ if (p_msg == NULL)
+ {
+ return AVCT_NO_RESOURCES;
+ }
+ AVCT_TRACE_API1("len: %d", p_msg->len);
+
+ /* map handle to ccb */
+ if ((p_ccb = avct_ccb_by_idx(handle)) == NULL)
+ {
+ result = AVCT_BAD_HANDLE;
+ GKI_freebuf(p_msg);
+ }
+ /* verify channel is bound to link */
+ else if (p_ccb->p_lcb == NULL)
+ {
+ result = AVCT_NOT_OPEN;
+ GKI_freebuf(p_msg);
+ }
+
+ if (result == AVCT_SUCCESS)
+ {
+ ul_msg.p_buf = p_msg;
+ ul_msg.p_ccb = p_ccb;
+ ul_msg.label = label;
+ ul_msg.cr = cr;
+
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ /* send msg event to bcb */
+ if (p_msg->layer_specific == AVCT_DATA_BROWSE)
+ {
+ if (p_ccb->p_bcb == NULL && (p_ccb->allocated & AVCT_ALOC_BCB) == 0)
+ {
+ /* BCB channel is not open and not allocated */
+ result = AVCT_BAD_HANDLE;
+ GKI_freebuf(p_msg);
+ }
+ else
+ {
+ p_ccb->p_bcb = avct_bcb_by_lcb(p_ccb->p_lcb);
+ avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg);
+ }
+ }
+ /* send msg event to lcb */
+ else
+#endif
+ {
+ avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg);
+ }
+ }
+ return result;
+}
+
diff --git a/stack/avct/avct_ccb.c b/stack/avct/avct_ccb.c
new file mode 100644
index 0000000..09051b1
--- /dev/null
+++ b/stack/avct/avct_ccb.c
@@ -0,0 +1,150 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains functions which operate on the AVCTP connection
+ * control block.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "avct_api.h"
+#include "avct_int.h"
+
+/*******************************************************************************
+**
+** Function avct_ccb_alloc
+**
+** Description Allocate a connection control block; copy parameters to ccb.
+**
+**
+** Returns pointer to the ccb, or NULL if none could be allocated.
+**
+*******************************************************************************/
+tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (!p_ccb->allocated)
+ {
+ p_ccb->allocated = AVCT_ALOC_LCB;
+ memcpy(&p_ccb->cc, p_cc, sizeof(tAVCT_CC));
+ AVCT_TRACE_DEBUG1("avct_ccb_alloc %d", i);
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_CONN)
+ {
+ /* out of ccbs */
+ p_ccb = NULL;
+ AVCT_TRACE_WARNING0("Out of ccbs");
+ }
+ return p_ccb;
+}
+
+/*******************************************************************************
+**
+** Function avct_ccb_dealloc
+**
+** Description Deallocate a connection control block and call application
+** callback.
+**
+**
+** Returns void.
+**
+*******************************************************************************/
+void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr)
+{
+ tAVCT_CTRL_CBACK *p_cback = p_ccb->cc.p_ctrl_cback;
+
+ AVCT_TRACE_DEBUG1("avct_ccb_dealloc %d", avct_ccb_to_idx(p_ccb));
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ if(p_ccb->p_bcb == NULL)
+ memset(p_ccb, 0, sizeof(tAVCT_CCB));
+ else
+ {
+ /* control channel is down, but the browsing channel is still connected 0 disconnect it now */
+ avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb);
+ p_ccb->p_lcb = NULL;
+ }
+#else
+ memset(p_ccb, 0, sizeof(tAVCT_CCB));
+#endif
+
+ if (event != AVCT_NO_EVT)
+ {
+ (*p_cback)(avct_ccb_to_idx(p_ccb), event, result, bd_addr);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_ccb_to_idx
+**
+** Description Given a pointer to an ccb, return its index.
+**
+**
+** Returns Index of ccb.
+**
+*******************************************************************************/
+UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb)
+{
+ /* use array arithmetic to determine index */
+ return (UINT8) (p_ccb - avct_cb.ccb);
+}
+
+/*******************************************************************************
+**
+** Function avct_ccb_by_idx
+**
+** Description Return ccb pointer based on ccb index (or handle).
+**
+**
+** Returns pointer to the ccb, or NULL if none found.
+**
+*******************************************************************************/
+tAVCT_CCB *avct_ccb_by_idx(UINT8 idx)
+{
+ tAVCT_CCB *p_ccb;
+
+ /* verify index */
+ if (idx < AVCT_NUM_CONN)
+ {
+ p_ccb = &avct_cb.ccb[idx];
+
+ /* verify ccb is allocated */
+ if (!p_ccb->allocated)
+ {
+ p_ccb = NULL;
+ AVCT_TRACE_WARNING1("ccb %d not allocated", idx);
+ }
+ }
+ else
+ {
+ p_ccb = NULL;
+ AVCT_TRACE_WARNING1("No ccb for idx %d", idx);
+ }
+ return p_ccb;
+}
diff --git a/stack/avct/avct_defs.h b/stack/avct/avct_defs.h
new file mode 100644
index 0000000..30b8859
--- /dev/null
+++ b/stack/avct/avct_defs.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This contains constants definitions and other information from the AVCTP
+ * specification. This file is intended for use internal to AVCT only.
+ *
+ ******************************************************************************/
+#ifndef AVCT_DEFS_H
+#define AVCT_DEFS_H
+
+/*****************************************************************************
+** constants
+*****************************************************************************/
+
+/* packet type */
+#define AVCT_PKT_TYPE_SINGLE 0 /* single packet */
+#define AVCT_PKT_TYPE_START 1 /* start packet */
+#define AVCT_PKT_TYPE_CONT 2 /* continue packet */
+#define AVCT_PKT_TYPE_END 3 /* end packet */
+
+/* header lengths for different packet types */
+#define AVCT_HDR_LEN_SINGLE 3
+#define AVCT_HDR_LEN_START 4
+#define AVCT_HDR_LEN_CONT 1
+#define AVCT_HDR_LEN_END 1
+
+/* invalid cr+ipid value */
+#define AVCT_CR_IPID_INVALID 1
+
+/*****************************************************************************
+** message parsing and building macros
+*****************************************************************************/
+
+#define AVCT_BLD_HDR(p, label, type, cr_ipid) \
+ *(p)++ = ((label) << 4) | ((type) << 2) | (cr_ipid);
+
+#define AVCT_PRS_HDR(p, label, type, cr_ipid) \
+ label = *(p) >> 4; \
+ type = (*(p) >> 2) & 3; \
+ cr_ipid = *(p)++ & 3;
+
+#define AVCT_PRS_PKT_TYPE(p, type) \
+ type = (*(p) >> 2) & 3;
+
+#endif /* AVCT_DEFS_H */
diff --git a/stack/avct/avct_int.h b/stack/avct/avct_int.h
new file mode 100644
index 0000000..b93c032
--- /dev/null
+++ b/stack/avct/avct_int.h
@@ -0,0 +1,239 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains interfaces which are internal to AVCTP.
+ *
+ ******************************************************************************/
+#ifndef AVCT_INT_H
+#define AVCT_INT_H
+
+#include "gki.h"
+#include "avct_api.h"
+#include "avct_defs.h"
+#include "l2c_api.h"
+
+/*****************************************************************************
+** constants
+*****************************************************************************/
+
+/* lcb state machine events */
+enum {
+ AVCT_LCB_UL_BIND_EVT,
+ AVCT_LCB_UL_UNBIND_EVT,
+ AVCT_LCB_UL_MSG_EVT,
+ AVCT_LCB_INT_CLOSE_EVT,
+ AVCT_LCB_LL_OPEN_EVT,
+ AVCT_LCB_LL_CLOSE_EVT,
+ AVCT_LCB_LL_MSG_EVT,
+ AVCT_LCB_LL_CONG_EVT
+};
+
+
+/* "states" used for L2CAP channel */
+#define AVCT_CH_IDLE 0 /* No connection */
+#define AVCT_CH_CONN 1 /* Waiting for connection confirm */
+#define AVCT_CH_CFG 2 /* Waiting for configuration complete */
+#define AVCT_CH_OPEN 3 /* Channel opened */
+
+/* "no event" indicator used by ccb dealloc */
+#define AVCT_NO_EVT 0xFF
+
+/*****************************************************************************
+** data types
+*****************************************************************************/
+/* sub control block type - common data members for tAVCT_LCB and tAVCT_BCB */
+typedef struct {
+ UINT16 peer_mtu; /* peer l2c mtu */
+ UINT16 ch_result; /* L2CAP connection result value */
+ UINT16 ch_lcid; /* L2CAP channel LCID */
+ UINT8 allocated; /* 0, not allocated. index+1, otherwise. */
+ UINT8 state; /* The state machine state */
+ UINT8 ch_state; /* L2CAP channel state */
+ UINT8 ch_flags; /* L2CAP configuration flags */
+} tAVCT_SCB;
+
+/* link control block type */
+typedef struct {
+ UINT16 peer_mtu; /* peer l2c mtu */
+ UINT16 ch_result; /* L2CAP connection result value */
+ UINT16 ch_lcid; /* L2CAP channel LCID */
+ UINT8 allocated; /* 0, not allocated. index+1, otherwise. */
+ UINT8 state; /* The state machine state */
+ UINT8 ch_state; /* L2CAP channel state */
+ UINT8 ch_flags; /* L2CAP configuration flags */
+ BT_HDR *p_rx_msg; /* Message being reassembled */
+ UINT16 conflict_lcid; /* L2CAP channel LCID */
+ BD_ADDR peer_addr; /* BD address of peer */
+ BUFFER_Q tx_q; /* Transmit data buffer queue */
+ BOOLEAN cong; /* TRUE, if congested */
+} tAVCT_LCB;
+
+/* browse control block type */
+typedef struct {
+ UINT16 peer_mtu; /* peer l2c mtu */
+ UINT16 ch_result; /* L2CAP connection result value */
+ UINT16 ch_lcid; /* L2CAP channel LCID */
+ UINT8 allocated; /* 0, not allocated. index+1, otherwise. */
+ UINT8 state; /* The state machine state */
+ UINT8 ch_state; /* L2CAP channel state */
+ UINT8 ch_flags; /* L2CAP configuration flags */
+ BT_HDR *p_tx_msg; /* Message to be sent - in case the browsing channel is not open when MsgReg is called */
+ UINT8 ch_close; /* CCB index+1, if CCB initiated channel close */
+} tAVCT_BCB;
+
+#define AVCT_ALOC_LCB 0x01
+#define AVCT_ALOC_BCB 0x02
+/* connection control block */
+typedef struct {
+ tAVCT_CC cc; /* parameters from connection creation */
+ tAVCT_LCB *p_lcb; /* Associated LCB */
+ tAVCT_BCB *p_bcb; /* associated BCB */
+ BOOLEAN ch_close; /* Whether CCB initiated channel close */
+ UINT8 allocated; /* Whether LCB/BCB is allocated */
+} tAVCT_CCB;
+
+/* data type associated with UL_MSG_EVT */
+typedef struct {
+ BT_HDR *p_buf;
+ tAVCT_CCB *p_ccb;
+ UINT8 label;
+ UINT8 cr;
+} tAVCT_UL_MSG;
+
+/* union associated with lcb state machine events */
+typedef union {
+ tAVCT_UL_MSG ul_msg;
+ BT_HDR *p_buf;
+ tAVCT_CCB *p_ccb;
+ UINT16 result;
+ BOOLEAN cong;
+ UINT8 err_code;
+} tAVCT_LCB_EVT;
+
+/* Control block for AVCT */
+typedef struct {
+ tAVCT_LCB lcb[AVCT_NUM_LINKS]; /* link control blocks */
+ tAVCT_BCB bcb[AVCT_NUM_LINKS]; /* browse control blocks */
+ tAVCT_CCB ccb[AVCT_NUM_CONN]; /* connection control blocks */
+ UINT16 mtu; /* our L2CAP MTU */
+ UINT16 mtu_br; /* our L2CAP MTU for the Browsing channel */
+ UINT8 trace_level; /* trace level */
+} tAVCT_CB;
+
+/*****************************************************************************
+** function declarations
+*****************************************************************************/
+
+/* LCB function declarations */
+extern void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data);
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+extern void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data);
+extern void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb);
+extern tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb);
+extern BOOLEAN avct_bcb_last_ccb(tAVCT_BCB *p_bcb, tAVCT_CCB *p_ccb_last);
+extern tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid);
+#endif
+extern tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr);
+extern tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr);
+extern void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid);
+extern tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid);
+extern BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last);
+
+/* LCB action functions */
+extern void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+extern void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data);
+
+/* BCB action functions */
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+typedef void (*tAVCT_BCB_ACTION)(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+extern void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+
+extern void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data);
+
+extern const tAVCT_BCB_ACTION avct_bcb_action[];
+extern const UINT8 avct_lcb_pkt_type_len[];
+extern const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def;
+#endif
+
+/* CCB function declarations */
+extern tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc);
+extern void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr);
+extern UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb);
+extern tAVCT_CCB *avct_ccb_by_idx(UINT8 idx);
+
+
+/*****************************************************************************
+** global data
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Main control block */
+#if AVCT_DYNAMIC_MEMORY == FALSE
+AVCT_API extern tAVCT_CB avct_cb;
+#else
+AVCT_API extern tAVCT_CB *avct_cb_ptr;
+#define avct_cb (*avct_cb_ptr)
+#endif
+
+/* L2CAP callback registration structure */
+extern const tL2CAP_APPL_INFO avct_l2c_appl;
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+extern const tL2CAP_APPL_INFO avct_l2c_br_appl;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVCT_INT_H */
+
+
diff --git a/stack/avct/avct_l2c.c b/stack/avct/avct_l2c.c
new file mode 100644
index 0000000..ef0f5fc
--- /dev/null
+++ b/stack/avct/avct_l2c.c
@@ -0,0 +1,434 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This AVCTP module interfaces to L2CAP
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "avct_api.h"
+#include "avct_int.h"
+#include "l2c_api.h"
+#include "l2cdefs.h"
+
+/* Configuration flags. */
+#define AVCT_L2C_CFG_IND_DONE (1<<0)
+#define AVCT_L2C_CFG_CFM_DONE (1<<1)
+
+/* callback function declarations */
+void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
+void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result);
+void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
+void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
+void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed);
+void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result);
+void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested);
+void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf);
+
+/* L2CAP callback function structure */
+const tL2CAP_APPL_INFO avct_l2c_appl = {
+ avct_l2c_connect_ind_cback,
+ avct_l2c_connect_cfm_cback,
+ NULL,
+ avct_l2c_config_ind_cback,
+ avct_l2c_config_cfm_cback,
+ avct_l2c_disconnect_ind_cback,
+ avct_l2c_disconnect_cfm_cback,
+ NULL,
+ avct_l2c_data_ind_cback,
+ avct_l2c_congestion_ind_cback,
+ NULL /* tL2CA_TX_COMPLETE_CB */
+};
+
+/*******************************************************************************
+**
+** Function avct_l2c_is_passive
+**
+** Description check is the CCB associated with the given LCB was created
+** as passive
+**
+** Returns TRUE, if the given LCB is created as AVCT_PASSIVE
+**
+*******************************************************************************/
+static BOOLEAN avct_l2c_is_passive (tAVCT_LCB *p_lcb)
+{
+ BOOLEAN is_passive = FALSE;
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb))
+ {
+ AVCT_TRACE_DEBUG1("avct_l2c_is_ct control:x%x", p_ccb->cc.control);
+ if (p_ccb->cc.control & AVCT_PASSIVE)
+ {
+ is_passive = TRUE;
+ break;
+ }
+ }
+ }
+ return is_passive;
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_connect_ind_cback
+**
+** Description This is the L2CAP connect indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
+{
+ tAVCT_LCB *p_lcb;
+ UINT16 result = L2CAP_CONN_OK;
+ tL2CAP_CFG_INFO cfg;
+
+ /* do we already have a channel for this peer? */
+ if ((p_lcb = avct_lcb_by_bd(bd_addr)) == NULL)
+ {
+ /* no, allocate lcb */
+ if ((p_lcb = avct_lcb_alloc(bd_addr)) == NULL)
+ {
+ /* no ccb available, reject L2CAP connection */
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+ }
+ /* else we already have a channel for this peer */
+ else
+ {
+ if (!avct_l2c_is_passive (p_lcb) || (p_lcb->ch_state == AVCT_CH_OPEN))
+ {
+ /* this LCB included CT role - reject */
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+ else
+ {
+ /* TG role only - accept the connection from CT. move the channel ID to the conflict list */
+ p_lcb->conflict_lcid = p_lcb->ch_lcid;
+ AVCT_TRACE_DEBUG1("avct_l2c_connect_ind_cback conflict_lcid:0x%x", p_lcb->conflict_lcid);
+ }
+ }
+
+ if(p_lcb)
+ {
+ AVCT_TRACE_DEBUG3("avct_l2c_connect_ind_cback: 0x%x, res: %d, ch_state: %d",
+ lcid, result, p_lcb->ch_state);
+ }
+ /* Send L2CAP connect rsp */
+ L2CA_ConnectRsp(bd_addr, id, lcid, result, 0);
+
+ /* if result ok, proceed with connection */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* store LCID */
+ p_lcb->ch_lcid = lcid;
+
+ /* transition to configuration state */
+ p_lcb->ch_state = AVCT_CH_CFG;
+
+ /* Send L2CAP config req */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ cfg.mtu_present = TRUE;
+ cfg.mtu = avct_cb.mtu;
+ L2CA_ConfigReq(lcid, &cfg);
+ AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req");
+ }
+
+#if (BT_USE_TRACES == TRUE)
+ if(p_lcb)
+ AVCT_TRACE_DEBUG1("ch_state cni: %d ", p_lcb->ch_state);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_connect_cfm_cback
+**
+** Description This is the L2CAP connect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result)
+{
+ tAVCT_LCB *p_lcb;
+ tL2CAP_CFG_INFO cfg;
+
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ AVCT_TRACE_DEBUG4("avct_l2c_connect_cfm_cback lcid:0x%x result: %d ch_state: %d, conflict_lcid:0x%x",
+ lcid, result, p_lcb->ch_state, p_lcb->conflict_lcid);
+ /* if in correct state */
+ if (p_lcb->ch_state == AVCT_CH_CONN)
+ {
+ /* if result successful */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* set channel state */
+ p_lcb->ch_state = AVCT_CH_CFG;
+
+ /* Send L2CAP config req */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ cfg.mtu_present = TRUE;
+ cfg.mtu = avct_cb.mtu;
+ L2CA_ConfigReq(lcid, &cfg);
+ AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req");
+ }
+ /* else failure */
+ else
+ {
+ AVCT_TRACE_DEBUG1("avct_l2c_connect_cfm_cback conflict_lcid:0x%x", p_lcb->conflict_lcid);
+ if (p_lcb->conflict_lcid == lcid)
+ p_lcb->conflict_lcid = 0;
+ else
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result);
+ }
+ }
+ else if (p_lcb->conflict_lcid == lcid)
+ {
+ /* we must be in AVCT_CH_CFG state for the ch_lcid channel */
+ AVCT_TRACE_DEBUG2("avct_l2c_connect_cfm_cback ch_state: %d, conflict_lcid:0x%x", p_lcb->ch_state, p_lcb->conflict_lcid);
+ if (result == L2CAP_CONN_OK)
+ {
+ /* just in case the peer also accepts our connection - Send L2CAP disconnect req */
+ L2CA_DisconnectReq(lcid);
+ }
+ p_lcb->conflict_lcid = 0;
+ }
+ AVCT_TRACE_DEBUG1("ch_state cnc: %d ", p_lcb->ch_state);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_config_cfm_cback
+**
+** Description This is the L2CAP config confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tAVCT_LCB *p_lcb;
+
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ AVCT_TRACE_DEBUG3("avct_l2c_config_cfm_cback: 0x%x, ch_state: %d, res: %d",
+ lcid, p_lcb->ch_state, p_cfg->result);
+ /* if in correct state */
+ if (p_lcb->ch_state == AVCT_CH_CFG)
+ {
+ /* if result successful */
+ if (p_cfg->result == L2CAP_CFG_OK)
+ {
+ /* update flags */
+ p_lcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE;
+
+ /* if configuration complete */
+ if (p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE)
+ {
+ p_lcb->ch_state = AVCT_CH_OPEN;
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL);
+ }
+ }
+ /* else failure */
+ else
+ {
+ AVCT_TRACE_DEBUG1("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ", p_lcb->ch_state);
+ /* store result value */
+ p_lcb->ch_result = p_cfg->result;
+
+ /* Send L2CAP disconnect req */
+ L2CA_DisconnectReq(lcid);
+ }
+ }
+ AVCT_TRACE_DEBUG1("ch_state cfc: %d ", p_lcb->ch_state);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_config_ind_cback
+**
+** Description This is the L2CAP config indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tAVCT_LCB *p_lcb;
+
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ AVCT_TRACE_DEBUG2("avct_l2c_config_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state);
+ /* store the mtu in tbl */
+ if (p_cfg->mtu_present)
+ {
+ p_lcb->peer_mtu = p_cfg->mtu;
+ }
+ else
+ {
+ p_lcb->peer_mtu = L2CAP_DEFAULT_MTU;
+ }
+
+ /* send L2CAP configure response */
+ memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ p_cfg->result = L2CAP_CFG_OK;
+ L2CA_ConfigRsp(lcid, p_cfg);
+
+ /* if first config ind */
+ if ((p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0)
+ {
+ /* update flags */
+ p_lcb->ch_flags |= AVCT_L2C_CFG_IND_DONE;
+
+ /* if configuration complete */
+ if (p_lcb->ch_flags & AVCT_L2C_CFG_CFM_DONE)
+ {
+ p_lcb->ch_state = AVCT_CH_OPEN;
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL);
+ }
+ }
+ AVCT_TRACE_DEBUG1("ch_state cfi: %d ", p_lcb->ch_state);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_disconnect_ind_cback
+**
+** Description This is the L2CAP disconnect indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed)
+{
+ tAVCT_LCB *p_lcb;
+ UINT16 result = AVCT_RESULT_FAIL;
+
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ AVCT_TRACE_DEBUG2("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state);
+ if (ack_needed)
+ {
+ /* send L2CAP disconnect response */
+ L2CA_DisconnectRsp(lcid);
+ }
+
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result);
+ AVCT_TRACE_DEBUG1("ch_state di: %d ", p_lcb->ch_state);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_disconnect_cfm_cback
+**
+** Description This is the L2CAP disconnect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result)
+{
+ tAVCT_LCB *p_lcb;
+ UINT16 res;
+
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ AVCT_TRACE_DEBUG3("avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d",
+ lcid, p_lcb->ch_state, result);
+ /* result value may be previously stored */
+ res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result;
+ p_lcb->ch_result = 0;
+
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &res);
+ AVCT_TRACE_DEBUG1("ch_state dc: %d ", p_lcb->ch_state);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_congestion_ind_cback
+**
+** Description This is the L2CAP congestion indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested)
+{
+ tAVCT_LCB *p_lcb;
+
+ AVCT_TRACE_DEBUG1("avct_l2c_congestion_ind_cback: 0x%x", lcid);
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_l2c_data_ind_cback
+**
+** Description This is the L2CAP data indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf)
+{
+ tAVCT_LCB *p_lcb;
+
+ AVCT_TRACE_DEBUG1("avct_l2c_data_ind_cback: 0x%x", lcid);
+ /* look up lcb for this channel */
+ if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL)
+ {
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf);
+ }
+ else /* prevent buffer leak */
+ {
+ AVCT_TRACE_WARNING0("ERROR -> avct_l2c_data_ind_cback drop buffer");
+ GKI_freebuf(p_buf);
+ }
+}
+
diff --git a/stack/avct/avct_lcb.c b/stack/avct/avct_lcb.c
new file mode 100644
index 0000000..bb3f447
--- /dev/null
+++ b/stack/avct/avct_lcb.c
@@ -0,0 +1,471 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains the link control state machine and functions which
+ * operate on the link control block.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "avct_api.h"
+#include "avct_int.h"
+#include "gki.h"
+
+/*****************************************************************************
+** state machine constants and types
+*****************************************************************************/
+
+#if BT_TRACE_VERBOSE == TRUE
+
+/* verbose state strings for trace */
+const char * const avct_lcb_st_str[] = {
+ "LCB_IDLE_ST",
+ "LCB_OPENING_ST",
+ "LCB_OPEN_ST",
+ "LCB_CLOSING_ST"
+};
+
+/* verbose event strings for trace */
+const char * const avct_lcb_evt_str[] = {
+ "UL_BIND_EVT",
+ "UL_UNBIND_EVT",
+ "UL_MSG_EVT",
+ "INT_CLOSE_EVT",
+ "LL_OPEN_EVT",
+ "LL_CLOSE_EVT",
+ "LL_MSG_EVT",
+ "LL_CONG_EVT"
+};
+
+#endif
+
+/* lcb state machine states */
+enum {
+ AVCT_LCB_IDLE_ST,
+ AVCT_LCB_OPENING_ST,
+ AVCT_LCB_OPEN_ST,
+ AVCT_LCB_CLOSING_ST
+};
+
+/* state machine action enumeration list */
+enum {
+ AVCT_LCB_CHNL_OPEN,
+ AVCT_LCB_CHNL_DISC,
+ AVCT_LCB_SEND_MSG,
+ AVCT_LCB_OPEN_IND,
+ AVCT_LCB_OPEN_FAIL,
+ AVCT_LCB_CLOSE_IND,
+ AVCT_LCB_CLOSE_CFM,
+ AVCT_LCB_MSG_IND,
+ AVCT_LCB_CONG_IND,
+ AVCT_LCB_BIND_CONN,
+ AVCT_LCB_BIND_FAIL,
+ AVCT_LCB_UNBIND_DISC,
+ AVCT_LCB_CHK_DISC,
+ AVCT_LCB_DISCARD_MSG,
+ AVCT_LCB_DEALLOC,
+ AVCT_LCB_FREE_MSG_IND,
+ AVCT_LCB_NUM_ACTIONS
+};
+
+#define AVCT_LCB_IGNORE AVCT_LCB_NUM_ACTIONS
+
+/* type for action functions */
+typedef void (*tAVCT_LCB_ACTION)(tAVCT_LCB *p_ccb, tAVCT_LCB_EVT *p_data);
+
+/* action function list */
+const tAVCT_LCB_ACTION avct_lcb_action[] = {
+ avct_lcb_chnl_open,
+ avct_lcb_chnl_disc,
+ avct_lcb_send_msg,
+ avct_lcb_open_ind,
+ avct_lcb_open_fail,
+ avct_lcb_close_ind,
+ avct_lcb_close_cfm,
+ avct_lcb_msg_ind,
+ avct_lcb_cong_ind,
+ avct_lcb_bind_conn,
+ avct_lcb_bind_fail,
+ avct_lcb_unbind_disc,
+ avct_lcb_chk_disc,
+ avct_lcb_discard_msg,
+ avct_lcb_dealloc,
+ avct_lcb_free_msg_ind
+};
+
+/* state table information */
+#define AVCT_LCB_ACTIONS 2 /* number of actions */
+#define AVCT_LCB_NEXT_STATE 2 /* position of next state */
+#define AVCT_LCB_NUM_COLS 3 /* number of columns in state tables */
+
+/* state table for idle state */
+const UINT8 avct_lcb_st_idle[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_CHNL_OPEN, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}
+};
+
+/* state table for opening state */
+const UINT8 avct_lcb_st_opening[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_OPEN_FAIL, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}
+};
+
+/* state table for open state */
+const UINT8 avct_lcb_st_open[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_BIND_CONN, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_CHK_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_SEND_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}
+};
+
+/* state table for closing state */
+const UINT8 avct_lcb_st_closing[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_BIND_FAIL, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_CFM, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}
+};
+
+/* type for state table */
+typedef const UINT8 (*tAVCT_LCB_ST_TBL)[AVCT_LCB_NUM_COLS];
+
+/* state table */
+const tAVCT_LCB_ST_TBL avct_lcb_st_tbl[] = {
+ avct_lcb_st_idle,
+ avct_lcb_st_opening,
+ avct_lcb_st_open,
+ avct_lcb_st_closing
+};
+
+/*******************************************************************************
+**
+** Function avct_lcb_event
+**
+** Description State machine event handling function for lcb
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_LCB_ST_TBL state_table;
+ UINT8 action;
+ int i;
+
+#if BT_TRACE_VERBOSE == TRUE
+ AVCT_TRACE_EVENT3("LCB lcb=%d event=%s state=%s", p_lcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_lcb->state]);
+#else
+ AVCT_TRACE_EVENT3("LCB lcb=%d event=%d state=%d", p_lcb->allocated, event, p_lcb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = avct_lcb_st_tbl[p_lcb->state];
+
+ /* set next state */
+ p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE];
+
+ /* execute action functions */
+ for (i = 0; i < AVCT_LCB_ACTIONS; i++)
+ {
+ if ((action = state_table[event][i]) != AVCT_LCB_IGNORE)
+ {
+ (*avct_lcb_action[action])(p_lcb, p_data);
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_bcb_event
+**
+** Description State machine event handling function for lcb
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_LCB_ST_TBL state_table;
+ UINT8 action;
+ int i;
+
+#if BT_TRACE_VERBOSE == TRUE
+ AVCT_TRACE_EVENT3("BCB lcb=%d event=%s state=%s", p_bcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_bcb->state]);
+#else
+ AVCT_TRACE_EVENT3("BCB lcb=%d event=%d state=%d", p_bcb->allocated, event, p_bcb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = avct_lcb_st_tbl[p_bcb->state];
+
+ /* set next state */
+ p_bcb->state = state_table[event][AVCT_LCB_NEXT_STATE];
+
+ /* execute action functions */
+ for (i = 0; i < AVCT_LCB_ACTIONS; i++)
+ {
+ if ((action = state_table[event][i]) != AVCT_LCB_IGNORE)
+ {
+ (*avct_bcb_action[action])(p_bcb, p_data);
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function avct_lcb_by_bd
+**
+** Description This lookup function finds the lcb for a BD address.
+**
+**
+** Returns pointer to the lcb, or NULL if none found.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ /* if allocated lcb has matching lcb */
+ if (p_lcb->allocated && (!memcmp(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN)))
+ {
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* if no lcb found */
+ p_lcb = NULL;
+
+ AVCT_TRACE_DEBUG6("No lcb for addr %02x-%02x-%02x-%02x-%02x-%02x",
+ bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
+ }
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_alloc
+**
+** Description Allocate a link control block.
+**
+**
+** Returns pointer to the lcb, or NULL if none could be allocated.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ if (!p_lcb->allocated)
+ {
+ p_lcb->allocated = (UINT8)(i + 1);
+ memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN);
+ AVCT_TRACE_DEBUG1("avct_lcb_alloc %d", p_lcb->allocated);
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* out of lcbs */
+ p_lcb = NULL;
+ AVCT_TRACE_WARNING0("Out of lcbs");
+ }
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_dealloc
+**
+** Description Deallocate a link control block.
+**
+**
+** Returns void.
+**
+*******************************************************************************/
+void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ BOOLEAN found = FALSE;
+ int i;
+
+ AVCT_TRACE_DEBUG1("avct_lcb_dealloc %d", p_lcb->allocated);
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ /* if ccb allocated and */
+ if (p_ccb->allocated)
+ {
+ if (p_ccb->p_lcb == p_lcb)
+ {
+ AVCT_TRACE_DEBUG1("avct_lcb_dealloc used by ccb: %d", i);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ AVCT_TRACE_DEBUG0("avct_lcb_dealloc now");
+
+ /* clear reassembled msg buffer if in use */
+ if (p_lcb->p_rx_msg != NULL)
+ {
+ GKI_freebuf(p_lcb->p_rx_msg);
+ }
+ memset(p_lcb, 0, sizeof(tAVCT_LCB));
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_by_lcid
+**
+** Description Find the LCB associated with the L2CAP LCID
+**
+**
+** Returns pointer to the lcb, or NULL if none found.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ if (p_lcb->allocated && ((p_lcb->ch_lcid == lcid) || (p_lcb->conflict_lcid == lcid)))
+ {
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* out of lcbs */
+ p_lcb = NULL;
+ AVCT_TRACE_WARNING1("No lcb for lcid %x", lcid);
+ }
+
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_has_pid
+**
+** Description See if any ccbs on this lcb have a particular pid.
+**
+**
+** Returns Pointer to CCB if PID found, NULL otherwise.
+**
+*******************************************************************************/
+tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb->cc.pid == pid))
+ {
+ return p_ccb;
+ }
+ }
+ return NULL;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_last_ccb
+**
+** Description See if given ccb is only one on the lcb.
+**
+**
+** Returns TRUE if ccb is last, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ AVCT_TRACE_WARNING0("avct_lcb_last_ccb");
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ AVCT_TRACE_WARNING6("%x: aloc:%d, lcb:0x%x/0x%x, ccb:0x%x/0x%x",
+ i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+
diff --git a/stack/avct/avct_lcb_act.c b/stack/avct/avct_lcb_act.c
new file mode 100644
index 0000000..b883517
--- /dev/null
+++ b/stack/avct/avct_lcb_act.c
@@ -0,0 +1,702 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains action functions of the link control state machine.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "avct_api.h"
+#include "avct_int.h"
+#include "gki.h"
+#include "btm_api.h"
+
+/* packet header length lookup table */
+const UINT8 avct_lcb_pkt_type_len[] = {
+ AVCT_HDR_LEN_SINGLE,
+ AVCT_HDR_LEN_START,
+ AVCT_HDR_LEN_CONT,
+ AVCT_HDR_LEN_END
+};
+
+/*******************************************************************************
+**
+** Function avct_lcb_msg_asmbl
+**
+** Description Reassemble incoming message.
+**
+**
+** Returns Pointer to reassembled message; NULL if no message
+** available.
+**
+*******************************************************************************/
+static BT_HDR *avct_lcb_msg_asmbl(tAVCT_LCB *p_lcb, BT_HDR *p_buf)
+{
+ UINT8 *p;
+ UINT8 pkt_type;
+ BT_HDR *p_ret;
+ UINT16 buf_len;
+
+ /* parse the message header */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ AVCT_PRS_PKT_TYPE(p, pkt_type);
+
+ /* quick sanity check on length */
+ if (p_buf->len < avct_lcb_pkt_type_len[pkt_type])
+ {
+ GKI_freebuf(p_buf);
+ AVCT_TRACE_WARNING0("Bad length during reassembly");
+ p_ret = NULL;
+ }
+ /* single packet */
+ else if (pkt_type == AVCT_PKT_TYPE_SINGLE)
+ {
+ /* if reassembly in progress drop message and process new single */
+ if (p_lcb->p_rx_msg != NULL)
+ {
+ GKI_freebuf(p_lcb->p_rx_msg);
+ p_lcb->p_rx_msg = NULL;
+ AVCT_TRACE_WARNING0("Got single during reassembly");
+ }
+ p_ret = p_buf;
+ }
+ /* start packet */
+ else if (pkt_type == AVCT_PKT_TYPE_START)
+ {
+ /* if reassembly in progress drop message and process new start */
+ if (p_lcb->p_rx_msg != NULL)
+ {
+ GKI_freebuf(p_lcb->p_rx_msg);
+ AVCT_TRACE_WARNING0("Got start during reassembly");
+ }
+ p_lcb->p_rx_msg = p_buf;
+
+ /* copy first header byte over nosp */
+ *(p + 1) = *p;
+
+ /* set offset to point to where to copy next */
+ p_lcb->p_rx_msg->offset += p_lcb->p_rx_msg->len;
+
+ /* adjust length for packet header */
+ p_lcb->p_rx_msg->len -= 1;
+
+ p_ret = NULL;
+ }
+ /* continue or end */
+ else
+ {
+ /* if no reassembly in progress drop message */
+ if (p_lcb->p_rx_msg == NULL)
+ {
+ GKI_freebuf(p_buf);
+ AVCT_TRACE_WARNING1("Pkt type=%d out of order", pkt_type);
+ p_ret = NULL;
+ }
+ else
+ {
+ /* get size of buffer holding assembled message */
+ buf_len = GKI_get_buf_size(p_lcb->p_rx_msg) - sizeof(BT_HDR);
+
+ /* adjust offset and len of fragment for header byte */
+ p_buf->offset += AVCT_HDR_LEN_CONT;
+ p_buf->len -= AVCT_HDR_LEN_CONT;
+
+ /* verify length */
+ if ((p_lcb->p_rx_msg->offset + p_buf->len) > buf_len)
+ {
+ /* won't fit; free everything */
+ GKI_freebuf(p_lcb->p_rx_msg);
+ p_lcb->p_rx_msg = NULL;
+ GKI_freebuf(p_buf);
+ p_ret = NULL;
+ AVCT_TRACE_WARNING0("Fragmented message to big!");
+ }
+ else
+ {
+ /* copy contents of p_buf to p_rx_msg */
+ memcpy((UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset,
+ (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len);
+
+ if (pkt_type == AVCT_PKT_TYPE_END)
+ {
+ p_lcb->p_rx_msg->offset -= p_lcb->p_rx_msg->len;
+ p_lcb->p_rx_msg->len += p_buf->len;
+ p_ret = p_lcb->p_rx_msg;
+ p_lcb->p_rx_msg = NULL;
+ }
+ else
+ {
+ p_lcb->p_rx_msg->offset += p_buf->len;
+ p_lcb->p_rx_msg->len += p_buf->len;
+ p_ret = NULL;
+ }
+ GKI_freebuf(p_buf);
+ }
+ }
+ }
+ return p_ret;
+}
+
+
+/*******************************************************************************
+**
+** Function avct_lcb_chnl_open
+**
+** Description Open L2CAP channel to peer
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ UINT16 result = AVCT_RESULT_FAIL;
+
+ BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP, 0);
+ /* call l2cap connect req */
+ p_lcb->ch_state = AVCT_CH_CONN;
+ if ((p_lcb->ch_lcid = L2CA_ConnectReq(AVCT_PSM, p_lcb->peer_addr)) == 0)
+ {
+ /* if connect req failed, send ourselves close event */
+ avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_unbind_disc
+**
+** Description Deallocate ccb and call callback with disconnect event.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ avct_ccb_dealloc(p_data->p_ccb, AVCT_DISCONNECT_CFM_EVT, 0, NULL);
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_open_ind
+**
+** Description Handle an LL_OPEN event. For each allocated ccb already
+** bound to this lcb, send a connect event. For each
+** unbound ccb with a new PID, bind that ccb to this lcb and
+** send a connect event.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+ BOOLEAN bind = FALSE;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ /* if ccb allocated and */
+ if (p_ccb->allocated)
+ {
+ /* if bound to this lcb send connect confirm event */
+ if (p_ccb->p_lcb == p_lcb)
+ {
+ bind = TRUE;
+ L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH);
+ p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_CFM_EVT,
+ 0, p_lcb->peer_addr);
+ }
+ /* if unbound acceptor and lcb doesn't already have a ccb for this PID */
+ else if ((p_ccb->p_lcb == NULL) && (p_ccb->cc.role == AVCT_ACP) &&
+ (avct_lcb_has_pid(p_lcb, p_ccb->cc.pid) == NULL))
+ {
+ /* bind ccb to lcb and send connect ind event */
+ bind = TRUE;
+ p_ccb->p_lcb = p_lcb;
+ L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH);
+ p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_IND_EVT,
+ 0, p_lcb->peer_addr);
+ }
+ }
+ }
+
+ /* if no ccbs bound to this lcb, disconnect */
+ if (bind == FALSE)
+ {
+ avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_open_fail
+**
+** Description L2CAP channel open attempt failed. Deallocate any ccbs
+** on this lcb and send connect confirm event with failure.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb))
+ {
+ avct_ccb_dealloc(p_ccb, AVCT_CONNECT_CFM_EVT,
+ p_data->result, p_lcb->peer_addr);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_close_ind
+**
+** Description L2CAP channel closed by peer. Deallocate any initiator
+** ccbs on this lcb and send disconnect ind event.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb))
+ {
+ if (p_ccb->cc.role == AVCT_INT)
+ {
+ avct_ccb_dealloc(p_ccb, AVCT_DISCONNECT_IND_EVT,
+ 0, p_lcb->peer_addr);
+ }
+ else
+ {
+ p_ccb->p_lcb = NULL;
+ (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_DISCONNECT_IND_EVT,
+ 0, p_lcb->peer_addr);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_close_cfm
+**
+** Description L2CAP channel closed by us. Deallocate any initiator
+** ccbs on this lcb and send disconnect ind or cfm event.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+ UINT8 event;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb))
+ {
+ /* if this ccb initiated close send disconnect cfm otherwise ind */
+ if (p_ccb->ch_close)
+ {
+ p_ccb->ch_close = FALSE;
+ event = AVCT_DISCONNECT_CFM_EVT;
+ }
+ else
+ {
+ event = AVCT_DISCONNECT_IND_EVT;
+ }
+
+ if (p_ccb->cc.role == AVCT_INT)
+ {
+ avct_ccb_dealloc(p_ccb, event, p_data->result, p_lcb->peer_addr);
+ }
+ else
+ {
+ p_ccb->p_lcb = NULL;
+ (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event,
+ p_data->result, p_lcb->peer_addr);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_bind_conn
+**
+** Description Bind ccb to lcb and send connect cfm event.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ p_data->p_ccb->p_lcb = p_lcb;
+ (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb),
+ AVCT_CONNECT_CFM_EVT, 0, p_lcb->peer_addr);
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_chk_disc
+**
+** Description A ccb wants to close; if it is the last ccb on this lcb,
+** close channel. Otherwise just deallocate and call
+** callback.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ AVCT_TRACE_WARNING0("avct_lcb_chk_disc");
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+ avct_close_bcb(p_lcb, p_data);
+#endif
+ if (avct_lcb_last_ccb(p_lcb, p_data->p_ccb))
+ {
+ AVCT_TRACE_WARNING0("closing");
+ p_data->p_ccb->ch_close = TRUE;
+ avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
+ }
+ else
+ {
+ AVCT_TRACE_WARNING0("dealloc ccb");
+ avct_lcb_unbind_disc(p_lcb, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_chnl_disc
+**
+** Description Disconnect L2CAP channel.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ L2CA_DisconnectReq(p_lcb->ch_lcid);
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_bind_fail
+**
+** Description Deallocate ccb and call callback with connect event
+** with failure result.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL);
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_cong_ind
+**
+** Description Handle congestion indication from L2CAP.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+ UINT8 event;
+ BT_HDR *p_buf;
+
+ /* set event */
+ event = (p_data->cong) ? AVCT_CONG_IND_EVT : AVCT_UNCONG_IND_EVT;
+ p_lcb->cong = p_data->cong;
+ if (p_lcb->cong == FALSE && GKI_getfirst(&p_lcb->tx_q))
+ {
+ while ( !p_lcb->cong && (p_buf = (BT_HDR *)GKI_dequeue(&p_lcb->tx_q)) != NULL)
+ {
+ if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED)
+ {
+ p_lcb->cong = TRUE;
+ }
+ }
+ }
+
+ /* send event to all ccbs on this lcb */
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb))
+ {
+ (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0, p_lcb->peer_addr);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_discard_msg
+**
+** Description Discard a message sent in from the API.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ AVCT_TRACE_WARNING0("Dropping msg");
+
+ GKI_freebuf(p_data->ul_msg.p_buf);
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_send_msg
+**
+** Description Build and send an AVCTP message.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ UINT16 curr_msg_len;
+ UINT8 pkt_type;
+ UINT8 hdr_len;
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT8 nosp = 0; /* number of subsequent packets */
+ UINT16 temp;
+ UINT16 buf_size = p_lcb->peer_mtu + L2CAP_MIN_OFFSET + BT_HDR_SIZE;
+
+
+ /* store msg len */
+ curr_msg_len = p_data->ul_msg.p_buf->len;
+
+ /* initialize packet type and other stuff */
+ if (curr_msg_len <= (p_lcb->peer_mtu - AVCT_HDR_LEN_SINGLE))
+ {
+ pkt_type = AVCT_PKT_TYPE_SINGLE;
+ }
+ else
+ {
+ pkt_type = AVCT_PKT_TYPE_START;
+ temp = (curr_msg_len + AVCT_HDR_LEN_START - p_lcb->peer_mtu);
+ nosp = temp / (p_lcb->peer_mtu - 1) + 1;
+ if ( (temp % (p_lcb->peer_mtu - 1)) != 0)
+ nosp++;
+ }
+
+ /* while we haven't sent all packets */
+ while (curr_msg_len != 0)
+ {
+ /* set header len */
+ hdr_len = avct_lcb_pkt_type_len[pkt_type];
+
+ /* if remaining msg must be fragmented */
+ if (p_data->ul_msg.p_buf->len > (p_lcb->peer_mtu - hdr_len))
+ {
+ /* get a new buffer for fragment we are sending */
+ if ((p_buf = (BT_HDR *) GKI_getbuf(buf_size)) == NULL)
+ {
+ /* whoops; free original msg buf and bail */
+ AVCT_TRACE_ERROR0 ("avct_lcb_send_msg cannot alloc buffer!!");
+ GKI_freebuf(p_data->ul_msg.p_buf);
+ break;
+ }
+
+ /* copy portion of data from current message to new buffer */
+ p_buf->offset = L2CAP_MIN_OFFSET + hdr_len;
+ p_buf->len = p_lcb->peer_mtu - hdr_len;
+
+ memcpy((UINT8 *)(p_buf + 1) + p_buf->offset,
+ (UINT8 *)(p_data->ul_msg.p_buf + 1) + p_data->ul_msg.p_buf->offset, p_buf->len);
+
+ p_data->ul_msg.p_buf->offset += p_buf->len;
+ p_data->ul_msg.p_buf->len -= p_buf->len;
+ }
+ else
+ {
+ p_buf = p_data->ul_msg.p_buf;
+ }
+
+ curr_msg_len -= p_buf->len;
+
+ /* set up to build header */
+ p_buf->len += hdr_len;
+ p_buf->offset -= hdr_len;
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* build header */
+ AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr);
+ if (pkt_type == AVCT_PKT_TYPE_START)
+ {
+ UINT8_TO_STREAM(p, nosp);
+ }
+ if ((pkt_type == AVCT_PKT_TYPE_START) || (pkt_type == AVCT_PKT_TYPE_SINGLE))
+ {
+ UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid);
+ }
+
+ if (p_lcb->cong == TRUE)
+ {
+ GKI_enqueue (&p_lcb->tx_q, p_buf);
+ }
+
+ /* send message to L2CAP */
+ else
+ {
+ if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED)
+ {
+ p_lcb->cong = TRUE;
+ }
+ }
+
+ /* update pkt type for next packet */
+ if (curr_msg_len > (p_lcb->peer_mtu - AVCT_HDR_LEN_END))
+ {
+ pkt_type = AVCT_PKT_TYPE_CONT;
+ }
+ else
+ {
+ pkt_type = AVCT_PKT_TYPE_END;
+ }
+ }
+ AVCT_TRACE_DEBUG1 ("avct_lcb_send_msg tx_q_count:%d", p_lcb->tx_q.count);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_free_msg_ind
+**
+** Description Discard an incoming AVCTP message.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ if (p_data)
+ GKI_freebuf(p_data->p_buf);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_msg_ind
+**
+** Description Handle an incoming AVCTP message.
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ UINT8 *p;
+ UINT8 label, type, cr_ipid;
+ UINT16 pid;
+ tAVCT_CCB *p_ccb;
+ BT_HDR *p_buf;
+
+ /* this p_buf is to be reported through p_msg_cback. The layer_specific
+ * needs to be set properly to indicate that it is received through
+ * control channel */
+ p_data->p_buf->layer_specific = AVCT_DATA_CTRL;
+
+ /* reassemble message; if no message available (we received a fragment) return */
+ if ((p_data->p_buf = avct_lcb_msg_asmbl(p_lcb, p_data->p_buf)) == NULL)
+ {
+ return;
+ }
+
+ p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset;
+
+ /* parse header byte */
+ AVCT_PRS_HDR(p, label, type, cr_ipid);
+
+ /* check for invalid cr_ipid */
+ if (cr_ipid == AVCT_CR_IPID_INVALID)
+ {
+ AVCT_TRACE_WARNING1("Invalid cr_ipid", cr_ipid);
+ GKI_freebuf(p_data->p_buf);
+ return;
+ }
+
+ /* parse and lookup PID */
+ BE_STREAM_TO_UINT16(pid, p);
+ if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL)
+ {
+ /* PID found; send msg up, adjust bt hdr and call msg callback */
+ p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE;
+ p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE;
+ (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf);
+ }
+ else
+ {
+ /* PID not found; drop message */
+ AVCT_TRACE_WARNING1("No ccb for PID=%x", pid);
+ GKI_freebuf(p_data->p_buf);
+
+ /* if command send reject */
+ if (cr_ipid == AVCT_CMD)
+ {
+ if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVCT_CMD_POOL_ID)) != NULL)
+ {
+ p_buf->len = AVCT_HDR_LEN_SINGLE;
+ p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE;
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ);
+ UINT16_TO_BE_STREAM(p, pid);
+ L2CA_DataWrite(p_lcb->ch_lcid, p_buf);
+ }
+ }
+ }
+}