/******************************************************************************
*
* Copyright (C) 1999-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 the main SDP functions
*
******************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "bt_target.h"
#include "gki.h"
#include "l2cdefs.h"
#include "hcidefs.h"
#include "hcimsgs.h"
#include "l2c_api.h"
#include "l2cdefs.h"
#include "btu.h"
#include "btm_api.h"
#include "sdp_api.h"
#include "sdpint.h"
/********************************************************************************/
/* G L O B A L S D P D A T A */
/********************************************************************************/
#if SDP_DYNAMIC_MEMORY == FALSE
tSDP_CB sdp_cb;
#endif
/********************************************************************************/
/* 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 sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm,
UINT8 l2cap_id);
static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
#if SDP_CLIENT_ENABLED == TRUE
static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result);
static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result);
#else
#define sdp_connect_cfm NULL
#define sdp_disconnect_cfm NULL
#endif
/*******************************************************************************
**
** Function sdp_init
**
** Description This function initializes the SDP unit.
**
** Returns void
**
*******************************************************************************/
void sdp_init (void)
{
/* Clears all structures and local SDP database (if Server is enabled) */
memset (&sdp_cb, 0, sizeof (tSDP_CB));
/* Initialize the L2CAP configuration. We only care about MTU and flush */
sdp_cb.l2cap_my_cfg.mtu_present = TRUE;
sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE;
sdp_cb.l2cap_my_cfg.flush_to_present = TRUE;
sdp_cb.l2cap_my_cfg.flush_to = SDP_FLUSH_TO;
sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16;
sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS;
#if SDP_SERVER_ENABLED == TRUE
/* Register with Security Manager for the specific security level */
if (!BTM_SetSecurityLevel (FALSE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
SDP_SECURITY_LEVEL, SDP_PSM, 0, 0))
{
SDP_TRACE_ERROR0 ("Security Registration Server failed");
return;
}
#endif
#if SDP_CLIENT_ENABLED == TRUE
/* Register with Security Manager for the specific security level */
if (!BTM_SetSecurityLevel (TRUE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
SDP_SECURITY_LEVEL, SDP_PSM, 0, 0))
{
SDP_TRACE_ERROR0 ("Security Registration for Client failed");
return;
}
#endif
#if defined(SDP_INITIAL_TRACE_LEVEL)
sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL;
#else
sdp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */
#endif
sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind;
sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm;
sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL;
sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind;
sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm;
sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind;
sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm;
sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL;
sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;
sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL;
sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL;
/* Now, register with L2CAP */
if (!L2CA_Register (SDP_PSM, &sdp_cb.reg_info))
{
SDP_TRACE_ERROR0 ("SDP Registration failed");
}
}
#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE)
/*******************************************************************************
**
** Function sdp_set_max_attr_list_size
**
** Description This function sets the max attribute list size to use
**
** Returns void
**
*******************************************************************************/
UINT16 sdp_set_max_attr_list_size (UINT16 max_size)
{
if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16) )
max_size = sdp_cb.l2cap_my_cfg.mtu - 16;
sdp_cb.max_attr_list_size = max_size;
return sdp_cb.max_attr_list_size;
}
#endif
/*******************************************************************************
**
** Function sdp_connect_ind
**
** Description This function handles an inbound connection indication
** from L2CAP. This is the case where we are acting as a
** server.
**
** Returns void
**
*******************************************************************************/
static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
{
#if SDP_SERVER_ENABLED == TRUE
tCONN_CB *p_ccb;
/* Allocate a new CCB. Return if none available. */
if ((p_ccb = sdpu_allocate_ccb()) == NULL)
return;
/* Transition to the next appropriate state, waiting for config setup. */
p_ccb->con_state = SDP_STATE_CFG_SETUP;
/* Save the BD Address and Channel ID. */
memcpy (&p_ccb->device_address[0], bd_addr, sizeof (BD_ADDR));
p_ccb->connection_id = l2cap_cid;
/* Send response to the L2CAP layer. */
L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
{
tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
if (cfg.fcr_present)
{
SDP_TRACE_DEBUG6("sdp_connect_ind: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u",
cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps);
}
if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present
&& cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
/* FCR not desired; try again in basic mode */
cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
cfg.fcr_present = FALSE;
L2CA_ConfigReq (l2cap_cid, &cfg);
}
}
SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x", p_ccb->connection_id);
#else /* No server */
/* Reject the connection */
L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0);
#endif
}
#if SDP_CLIENT_ENABLED == TRUE
/*******************************************************************************
**
** Function sdp_connect_cfm
**
** Description This function handles the connect confirm events
** from L2CAP. This is the case when we are acting as a
** client and have sent a connect request.
**
** Returns void
**
*******************************************************************************/
static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result)
{
tCONN_CB *p_ccb;
tL2CAP_CFG_INFO cfg;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid);
return;
}
/* If the connection response contains success status, then */
/* Transition to the next state and startup the timer. */
if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP))
{
p_ccb->con_state = SDP_STATE_CFG_SETUP;
cfg = sdp_cb.l2cap_my_cfg;
if (cfg.fcr_present)
{
SDP_TRACE_DEBUG6("sdp_connect_cfm: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u",
cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps);
}
if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present
&& cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
{
/* FCR not desired; try again in basic mode */
cfg.fcr_present = FALSE;
cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
L2CA_ConfigReq (l2cap_cid, &cfg);
}
SDP_TRACE_EVENT1 ("SDP - got conn cnf, sent cfg req, CID: 0x%x", p_ccb->connection_id);
}
else
{
SDP_TRACE_WARNING2 ("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x", result, p_ccb->connection_id);
/* Tell the user if he has a callback */
if (p_ccb->p_cb || p_ccb->p_cb2)
{
UINT16 err = -1;
if ((result == HCI_ERR_HOST_REJECT_SECURITY)
|| (result == HCI_ERR_AUTH_FAILURE)
|| (result == HCI_ERR_PAIRING_NOT_ALLOWED)
|| (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED)
|| (result == HCI_ERR_KEY_MISSING))
err = SDP_SECURITY_ERR;
else if (result == HCI_ERR_HOST_REJECT_DEVICE)
err = SDP_CONN_REJECTED;
else
err = SDP_CONN_FAILED;
if(p_ccb->p_cb)
(*p_ccb->p_cb)(err);
else if(p_ccb->p_cb2)
(*p_ccb->p_cb2)(err, p_ccb->user_data);
}
sdpu_release_ccb (p_ccb);
}
}
#endif /* SDP_CLIENT_ENABLED == TRUE */
/*******************************************************************************
**
** Function sdp_config_ind
**
** Description This function processes the L2CAP configuration indication
** event.
**
** Returns void
**
*******************************************************************************/
static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
return;
}
/* Remember the remote MTU size */
if (!p_cfg->mtu_present)
{
/* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */
p_ccb->rem_mtu_size = (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE)?SDP_MTU_SIZE:L2CAP_DEFAULT_MTU;
}
else
{
if (p_cfg->mtu > SDP_MTU_SIZE)
p_ccb->rem_mtu_size = SDP_MTU_SIZE;
else
p_ccb->rem_mtu_size = p_cfg->mtu;
}
/* For now, always accept configuration from the other side */
p_cfg->flush_to_present = FALSE;
p_cfg->mtu_present = FALSE;
p_cfg->result = L2CAP_CFG_OK;
/* Check peer config request against our rfcomm configuration */
if (p_cfg->fcr_present)
{
/* Reject the window size if it is bigger than we want it to be */
if (p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE)
{
if (sdp_cb.l2cap_my_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE
&& p_cfg->fcr.tx_win_sz > sdp_cb.l2cap_my_cfg.fcr.tx_win_sz)
{
p_cfg->fcr.tx_win_sz = sdp_cb.l2cap_my_cfg.fcr.tx_win_sz;
p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with SMALLER TX WINDOW");
}
/* Reject if locally we want basic and they don't */
if (sdp_cb.l2cap_my_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
{
/* Ask for a new setup */
p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with BASIC mode");
}
/* Remain in configure state and give the peer our desired configuration */
if (p_cfg->result != L2CAP_CFG_OK)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: 0x%x", l2cap_cid);
L2CA_ConfigRsp (l2cap_cid, p_cfg);
return;
}
}
else /* We agree with peer's request */
p_cfg->fcr_present = FALSE;
}
L2CA_ConfigRsp (l2cap_cid, p_cfg);
SDP_TRACE_EVENT1 ("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
p_ccb->con_flags |= SDP_FLAGS_HIS_CFG_DONE;
if (p_ccb->con_flags & SDP_FLAGS_MY_CFG_DONE)
{
p_ccb->con_state = SDP_STATE_CONNECTED;
if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
sdp_disc_connected (p_ccb);
else
/* Start inactivity timer */
btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
}
}
/*******************************************************************************
**
** Function sdp_config_cfm
**
** Description This function processes the L2CAP configuration confirmation
** event.
**
** Returns void
**
*******************************************************************************/
static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
{
tCONN_CB *p_ccb;
SDP_TRACE_EVENT2 ("SDP - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result);
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
return;
}
/* For now, always accept configuration from the other side */
if (p_cfg->result == L2CAP_CFG_OK)
{
p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE;
if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE)
{
p_ccb->con_state = SDP_STATE_CONNECTED;
if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
sdp_disc_connected (p_ccb);
else
/* Start inactivity timer */
btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
}
}
else
{
/* If peer has rejected FCR and suggested basic then try basic */
if (p_cfg->fcr_present)
{
tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
cfg.fcr_present = FALSE;
L2CA_ConfigReq (l2cap_cid, &cfg);
/* Remain in configure state */
return;
}
#if SDP_CLIENT_ENABLED == TRUE
sdp_disconnect(p_ccb, SDP_CFG_FAILED);
#endif
}
}
/*******************************************************************************
**
** Function sdp_disconnect_ind
**
** Description This function handles a disconnect event from L2CAP. If
** requested to, we ack the disconnect before dropping the CCB
**
** Returns void
**
*******************************************************************************/
static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
return;
}
if (ack_needed)
L2CA_DisconnectRsp (l2cap_cid);
SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
#if SDP_CLIENT_ENABLED == TRUE
/* Tell the user if he has a callback */
if (p_ccb->p_cb)
(*p_ccb->p_cb) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
SDP_SUCCESS : SDP_CONN_FAILED));
else if (p_ccb->p_cb2)
(*p_ccb->p_cb2) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data);
#endif
sdpu_release_ccb (p_ccb);
}
/*******************************************************************************
**
** Function sdp_data_ind
**
** Description This function is called when data is received from L2CAP.
** if we are the originator of the connection, we are the SDP
** client, and the received message is queued up for the client.
**
** If we are the destination of the connection, we are the SDP
** server, so the message is passed to the server processing
** function.
**
** Returns void
**
*******************************************************************************/
static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL)
{
if (p_ccb->con_state == SDP_STATE_CONNECTED)
{
if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
sdp_disc_server_rsp (p_ccb, p_msg);
else
sdp_server_handle_client_req (p_ccb, p_msg);
}
else
{
SDP_TRACE_WARNING2 ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x",
p_ccb->con_state, l2cap_cid);
}
}
else
{
SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
}
GKI_freebuf (p_msg);
}
#if SDP_CLIENT_ENABLED == TRUE
/*******************************************************************************
**
** Function sdp_conn_originate
**
** Description This function is called from the API to originate a
** connection.
**
** Returns void
**
*******************************************************************************/
tCONN_CB* sdp_conn_originate (UINT8 *p_bd_addr)
{
tCONN_CB *p_ccb;
UINT16 cid;
/* Allocate a new CCB. Return if none available. */
if ((p_ccb = sdpu_allocate_ccb()) == NULL)
{
SDP_TRACE_WARNING0 ("SDP - no spare CCB for orig");
return (NULL);
}
SDP_TRACE_EVENT0 ("SDP - Originate started");
/* We are the originator of this connection */
p_ccb->con_flags |= SDP_FLAGS_IS_ORIG;
/* Save the BD Address and Channel ID. */
memcpy (&p_ccb->device_address[0], p_bd_addr, sizeof (BD_ADDR));
/* Transition to the next appropriate state, waiting for connection confirm. */
p_ccb->con_state = SDP_STATE_CONN_SETUP;
// btla-specific ++
#ifndef ANDROID_APP_INCLUDED /* Skip for Android: Do not need to set out_service for sdp, since sdp does not use sec. Prevents over-writing service_rec of a connection already in progress */
BTM_SetOutService(p_bd_addr, BTM_SEC_SERVICE_SDP_SERVER, 0);
#endif
// btla-specific --
cid = L2CA_ConnectReq (SDP_PSM, p_bd_addr);
/* Check if L2CAP started the connection process */
if (cid != 0)
{
p_ccb->connection_id = cid;
return (p_ccb);
}
else
{
SDP_TRACE_WARNING0 ("SDP - Originate failed");
sdpu_release_ccb (p_ccb);
return (NULL);
}
}
/*******************************************************************************
**
** Function sdp_disconnect
**
** Description This function disconnects a connection.
**
** Returns void
**
*******************************************************************************/
void sdp_disconnect (tCONN_CB*p_ccb, UINT16 reason)
{
#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
/* If we are browsing for multiple UUIDs ... */
if ((p_ccb->con_state == SDP_STATE_CONNECTED)
&& (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
&& ((reason == SDP_SUCCESS) || (reason == SDP_NO_RECS_MATCH)))
{
/* If the browse found something, do no more searching */
if ((p_ccb->cur_uuid_idx == 0) && (p_ccb->p_db->p_first_rec))
p_ccb->cur_uuid_idx = p_ccb->p_db->num_uuid_filters;
while (++p_ccb->cur_uuid_idx < p_ccb->p_db->num_uuid_filters)
{
/* Check we have not already found the UUID (maybe through browse) */
if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len == 2)
&& (SDP_FindServiceInDb (p_ccb->p_db,
p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].uu.uuid16,
NULL)))
continue;
if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len > 2)
&& (SDP_FindServiceUUIDInDb (p_ccb->p_db,
&p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx], NULL)))
continue;
p_ccb->cur_handle = 0;
SDP_TRACE_EVENT1 ("SDP - looking for for more, CID: 0x%x",
p_ccb->connection_id);
sdp_disc_connected (p_ccb);
return;
}
}
if ((reason == SDP_NO_RECS_MATCH) && (p_ccb->p_db->p_first_rec))
reason = SDP_SUCCESS;
#endif
SDP_TRACE_EVENT1 ("SDP - disconnect CID: 0x%x", p_ccb->connection_id);
/* Check if we have a connection ID */
if (p_ccb->connection_id != 0)
{
L2CA_DisconnectReq (p_ccb->connection_id);
p_ccb->disconnect_reason = reason;
}
/* If at setup state, we may not get callback ind from L2CAP */
/* Call user callback immediately */
if (p_ccb->con_state == SDP_STATE_CONN_SETUP)
{
/* Tell the user if he has a callback */
if (p_ccb->p_cb)
(*p_ccb->p_cb) (reason);
else if (p_ccb->p_cb2)
(*p_ccb->p_cb2) (reason, p_ccb->user_data);
sdpu_release_ccb (p_ccb);
}
}
/*******************************************************************************
**
** Function sdp_disconnect_cfm
**
** Description This function handles a disconnect confirm event from L2CAP.
**
** Returns void
**
*******************************************************************************/
static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
{
SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
return;
}
SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
/* Tell the user if he has a callback */
if (p_ccb->p_cb)
(*p_ccb->p_cb) (p_ccb->disconnect_reason);
else if (p_ccb->p_cb2)
(*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data);
sdpu_release_ccb (p_ccb);
}
#endif /* SDP_CLIENT_ENABLED == TRUE */
/*******************************************************************************
**
** Function sdp_conn_timeout
**
** Description This function processes a timeout. Currently, we simply send
** a disconnect request to L2CAP.
**
** Returns void
**
*******************************************************************************/
void sdp_conn_timeout (tCONN_CB*p_ccb)
{
SDP_TRACE_EVENT2 ("SDP - CCB timeout in state: %d CID: 0x%x",
p_ccb->con_state, p_ccb->connection_id);
L2CA_DisconnectReq (p_ccb->connection_id);
#if SDP_CLIENT_ENABLED == TRUE
/* Tell the user if he has a callback */
if (p_ccb->p_cb)
(*p_ccb->p_cb) (SDP_CONN_FAILED);
else if (p_ccb->p_cb2)
(*p_ccb->p_cb2) (SDP_CONN_FAILED, p_ccb->user_data);
#endif
sdpu_release_ccb (p_ccb);
}