| /****************************************************************************** |
| * |
| * 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); |
| } |
| } |