1/******************************************************************************
2 *
3 *  Copyright (C) 2009-2013 Broadcom Corporation
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19#include <string.h>
20#include "bt_target.h"
21#include "gap_int.h"
22
23/*****************************************************************************/
24/*                 G L O B A L      GAP       D A T A                        */
25/*****************************************************************************/
26#if GAP_DYNAMIC_MEMORY == FALSE
27tGAP_CB  gap_cb;
28#endif
29
30/*****************************************************************************
31** Callbacks passed to BTM -
32**      There are different callbacks based on the control block index so that
33**      more than one command can be pending at a time.
34**  NOTE:  There must be 1 callback for each control block defined
35**          GAP_MAX_BLOCKS
36**
37**          Also, the inquiry results event has its own callback; Not handled here!
38******************************************************************************/
39static void btm_cback(UINT16 index, void *p_data)
40{
41    tGAP_INFO       *p_cb;
42    tGAP_INQ_CMPL    inq_cmpl;
43
44    /* Make sure that the index is valid AND it is in use */
45    if (index < GAP_MAX_BLOCKS && gap_cb.blk[index].in_use)
46    {
47        p_cb = &gap_cb.blk[index];
48
49        /* If the callback is non-NULL, call it with the specified event */
50        switch (p_cb->event)
51        {
52        case GAP_EVT_INQUIRY_COMPLETE:
53            /* pass the number of results to caller */
54            inq_cmpl.num_results = ((tBTM_INQUIRY_CMPL *)p_data)->num_resp;
55
56            inq_cmpl.status = (((tBTM_INQUIRY_CMPL *)p_data)->status == BTM_SUCCESS) ? BT_PASS : GAP_ERR_PROCESSING;
57
58            p_data = &inq_cmpl;
59
60            GAP_TRACE_EVENT2("   GAP Inquiry Complete Event (Status 0x%04x, Result(s) %d)",
61                            inq_cmpl.status, inq_cmpl.num_results);
62            break;
63
64        case GAP_EVT_DISCOVERY_COMPLETE:
65            if (*((UINT16 *) p_data))
66            {
67                GAP_TRACE_EVENT1("   GAP Discovery Complete Event(SDP Result: 0x%04x)", *((UINT16 *) p_data));
68            }
69            else
70            {
71                GAP_TRACE_EVENT0("   GAP Discovery Successfully Completed");
72            }
73
74            break;
75
76        case GAP_EVT_REM_NAME_COMPLETE:
77            /* override the BTM error code with a GAP error code */
78            ((tGAP_REMOTE_DEV_NAME *)p_data)->status =
79                    gap_convert_btm_status ((tBTM_STATUS)((tBTM_REMOTE_DEV_NAME *)p_data)->status);
80
81            GAP_TRACE_EVENT1("   GAP Remote Name Complete Event (status 0x%04x)", ((tGAP_REMOTE_DEV_NAME *)p_data)->status);
82
83            break;
84        };
85
86        if (p_cb->gap_cback)
87            p_cb->gap_cback(p_cb->event, p_data);
88
89        /* Deallocate the control block */
90        gap_free_cb(p_cb);
91    }
92}
93
94
95/*** Callback functions for BTM_CMPL_CB ***/
96void gap_btm_cback0(void *p1)
97{
98    btm_cback(0, p1);
99}
100
101#if GAP_MAX_BLOCKS > 1
102void gap_btm_cback1(void *p1)
103{
104    btm_cback(1, p1);
105}
106#endif
107#if GAP_MAX_BLOCKS > 2
108void gap_btm_cback2(void *p1)
109{
110    btm_cback(2, p1);
111}
112#endif
113
114/* There is only one instance of this because only 1 inquiry can be active at a time */
115void gap_inq_results_cb(tBTM_INQ_RESULTS *p_results, UINT8 *p_eir)
116{
117    tGAP_INFO   *p_cb;
118    UINT8        index;
119
120    GAP_TRACE_EVENT6 ("GAP Inquiry Results Callback (bdaddr [%02x%02x%02x%02x%02x%02x])",
121                p_results->remote_bd_addr[0], p_results->remote_bd_addr[1],
122                p_results->remote_bd_addr[2], p_results->remote_bd_addr[3],
123                p_results->remote_bd_addr[4], p_results->remote_bd_addr[5]);
124    GAP_TRACE_EVENT4 ("                             (COD [%02x%02x%02x], clkoff 0x%04x)",
125                p_results->dev_class[0], p_results->dev_class[1], p_results->dev_class[2],
126                p_results->clock_offset);
127
128    /* Find the control block which has an Inquiry Active and call its results callback */
129    for (index = 0, p_cb = &gap_cb.blk[0]; index < GAP_MAX_BLOCKS; index++, p_cb++)
130    {
131        /* Look for the control block that is using inquiry */
132        if (p_cb->in_use && (p_cb->event == GAP_EVT_INQUIRY_COMPLETE))
133        {
134            /* Notify the higher layer if they care */
135            if (p_cb->gap_inq_rslt_cback)
136                p_cb->gap_inq_rslt_cback (GAP_EVT_INQUIRY_RESULTS, (tGAP_INQ_RESULTS *)p_results);
137        }
138    }
139}
140
141
142/*******************************************************************************
143**
144** Function         gap_find_addr_name_cb
145**
146** Description      Processes the remote name request event when the Find Addr by Name
147**                  request is active.  The following procedure takes place:
148**                  1. Check the resulting name (If return status is ok)
149**                  2. If name matches requested name, we're done, call the appl's callback
150**                          with the BD ADDR.
151**                  3. Otherwise get the next BD ADDR out of the inquiry database and intiate
152**                          another remote name request.
153**                  4. If there are no more BD ADDRs, then call the appl's callback with a FAIL
154**                          status.
155**
156** Returns          void
157**
158*******************************************************************************/
159void gap_find_addr_name_cb (tBTM_REMOTE_DEV_NAME *p)
160{
161    tGAP_FINDADDR_CB        *p_cb = &gap_cb.findaddr_cb;
162    tGAP_FINDADDR_RESULTS   *p_result = &p_cb->results;
163
164    if (p_cb->in_use)
165    {
166        if (p->status == BTM_SUCCESS)
167        {
168            GAP_TRACE_EVENT2("   GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x, Name [%s])",
169                                p->status, p->remote_bd_name);
170
171            /* See if the returned name matches the desired name; if not initiate another search */
172            if (!strncmp ((char *)p_result->devname, (char *) p->remote_bd_name, strlen ((char *) p_result->devname)))
173            {
174                /* We found the device!  Copy it into the return structure */
175                memcpy (p_result->bd_addr, p_cb->p_cur_inq->results.remote_bd_addr, BD_ADDR_LEN);
176                p_result->status = BT_PASS;
177            }
178            else    /* The name doesn't match so initiate another search */
179            {
180                /* Get the device address of the next database entry */
181                if ((p_cb->p_cur_inq = BTM_InqDbNext(p_cb->p_cur_inq)) != NULL)
182                {
183                    if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
184                        (tBTM_CMPL_CB *) gap_find_addr_name_cb)) == BTM_CMD_STARTED)
185                        return;     /* This routine will get called again with the next results */
186                    else
187                        p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
188                }
189                else
190                    p_result->status = GAP_EOINQDB;     /* No inquiry results; we're done! */
191            }
192        }
193        else
194        {
195            GAP_TRACE_EVENT1("   GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x)", p->status);
196            p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
197        }
198
199        /* If this code is reached, the process has completed so call the appl's callback with results */
200        if (p_cb->p_cback)
201            p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
202
203        /* Clear out the control block */
204        p_cb->in_use = FALSE;
205        p_cb->p_cback = (tGAP_CALLBACK *) NULL;
206    }
207}
208
209/*******************************************************************************
210**
211** Function         gap_find_addr_inq_cb
212**
213** Description      Processes the inquiry complete event when the Find Addr by Name
214**                  request is active.  This callback performs one of the two following
215**                  steps:
216**                      1. If the remote name is retrieved automatically, the DB is searched
217**                          immediately, and the results are returned in the appls callback.
218**
219**                      2. If remote name is not automatic, retrieve the first BTM INQ
220**                         database entry and initiate a remote name request.
221**
222** Returns          void
223**
224*******************************************************************************/
225void gap_find_addr_inq_cb (tBTM_INQUIRY_CMPL *p)
226{
227    tGAP_FINDADDR_CB        *p_cb = &gap_cb.findaddr_cb;
228    tGAP_FINDADDR_RESULTS   *p_result = &p_cb->results;
229
230    if (p_cb->in_use)
231    {
232
233        GAP_TRACE_EVENT2("   GAP: FindAddrByName Inq Cmpl Evt (Status 0x%04x, Result(s) %d)",
234            p->status, p->num_resp);
235
236        if (p->status == BTM_SUCCESS)
237        {
238            /* Step 1: If automatically retrieving remote names then search the local database */
239            if ((p_result->status = gap_find_local_addr_by_name (p_result->devname, p_result->bd_addr)) == GAP_NO_DATA_AVAIL)
240            {
241                /* Step 2:  The name is not stored automatically, so a search of all devices needs to
242                 *          be initiated.
243                 */
244                if ((p_cb->p_cur_inq = BTM_InqDbFirst()) != NULL)
245                {
246                    if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
247                        (tBTM_CMPL_CB *) gap_find_addr_name_cb)) == BTM_CMD_STARTED)
248                        return;     /* Wait for the response in gap_find_addr_name_cb() */
249                    else
250                        p_result->status = gap_convert_btm_status (p->status);
251                }
252                else
253                    p_result->status = GAP_EOINQDB;     /* No inquiry results; we're done! */
254            }
255        }
256        else
257            p_result->status = gap_convert_btm_status (p->status);
258
259        /* If this code is reached, the process has completed so call the appl's callback with results */
260        if (p_cb->p_cback)
261            p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
262
263        /* Clear out the control block */
264        p_cb->in_use = FALSE;
265        p_cb->p_cback = (tGAP_CALLBACK *) NULL;
266    }
267}
268
269/*******************************************************************************
270**
271** Function         gap_find_local_addr_by_name
272**
273** Description      Searches through the internal inquiry database for a device
274**                  that has the same name as the one passed in.  If found, the
275**                  device address is filled in.
276**
277**                  NOTE:  It only searches up to the first BTM_MAX_REM_BD_NAME_LEN
278**                          bytes because the inquiry database uses tBTM_BD_NAME.
279**
280** Returns          BT_PASS if the name was found and the device address is filled in
281**                  GAP_EOINQDB if the name was not found in the database
282**                  GAP_NO_DATA_AVAIL if the name is not saved with the inquiry
283**
284*******************************************************************************/
285UINT16 gap_find_local_addr_by_name (const tBTM_BD_NAME devname, BD_ADDR bd_addr)
286{
287
288/* If the remote name is retrieved automatically during an inquiry search the local db */
289#if (BTM_INQ_GET_REMOTE_NAME == TRUE)
290    tBTM_INQ_INFO   *p_result;
291
292    p_result = BTM_InqDbFirst();
293
294    while (p_result)
295    {
296        /* Check the entry for a device name match */
297        if (!strncmp ((char *)devname, (char *)p_result->remote_name, BTM_MAX_REM_BD_NAME_LEN))
298        {
299            memcpy (bd_addr, p_result->results.remote_bd_addr, BD_ADDR_LEN);
300            return (BT_PASS);
301        }
302        else
303            p_result = BTM_InqDbNext(p_result);
304    };
305
306    return (GAP_EOINQDB);
307#else
308    /* No data available because we are not automatically saving the data */
309    return (GAP_NO_DATA_AVAIL);
310#endif
311}
312
313
314/*******************************************************************************
315**
316** Function         gap_allocate_cb
317**
318** Description      Look through the GAP Control Blocks for a free one.
319**
320** Returns          Pointer to the control block or NULL if not found
321**
322*******************************************************************************/
323tGAP_INFO *gap_allocate_cb (void)
324{
325    tGAP_INFO     *p_cb = &gap_cb.blk[0];
326    UINT8        x;
327
328    for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
329    {
330        if (!p_cb->in_use)
331        {
332            memset (p_cb, 0, sizeof (tGAP_INFO));
333
334            p_cb->in_use = TRUE;
335            p_cb->index = x;
336            p_cb->p_data = (void *)NULL;
337            return (p_cb);
338        }
339    }
340
341    /* If here, no free control blocks found */
342    return (NULL);
343}
344
345
346/*******************************************************************************
347**
348** Function         gap_free_cb
349**
350** Description      Release GAP control block.
351**
352** Returns          Pointer to the control block or NULL if not found
353**
354*******************************************************************************/
355void gap_free_cb (tGAP_INFO *p_cb)
356{
357    if (p_cb)
358    {
359        p_cb->gap_cback = NULL;
360        p_cb->in_use = FALSE;
361    }
362}
363
364
365/*******************************************************************************
366**
367** Function         gap_is_service_busy
368**
369** Description      Look through the GAP Control Blocks that are in use
370**                  and check to see if the event waiting for is the command
371**                  requested.
372**
373** Returns          TRUE if already in use
374**                  FALSE if not busy
375**
376*******************************************************************************/
377BOOLEAN gap_is_service_busy (UINT16 request)
378{
379    tGAP_INFO   *p_cb = &gap_cb.blk[0];
380    UINT8        x;
381
382    for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
383    {
384        if (p_cb->in_use && p_cb->event == request)
385            return (TRUE);
386    }
387
388    /* If here, service is not busy */
389    return (FALSE);
390}
391
392
393/*******************************************************************************
394**
395** Function         gap_convert_btm_status
396**
397** Description      Converts a BTM error status into a GAP error status
398**
399**
400** Returns          GAP_UNKNOWN_BTM_STATUS is returned if not recognized
401**
402*******************************************************************************/
403UINT16 gap_convert_btm_status (tBTM_STATUS btm_status)
404{
405    switch (btm_status)
406    {
407    case BTM_SUCCESS:
408        return (BT_PASS);
409
410    case BTM_CMD_STARTED:
411        return (GAP_CMD_INITIATED);
412
413    case BTM_BUSY:
414        return (GAP_ERR_BUSY);
415
416    case BTM_MODE_UNSUPPORTED:
417    case BTM_ILLEGAL_VALUE:
418        return (GAP_ERR_ILL_PARM);
419
420    case BTM_WRONG_MODE:
421        return (GAP_DEVICE_NOT_UP);
422
423    case BTM_UNKNOWN_ADDR:
424        return (GAP_BAD_BD_ADDR);
425
426    case BTM_DEVICE_TIMEOUT:
427        return (GAP_ERR_TIMEOUT);
428
429    default:
430        return (GAP_ERR_PROCESSING);
431    }
432}
433