blob: 8e4ad58caac4424bbfc49175669130939ad535ac [file] [log] [blame]
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of The Linux Foundation nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/******************************************************************************
*
* This file contains state machine and action routines for L2cap Socket
*
******************************************************************************/
#include "bt_target.h"
#if (defined(OBX_OVER_L2CAP_INCLUDED) && OBX_OVER_L2CAP_INCLUDED == TRUE)
#include <string.h>
#include "gki.h"
#include "bt_types.h"
#include "l2c_sock_api.h"
#include "l2c_sock_int.h"
tL2C_SOCK_MCB l2c_sock_mcb;
/********************************************************************************/
/* L O C A L F U N C T I O N P R O T O T Y P E S */
/********************************************************************************/
static void l2c_sock_sm_state_idle (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data);
static void l2c_sock_sm_state_wait_conn_cnf (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data);
static void l2c_sock_sm_state_configure (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data);
static void l2c_sock_sm_state_connected (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data);
static void l2c_sock_sm_state_wait_disc_cnf (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data);
static void l2c_sock_conf_ind (tL2C_SOCK_CB *p_l2c_cb, tL2CAP_CFG_INFO *p_cfg);
static void l2c_sock_conf_cnf (tL2C_SOCK_CB *p_l2c_cb, tL2CAP_CFG_INFO *p_cfg);
/*******************************************************************************
**
** Function l2c_sock_sm_execute
**
** Description This function sends BTA layer and Core L2cap events through
** the state machine.
**
** Returns void
**
*******************************************************************************/
void l2c_sock_sm_execute (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
switch (p_l2c_cb->state)
{
case L2C_SOCK_STATE_IDLE:
l2c_sock_sm_state_idle (p_l2c_cb, event, p_data);
break;
case L2C_SOCK_STATE_WAIT_CONN_CNF:
l2c_sock_sm_state_wait_conn_cnf (p_l2c_cb, event, p_data);
break;
case L2C_SOCK_STATE_CONFIGURE:
l2c_sock_sm_state_configure (p_l2c_cb, event, p_data);
break;
case L2C_SOCK_STATE_CONNECTED:
l2c_sock_sm_state_connected (p_l2c_cb, event, p_data);
break;
case L2C_SOCK_STATE_WAIT_DISC_CNF:
l2c_sock_sm_state_wait_disc_cnf (p_l2c_cb, event, p_data);
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_sm_state_idle
**
** Description This function handles events when the L2cap socket is in
** IDLE state.
** Returns void
**
*******************************************************************************/
static void l2c_sock_sm_state_idle (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
L2C_SOCK_TRACE_EVENT ("l2c_sock_sm_state_idle - evt:%d", event);
switch (event)
{
case L2C_SOCK_EVT_START_REQ:
/* send the L2cap connect request */
if ((p_l2c_cb->lcid = L2CA_ErtmConnectReq (p_l2c_cb->psm, p_l2c_cb->bd_addr,
&p_l2c_cb->ertm_info)) == 0)
{
/* Intimate the upper layer if there is a callback */
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb(p_l2c_cb);
return;
}
p_l2c_cb->state = L2C_SOCK_STATE_WAIT_CONN_CNF;
break;
case L2C_SOCK_EVT_START_RSP:
case L2C_SOCK_EVT_CONN_CNF:
case L2C_SOCK_EVT_CONF_IND:
case L2C_SOCK_EVT_CONF_CNF:
case L2C_SOCK_EVT_DISC_IND:
L2C_SOCK_TRACE_ERROR (" error state %d event %d", p_l2c_cb->state, event);
break;
case L2C_SOCK_EVT_CONN_IND:
{
UINT8 id = *((UINT8 *)p_data);
/* Transition to the next appropriate state, waiting for config setup. */
p_l2c_cb->state = L2C_SOCK_STATE_CONFIGURE;
/* Send response to the L2CAP layer. */
L2CA_ErtmConnectRsp (p_l2c_cb->bd_addr, id, p_l2c_cb->lcid, L2CAP_CONN_OK,
L2CAP_CONN_OK, &p_l2c_cb->ertm_info);
/* Send a Configuration Request. */
L2CA_CONFIG_REQ (p_l2c_cb->lcid, &p_l2c_cb->cfg);
}
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_sm_state_wait_conn_cnf
**
** Description This function handles events when the L2C socket is
** waiting for Connection Confirm from L2CAP.
**
** Returns void
**
*******************************************************************************/
static void l2c_sock_sm_state_wait_conn_cnf (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
L2C_SOCK_TRACE_EVENT ("l2c_sock_sm_state_wait_conn_cnf - evt:%d", event);
switch (event)
{
case L2C_SOCK_EVT_START_REQ:
L2C_SOCK_TRACE_ERROR (" error state %d event %d", p_l2c_cb->state, event);
break;
case L2C_SOCK_EVT_CONF_IND:
/* move to the state connected if local cfg sent and remote cfg is received */
l2c_sock_conf_ind (p_l2c_cb, (tL2CAP_CFG_INFO *)p_data);
break;
case L2C_SOCK_EVT_CONN_CNF:
{
UINT16 result = *((UINT16 *)p_data);
/* send the config request and make the state chnage */
if (result == L2CAP_CONN_OK)
{
p_l2c_cb->state = L2C_SOCK_STATE_CONFIGURE;
L2CA_CONFIG_REQ (p_l2c_cb->lcid, &p_l2c_cb->cfg);
}
else
{
/* Intimate the upper layer if there is a callback */
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb (p_l2c_cb);
}
}
break;
case L2C_SOCK_EVT_DISC_IND:
p_l2c_cb->state = L2C_SOCK_STATE_IDLE;
/* Intimate the upper layer if there is a callback */
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb(p_l2c_cb);
break;
case L2C_SOCK_EVT_CLOSE_REQ:
/* send the L2CA_DISCONNECT_REQ and move the state to L2C_SOCK_STATE_WAIT_DISC_CNF */
p_l2c_cb->state = L2C_SOCK_STATE_WAIT_DISC_CNF;
L2CA_DISCONNECT_REQ (p_l2c_cb->lcid);
l2c_sock_release_scb (p_l2c_cb);
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_sm_state_configure
**
** Description This function handles events when the L2C socket in the
** configuration state.
**
** Returns void
**
*******************************************************************************/
static void l2c_sock_sm_state_configure (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
L2C_SOCK_TRACE_EVENT ("l2c_sock_sm_state_configure - evt:%d", event);
switch (event)
{
case L2C_SOCK_EVT_START_REQ:
case L2C_SOCK_EVT_CONN_CNF:
L2C_SOCK_TRACE_ERROR (" error state %d event %d", p_l2c_cb->state, event);
break;
case L2C_SOCK_EVT_CONF_IND:
/* move to the state connected if local cfg sent and remote cfg is received */
l2c_sock_conf_ind (p_l2c_cb, (tL2CAP_CFG_INFO *)p_data);
break;
case L2C_SOCK_EVT_CONF_CNF:
/* move to the state connected if local cfg sent and remote cfg is received */
l2c_sock_conf_cnf (p_l2c_cb, (tL2CAP_CFG_INFO *)p_data);
break;
case L2C_SOCK_EVT_DISC_IND:
p_l2c_cb->state = L2C_SOCK_STATE_IDLE;
/* Intimate the upper layer if there is a callback */
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb(p_l2c_cb);
break;
case L2C_SOCK_EVT_CLOSE_REQ:
/* send the L2CA_DISCONNECT_REQ and move the state to L2C_SOCK_STATE_WAIT_DISC_CNF */
p_l2c_cb->state = L2C_SOCK_STATE_WAIT_DISC_CNF;
L2CA_DISCONNECT_REQ (p_l2c_cb->lcid);
l2c_sock_release_scb (p_l2c_cb);
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_sm_state_connected
**
** Description This function handles events when the L2C socket is
** in the CONNECTED state
**
** Returns void
**
*******************************************************************************/
static void l2c_sock_sm_state_connected (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
L2C_SOCK_TRACE_EVENT ("l2c_sock_sm_state_connected - evt:%d", event);
switch (event)
{
case L2C_SOCK_EVT_CLOSE_REQ:
/* send the L2CA_DISCONNECT_REQ and move the state to L2C_SOCK_STATE_WAIT_DISC_CNF */
p_l2c_cb->state = L2C_SOCK_STATE_WAIT_DISC_CNF;
L2CA_DISCONNECT_REQ (p_l2c_cb->lcid);
l2c_sock_release_scb (p_l2c_cb);
break;
case L2C_SOCK_EVT_DISC_IND:
p_l2c_cb->state = L2C_SOCK_STATE_IDLE;
/* Intimate the upper layer if there is a callback */
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb(p_l2c_cb);
break;
case L2C_SOCK_EVT_DATA_IN:
/* incoming data from lower L2cap layers so sent this data to app layer */
if (p_l2c_cb->p_l2c_sock_data_co_cback)
{
p_l2c_cb->p_l2c_sock_data_co_cback (p_l2c_cb->inx, (UINT8 *) p_data, -1,
DATA_CO_CALLBACK_TYPE_INCOMING);
}
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_sm_state_wait_disc_cnf
**
** Description This function handles events when the L2C socket sent
** DISC and is waiting for disconnection confirmation.
**
** Returns void
**
*******************************************************************************/
static void l2c_sock_sm_state_wait_disc_cnf (tL2C_SOCK_CB *p_l2c_cb, UINT16 event, void *p_data)
{
BT_HDR *p_buf;
L2C_SOCK_TRACE_EVENT ("l2c_sock_sm_state_wait_disc_cnf - evt:%d", event);
switch (event)
{
case L2C_SOCK_EVT_DISC_CNF:
p_l2c_cb->p_sock_mgmt_cback(p_l2c_cb->inx, L2C_SOCK_CLOSED);
p_l2c_cb->state = L2C_SOCK_STATE_IDLE;
/* clean up the socket data */
break;
case L2C_SOCK_EVT_DISC_IND:
case L2C_SOCK_EVT_START_REQ:
case L2C_SOCK_EVT_CLOSE_REQ:
break;
}
}
/*******************************************************************************
**
** Function l2c_sock_conf_cnf
**
** Description This function handles L2CA_ConfigCnf message from the
** L2CAP. If result is not success tell upper layer that
** start has not been accepted.
**
*******************************************************************************/
static void l2c_sock_conf_cnf (tL2C_SOCK_CB *p_l2c_cb, tL2CAP_CFG_INFO *p_cfg)
{
L2C_SOCK_TRACE_EVENT ("l2c_sock_conf_cnf p_cfg:%08x res:%d ", p_cfg, (p_cfg) ? p_cfg->result : 0);
if (p_cfg && p_cfg->result == L2CAP_CFG_OK)
{
p_l2c_cb->local_cfg_sent = TRUE;
if ((p_l2c_cb->state == L2C_SOCK_STATE_CONFIGURE) && p_l2c_cb->peer_cfg_rcvd)
{
p_l2c_cb->state = L2C_SOCK_STATE_CONNECTED;
p_l2c_cb->p_sock_mgmt_cback(p_l2c_cb->inx, L2C_SOCK_SUCCESS);
}
if (p_l2c_cb->cfg.fcr_present)
p_l2c_cb->cfg.fcr.mode = p_cfg->fcr.mode;
else
p_l2c_cb->cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
}
else
{
if (p_l2c_cb->p_sock_mgmt_cback)
(*p_l2c_cb->p_sock_mgmt_cback) (p_l2c_cb->inx, L2C_SOCK_UNKNOWN_ERROR);
l2c_sock_release_scb (p_l2c_cb);
}
}
/*******************************************************************************
**
** Function l2c_sock_conf_ind
**
** Description This function handles L2CA_ConfigInd message from the
** L2CAP. Send the L2CA_ConfigRsp message.
**
*******************************************************************************/
static void l2c_sock_conf_ind (tL2C_SOCK_CB *p_l2c_cb, tL2CAP_CFG_INFO *p_cfg)
{
UINT16 local_mtu_size;
if (p_l2c_cb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
{
local_mtu_size = GKI_get_pool_bufsize (p_l2c_cb->ertm_info.user_tx_pool_id)
- sizeof(BT_HDR) - L2CAP_MIN_OFFSET;
}
else
local_mtu_size = L2CAP_MTU_SIZE;
if ((!p_cfg->mtu_present)||(p_cfg->mtu > local_mtu_size))
{
p_l2c_cb->rem_mtu_size = local_mtu_size;
}
else
p_l2c_cb->rem_mtu_size = p_cfg->mtu;
/* For now, always accept configuration from the other side */
p_cfg->flush_to_present = FALSE;
p_cfg->mtu_present = FALSE;
p_cfg->result = L2CAP_CFG_OK;
p_cfg->fcs_present = FALSE;
L2CA_CONFIG_RSP (p_l2c_cb->lcid, p_cfg);
p_l2c_cb->peer_cfg_rcvd = TRUE;
if ((p_l2c_cb->state == L2C_SOCK_STATE_CONFIGURE) && p_l2c_cb->local_cfg_sent)
{
p_l2c_cb->p_sock_mgmt_cback(p_l2c_cb->inx, L2C_SOCK_SUCCESS);
p_l2c_cb->state = L2C_SOCK_STATE_CONNECTED;
}
}
#endif