/******************************************************************************
*
* Copyright (C) 2009-2013 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.
*
******************************************************************************/
#include <string.h>
#include "bt_target.h"
#include "gap_int.h"
/*****************************************************************************/
/* G L O B A L GAP D A T A */
/*****************************************************************************/
#if GAP_DYNAMIC_MEMORY == FALSE
tGAP_CB gap_cb;
#endif
/*****************************************************************************
** Callbacks passed to BTM -
** There are different callbacks based on the control block index so that
** more than one command can be pending at a time.
** NOTE: There must be 1 callback for each control block defined
** GAP_MAX_BLOCKS
**
** Also, the inquiry results event has its own callback; Not handled here!
******************************************************************************/
static void btm_cback(UINT16 index, void *p_data)
{
tGAP_INFO *p_cb;
tGAP_INQ_CMPL inq_cmpl;
/* Make sure that the index is valid AND it is in use */
if (index < GAP_MAX_BLOCKS && gap_cb.blk[index].in_use)
{
p_cb = &gap_cb.blk[index];
/* If the callback is non-NULL, call it with the specified event */
switch (p_cb->event)
{
case GAP_EVT_INQUIRY_COMPLETE:
/* pass the number of results to caller */
inq_cmpl.num_results = ((tBTM_INQUIRY_CMPL *)p_data)->num_resp;
inq_cmpl.status = (((tBTM_INQUIRY_CMPL *)p_data)->status == BTM_SUCCESS) ? BT_PASS : GAP_ERR_PROCESSING;
p_data = &inq_cmpl;
GAP_TRACE_EVENT2(" GAP Inquiry Complete Event (Status 0x%04x, Result(s) %d)",
inq_cmpl.status, inq_cmpl.num_results);
break;
case GAP_EVT_DISCOVERY_COMPLETE:
if (*((UINT16 *) p_data))
{
GAP_TRACE_EVENT1(" GAP Discovery Complete Event(SDP Result: 0x%04x)", *((UINT16 *) p_data));
}
else
{
GAP_TRACE_EVENT0(" GAP Discovery Successfully Completed");
}
break;
case GAP_EVT_REM_NAME_COMPLETE:
/* override the BTM error code with a GAP error code */
((tGAP_REMOTE_DEV_NAME *)p_data)->status =
gap_convert_btm_status ((tBTM_STATUS)((tBTM_REMOTE_DEV_NAME *)p_data)->status);
GAP_TRACE_EVENT1(" GAP Remote Name Complete Event (status 0x%04x)", ((tGAP_REMOTE_DEV_NAME *)p_data)->status);
break;
};
if (p_cb->gap_cback)
p_cb->gap_cback(p_cb->event, p_data);
/* Deallocate the control block */
gap_free_cb(p_cb);
}
}
/*** Callback functions for BTM_CMPL_CB ***/
void gap_btm_cback0(void *p1)
{
btm_cback(0, p1);
}
#if GAP_MAX_BLOCKS > 1
void gap_btm_cback1(void *p1)
{
btm_cback(1, p1);
}
#endif
#if GAP_MAX_BLOCKS > 2
void gap_btm_cback2(void *p1)
{
btm_cback(2, p1);
}
#endif
/* There is only one instance of this because only 1 inquiry can be active at a time */
void gap_inq_results_cb(tBTM_INQ_RESULTS *p_results, UINT8 *p_eir)
{
tGAP_INFO *p_cb;
UINT8 index;
GAP_TRACE_EVENT6 ("GAP Inquiry Results Callback (bdaddr [%02x%02x%02x%02x%02x%02x])",
p_results->remote_bd_addr[0], p_results->remote_bd_addr[1],
p_results->remote_bd_addr[2], p_results->remote_bd_addr[3],
p_results->remote_bd_addr[4], p_results->remote_bd_addr[5]);
GAP_TRACE_EVENT4 (" (COD [%02x%02x%02x], clkoff 0x%04x)",
p_results->dev_class[0], p_results->dev_class[1], p_results->dev_class[2],
p_results->clock_offset);
/* Find the control block which has an Inquiry Active and call its results callback */
for (index = 0, p_cb = &gap_cb.blk[0]; index < GAP_MAX_BLOCKS; index++, p_cb++)
{
/* Look for the control block that is using inquiry */
if (p_cb->in_use && (p_cb->event == GAP_EVT_INQUIRY_COMPLETE))
{
/* Notify the higher layer if they care */
if (p_cb->gap_inq_rslt_cback)
p_cb->gap_inq_rslt_cback (GAP_EVT_INQUIRY_RESULTS, (tGAP_INQ_RESULTS *)p_results);
}
}
}
/*******************************************************************************
**
** Function gap_find_addr_name_cb
**
** Description Processes the remote name request event when the Find Addr by Name
** request is active. The following procedure takes place:
** 1. Check the resulting name (If return status is ok)
** 2. If name matches requested name, we're done, call the appl's callback
** with the BD ADDR.
** 3. Otherwise get the next BD ADDR out of the inquiry database and intiate
** another remote name request.
** 4. If there are no more BD ADDRs, then call the appl's callback with a FAIL
** status.
**
** Returns void
**
*******************************************************************************/
void gap_find_addr_name_cb (tBTM_REMOTE_DEV_NAME *p)
{
tGAP_FINDADDR_CB *p_cb = &gap_cb.findaddr_cb;
tGAP_FINDADDR_RESULTS *p_result = &p_cb->results;
if (p_cb->in_use)
{
if (p->status == BTM_SUCCESS)
{
GAP_TRACE_EVENT2(" GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x, Name [%s])",
p->status, p->remote_bd_name);
/* See if the returned name matches the desired name; if not initiate another search */
if (!strncmp ((char *)p_result->devname, (char *) p->remote_bd_name, strlen ((char *) p_result->devname)))
{
/* We found the device! Copy it into the return structure */
memcpy (p_result->bd_addr, p_cb->p_cur_inq->results.remote_bd_addr, BD_ADDR_LEN);
p_result->status = BT_PASS;
}
else /* The name doesn't match so initiate another search */
{
/* Get the device address of the next database entry */
if ((p_cb->p_cur_inq = BTM_InqDbNext(p_cb->p_cur_inq)) != NULL)
{
if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
(tBTM_CMPL_CB *) gap_find_addr_name_cb)) == BTM_CMD_STARTED)
return; /* This routine will get called again with the next results */
else
p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
}
else
p_result->status = GAP_EOINQDB; /* No inquiry results; we're done! */
}
}
else
{
GAP_TRACE_EVENT1(" GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x)", p->status);
p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
}
/* If this code is reached, the process has completed so call the appl's callback with results */
if (p_cb->p_cback)
p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
/* Clear out the control block */
p_cb->in_use = FALSE;
p_cb->p_cback = (tGAP_CALLBACK *) NULL;
}
}
/*******************************************************************************
**
** Function gap_find_addr_inq_cb
**
** Description Processes the inquiry complete event when the Find Addr by Name
** request is active. This callback performs one of the two following
** steps:
** 1. If the remote name is retrieved automatically, the DB is searched
** immediately, and the results are returned in the appls callback.
**
** 2. If remote name is not automatic, retrieve the first BTM INQ
** database entry and initiate a remote name request.
**
** Returns void
**
*******************************************************************************/
void gap_find_addr_inq_cb (tBTM_INQUIRY_CMPL *p)
{
tGAP_FINDADDR_CB *p_cb = &gap_cb.findaddr_cb;
tGAP_FINDADDR_RESULTS *p_result = &p_cb->results;
if (p_cb->in_use)
{
GAP_TRACE_EVENT2(" GAP: FindAddrByName Inq Cmpl Evt (Status 0x%04x, Result(s) %d)",
p->status, p->num_resp);
if (p->status == BTM_SUCCESS)
{
/* Step 1: If automatically retrieving remote names then search the local database */
if ((p_result->status = gap_find_local_addr_by_name (p_result->devname, p_result->bd_addr)) == GAP_NO_DATA_AVAIL)
{
/* Step 2: The name is not stored automatically, so a search of all devices needs to
* be initiated.
*/
if ((p_cb->p_cur_inq = BTM_InqDbFirst()) != NULL)
{
if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
(tBTM_CMPL_CB *) gap_find_addr_name_cb)) == BTM_CMD_STARTED)
return; /* Wait for the response in gap_find_addr_name_cb() */
else
p_result->status = gap_convert_btm_status (p->status);
}
else
p_result->status = GAP_EOINQDB; /* No inquiry results; we're done! */
}
}
else
p_result->status = gap_convert_btm_status (p->status);
/* If this code is reached, the process has completed so call the appl's callback with results */
if (p_cb->p_cback)
p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
/* Clear out the control block */
p_cb->in_use = FALSE;
p_cb->p_cback = (tGAP_CALLBACK *) NULL;
}
}
/*******************************************************************************
**
** Function gap_find_local_addr_by_name
**
** Description Searches through the internal inquiry database for a device
** that has the same name as the one passed in. If found, the
** device address is filled in.
**
** NOTE: It only searches up to the first BTM_MAX_REM_BD_NAME_LEN
** bytes because the inquiry database uses tBTM_BD_NAME.
**
** Returns BT_PASS if the name was found and the device address is filled in
** GAP_EOINQDB if the name was not found in the database
** GAP_NO_DATA_AVAIL if the name is not saved with the inquiry
**
*******************************************************************************/
UINT16 gap_find_local_addr_by_name (const tBTM_BD_NAME devname, BD_ADDR bd_addr)
{
/* If the remote name is retrieved automatically during an inquiry search the local db */
#if (BTM_INQ_GET_REMOTE_NAME == TRUE)
tBTM_INQ_INFO *p_result;
p_result = BTM_InqDbFirst();
while (p_result)
{
/* Check the entry for a device name match */
if (!strncmp ((char *)devname, (char *)p_result->remote_name, BTM_MAX_REM_BD_NAME_LEN))
{
memcpy (bd_addr, p_result->results.remote_bd_addr, BD_ADDR_LEN);
return (BT_PASS);
}
else
p_result = BTM_InqDbNext(p_result);
};
return (GAP_EOINQDB);
#else
/* No data available because we are not automatically saving the data */
return (GAP_NO_DATA_AVAIL);
#endif
}
/*******************************************************************************
**
** Function gap_allocate_cb
**
** Description Look through the GAP Control Blocks for a free one.
**
** Returns Pointer to the control block or NULL if not found
**
*******************************************************************************/
tGAP_INFO *gap_allocate_cb (void)
{
tGAP_INFO *p_cb = &gap_cb.blk[0];
UINT8 x;
for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
{
if (!p_cb->in_use)
{
memset (p_cb, 0, sizeof (tGAP_INFO));
p_cb->in_use = TRUE;
p_cb->index = x;
p_cb->p_data = (void *)NULL;
return (p_cb);
}
}
/* If here, no free control blocks found */
return (NULL);
}
/*******************************************************************************
**
** Function gap_free_cb
**
** Description Release GAP control block.
**
** Returns Pointer to the control block or NULL if not found
**
*******************************************************************************/
void gap_free_cb (tGAP_INFO *p_cb)
{
if (p_cb)
{
p_cb->gap_cback = NULL;
p_cb->in_use = FALSE;
}
}
/*******************************************************************************
**
** Function gap_is_service_busy
**
** Description Look through the GAP Control Blocks that are in use
** and check to see if the event waiting for is the command
** requested.
**
** Returns TRUE if already in use
** FALSE if not busy
**
*******************************************************************************/
BOOLEAN gap_is_service_busy (UINT16 request)
{
tGAP_INFO *p_cb = &gap_cb.blk[0];
UINT8 x;
for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
{
if (p_cb->in_use && p_cb->event == request)
return (TRUE);
}
/* If here, service is not busy */
return (FALSE);
}
/*******************************************************************************
**
** Function gap_convert_btm_status
**
** Description Converts a BTM error status into a GAP error status
**
**
** Returns GAP_UNKNOWN_BTM_STATUS is returned if not recognized
**
*******************************************************************************/
UINT16 gap_convert_btm_status (tBTM_STATUS btm_status)
{
switch (btm_status)
{
case BTM_SUCCESS:
return (BT_PASS);
case BTM_CMD_STARTED:
return (GAP_CMD_INITIATED);
case BTM_BUSY:
return (GAP_ERR_BUSY);
case BTM_MODE_UNSUPPORTED:
case BTM_ILLEGAL_VALUE:
return (GAP_ERR_ILL_PARM);
case BTM_WRONG_MODE:
return (GAP_DEVICE_NOT_UP);
case BTM_UNKNOWN_ADDR:
return (GAP_BAD_BD_ADDR);
case BTM_DEVICE_TIMEOUT:
return (GAP_ERR_TIMEOUT);
default:
return (GAP_ERR_PROCESSING);
}
}