Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
+444
View File
@@ -0,0 +1,444 @@
/******************************************************************************
*
* 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 "stack/bt_types.h"
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "stack/l2c_api.h"
#include "stack/l2cdefs.h"
#include "stack/btm_api.h"
#include "stack/avct_api.h"
#include "avct_int.h"
#include "osi/allocator.h"
#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE)
/* Control block for AVCT */
#if AVCT_DYNAMIC_MEMORY == FALSE
tAVCT_CB avct_cb;
#else
tAVCT_CB *avct_cb_ptr;
#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)
{
UNUSED(mtu_br);
AVCT_TRACE_API("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_API("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_API("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_DEBUG("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_API("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_API("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) || (p_ccb->p_lcb->allocated == 0)) {
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_DEBUG("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
UNUSED(handle);
UNUSED(role);
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_API("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
UNUSED(handle);
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;
}
#else
UNUSED(handle);
#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_API("AVCT_MsgReq");
/* verify p_msg parameter */
if (p_msg == NULL) {
return AVCT_NO_RESOURCES;
}
AVCT_TRACE_API("len: %d", p_msg->len);
/* map handle to ccb */
if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) {
result = AVCT_BAD_HANDLE;
osi_free(p_msg);
}
/* verify channel is bound to link */
else if (p_ccb->p_lcb == NULL) {
result = AVCT_NOT_OPEN;
osi_free(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;
osi_free(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;
}
#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */
+145
View File
@@ -0,0 +1,145 @@
/******************************************************************************
*
* 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 "stack/bt_types.h"
#include "common/bt_target.h"
#include "stack/avct_api.h"
#include "avct_int.h"
#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE)
/*******************************************************************************
**
** 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_DEBUG("avct_ccb_alloc %d", i);
break;
}
}
if (i == AVCT_NUM_CONN) {
/* out of ccbs */
p_ccb = NULL;
AVCT_TRACE_WARNING("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_DEBUG("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_WARNING("ccb %d not allocated", idx);
}
} else {
p_ccb = NULL;
AVCT_TRACE_WARNING("No ccb for idx %d", idx);
}
return p_ccb;
}
#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */
+406
View File
@@ -0,0 +1,406 @@
/******************************************************************************
*
* 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 "stack/bt_types.h"
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "stack/avct_api.h"
#include "avct_int.h"
#include "stack/l2c_api.h"
#include "stack/l2cdefs.h"
#include "osi/allocator.h"
#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE)
/* 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_DEBUG("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;
UNUSED(psm);
/* 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_DEBUG("avct_l2c_connect_ind_cback conflict_lcid:0x%x", p_lcb->conflict_lcid);
}
}
if (p_lcb) {
AVCT_TRACE_DEBUG("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_DEBUG("avct_l2c snd Cfg Req");
}
#if (BT_USE_TRACES == TRUE)
if (p_lcb) {
AVCT_TRACE_DEBUG("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_DEBUG("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_DEBUG("avct_l2c snd Cfg Req");
}
/* else failure */
else {
AVCT_TRACE_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_WARNING("ERROR -> avct_l2c_data_ind_cback drop buffer");
osi_free(p_buf);
}
}
#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */
+445
View File
@@ -0,0 +1,445 @@
/******************************************************************************
*
* 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 "stack/bt_types.h"
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "stack/avct_api.h"
#include "avct_int.h"
#include "osi/allocator.h"
#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE)
/*****************************************************************************
** 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_EVENT("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_EVENT("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_EVENT("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_EVENT("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_DEBUG("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_DEBUG("avct_lcb_alloc %d", p_lcb->allocated);
p_lcb->tx_q = fixed_queue_new(QUEUE_SIZE_MAX);
break;
}
}
if (i == AVCT_NUM_LINKS) {
/* out of lcbs */
p_lcb = NULL;
AVCT_TRACE_WARNING("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)
{
UNUSED(p_data);
AVCT_TRACE_DEBUG("%s allocated: %d", __func__, p_lcb->allocated);
// Check if the LCB is still referenced
tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
for (size_t i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
{
if (p_ccb->allocated && p_ccb->p_lcb == p_lcb)
{
AVCT_TRACE_DEBUG("%s LCB in use; lcb index: %d", __func__, i);
return;
}
}
// If not, de-allocate now...
AVCT_TRACE_DEBUG("%s Freeing LCB", __func__);
osi_free(p_lcb->p_rx_msg);
fixed_queue_free(p_lcb->tx_q, NULL);
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_WARNING("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_WARNING("avct_lcb_last_ccb");
for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
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;
}
#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */
@@ -0,0 +1,685 @@
/******************************************************************************
*
* 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 "stack/bt_types.h"
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "stack/avct_api.h"
#include "avct_int.h"
#include "stack/btm_api.h"
#include "osi/allocator.h"
#if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE)
/* 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]) {
osi_free(p_buf);
AVCT_TRACE_WARNING("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) {
AVCT_TRACE_WARNING("Got single during reassembly");
}
osi_free(p_lcb->p_rx_msg);
p_lcb->p_rx_msg = NULL;
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) {
AVCT_TRACE_WARNING("Got start during reassembly");
}
osi_free(p_lcb->p_rx_msg);
/* Allocate bigger buffer for reassembly. As lower layers are
* not aware of possible packet size after reassembly they
* would have allocated smaller buffer.
*/
p_lcb->p_rx_msg = (BT_HDR *)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
if (p_lcb->p_rx_msg == NULL) {
AVCT_TRACE_ERROR ("Cannot alloc buffer for reassembly !!");
osi_free(p_buf);
} else {
memcpy (p_lcb->p_rx_msg, p_buf,
sizeof(BT_HDR) + p_buf->offset + p_buf->len);
/* Free original buffer */
osi_free(p_buf);
/* update p to point to new buffer */
p = (UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset;
/* 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) {
osi_free(p_buf);
AVCT_TRACE_WARNING("Pkt type=%d out of order", pkt_type);
p_ret = NULL;
} else {
/* get size of buffer holding assembled message */
/*
* NOTE: The buffer is allocated above at the beginning of the
* reassembly, and is always of size BT_DEFAULT_BUFFER_SIZE.
*/
buf_len = BT_DEFAULT_BUFFER_SIZE - 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 */
AVCT_TRACE_WARNING("%s: Fragmented message too big!", __func__);
osi_free(p_lcb->p_rx_msg);
p_lcb->p_rx_msg = NULL;
osi_free(p_buf);
p_ret = NULL;
} 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;
}
osi_free(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;
UNUSED(p_data);
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)
{
UNUSED(p_lcb);
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;
UNUSED(p_data);
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_EVENT("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_EVENT("closing");
p_data->p_ccb->ch_close = TRUE;
avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
} else {
AVCT_TRACE_EVENT("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)
{
UNUSED(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)
{
UNUSED(p_lcb);
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 && !fixed_queue_is_empty(p_lcb->tx_q))
{
while (!p_lcb->cong &&
(p_buf = (BT_HDR *)fixed_queue_dequeue(p_lcb->tx_q, 0)) != 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)
{
UNUSED(p_lcb);
AVCT_TRACE_WARNING("Dropping msg");
osi_free(p_data->ul_msg.p_buf);
p_data->ul_msg.p_buf = NULL;
}
/*******************************************************************************
**
** 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;
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) {
BT_HDR *p_buf;
/* 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 *) osi_malloc(buf_size)) == NULL) {
/* whoops; free original msg buf and bail */
AVCT_TRACE_ERROR ("avct_lcb_send_msg cannot alloc buffer!!");
osi_free(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) {
fixed_queue_enqueue(p_lcb->tx_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
}
/* 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_DEBUG ("avct_lcb_send_msg tx_q_count:%d",
fixed_queue_length(p_lcb->tx_q));
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)
{
UNUSED(p_lcb);
if (p_data == NULL) {
return;
}
osi_free(p_data->p_buf);
p_data->p_buf = NULL;
}
/*******************************************************************************
**
** 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);
UNUSED(type);
/* check for invalid cr_ipid */
if (cr_ipid == AVCT_CR_IPID_INVALID) {
AVCT_TRACE_WARNING("Invalid cr_ipid %d", cr_ipid);
osi_free(p_data->p_buf);
p_data->p_buf = NULL;
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_WARNING("No ccb for PID=%x", pid);
osi_free(p_data->p_buf);
p_data->p_buf = NULL;
/* if command send reject */
if (cr_ipid == AVCT_CMD) {
if ((p_buf = (BT_HDR *) osi_malloc(AVCT_CMD_BUF_SIZE)) != 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);
}
}
}
}
#endif /* #if (defined(AVCT_INCLUDED) && AVCT_INCLUDED == TRUE) */
@@ -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 */
@@ -0,0 +1,237 @@
/******************************************************************************
*
* 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 "stack/avct_api.h"
#include "avct_defs.h"
#include "stack/l2c_api.h"
#include "osi/fixed_queue.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 */
fixed_queue_t *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
extern tAVCT_CB avct_cb;
#else
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 */