1/******************************************************************************
2 *
3 *  Copyright (C) 2003-2012 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/******************************************************************************
20 *
21 *  This file contains the action functions for device manager state
22 *  machine.
23 *
24 ******************************************************************************/
25
26#include "gki.h"
27#include "bd.h"
28#include "bta_sys.h"
29#include "bta_api.h"
30#include "bta_dm_int.h"
31#include "btm_api.h"
32
33#include <string.h>
34
35
36static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr);
37static void bta_dm_pm_set_mode(BD_ADDR peer_addr, BOOLEAN timed_out );
38static void bta_dm_pm_timer_cback(void *p_tle);
39static void bta_dm_pm_btm_cback(BD_ADDR bd_addr, tBTM_PM_STATUS status, UINT16 value, UINT8 hci_status);
40static BOOLEAN bta_dm_pm_park(BD_ADDR peer_addr);
41static BOOLEAN bta_dm_pm_sniff(tBTA_DM_PEER_DEVICE *p_peer_dev, UINT8 index);
42static BOOLEAN bta_dm_pm_is_sco_active ();
43static void bta_dm_pm_hid_check(BOOLEAN bScoActive);
44static void bta_dm_pm_set_sniff_policy(tBTA_DM_PEER_DEVICE *p_dev, BOOLEAN bDisable);
45#if (BTM_SSR_INCLUDED == TRUE)
46static void bta_dm_pm_ssr(BD_ADDR peer_addr);
47static void bta_dm_ssr_cfg_cback(UINT8 id, UINT8 app_id, UINT16 max_lat, UINT16 min_rmt_to);
48#endif
49
50tBTA_DM_CONNECTED_SRVCS bta_dm_conn_srvcs;
51
52
53/*******************************************************************************
54**
55** Function         bta_dm_init_pm
56**
57** Description      Initialises the BT low power manager
58**
59**
60** Returns          void
61**
62*******************************************************************************/
63void bta_dm_init_pm(void)
64{
65
66    memset(&bta_dm_conn_srvcs, 0x00, sizeof(bta_dm_conn_srvcs));
67
68    /* if there are no power manger entries, so not register */
69    if(p_bta_dm_pm_cfg[0].app_id != 0)
70    {
71        bta_sys_pm_register((tBTA_SYS_CONN_CBACK*)bta_dm_pm_cback);
72
73#if (BTM_SSR_INCLUDED == TRUE)
74        bta_sys_ssr_cfg_register((tBTA_SYS_SSR_CFG_CBACK*)bta_dm_ssr_cfg_cback);
75#endif
76        BTM_PmRegister((BTM_PM_REG_SET | BTM_PM_REG_NOTIF), &bta_dm_cb.pm_id,
77                       bta_dm_pm_btm_cback);
78    }
79
80
81}
82
83
84/*******************************************************************************
85**
86** Function         bta_dm_disable_pm
87**
88** Description      Disable PM
89**
90**
91** Returns          void
92**
93*******************************************************************************/
94void bta_dm_disable_pm(void)
95{
96    UINT8 i;
97
98    bta_sys_pm_register(NULL);
99    BTM_PmRegister( BTM_PM_DEREG, &bta_dm_cb.pm_id, NULL);
100
101    /* Need to stop all active timers. */
102    for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
103    {
104        if(bta_dm_cb.pm_timer[i].in_use)
105        {
106            APPL_TRACE_DEBUG1("stop dm_pm_timer:%d", i);
107            bta_sys_stop_timer(&bta_dm_cb.pm_timer[i].timer);
108            bta_dm_cb.pm_timer[i].in_use = FALSE;
109        }
110    }
111}
112
113/*******************************************************************************
114**
115** Function         bta_dm_pm_stop_timer
116**
117** Description      stop a PM timer
118**
119**
120** Returns          void
121**
122*******************************************************************************/
123static void bta_dm_pm_stop_timer(BD_ADDR peer_addr)
124{
125    UINT8 i;
126
127    for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
128    {
129
130        if(bta_dm_cb.pm_timer[i].in_use && !bdcmp(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr))
131        {
132            APPL_TRACE_DEBUG1("stop dm_pm_timer:%d", i);
133            bta_sys_stop_timer(&bta_dm_cb.pm_timer[i].timer);
134            bta_dm_cb.pm_timer[i].in_use = FALSE;
135            break;
136        }
137
138    }
139}
140
141/*******************************************************************************
142**
143** Function         bta_dm_pm_cback
144**
145** Description      Conn change callback from sys for low power management
146**
147**
148** Returns          void
149**
150*******************************************************************************/
151static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 app_id, BD_ADDR peer_addr)
152{
153
154    UINT8 i,j;
155    UINT16 policy_setting;
156    tBTM_STATUS btm_status;
157    tBTM_VERSION_INFO vers;
158#if (BTM_SSR_INCLUDED == TRUE)
159    int               index = BTA_DM_PM_SSR0;
160#endif
161    tBTA_DM_PEER_DEVICE *p_dev;
162
163    APPL_TRACE_DEBUG3("bta_dm_pm_cback: st(%d), id(%d), app(%d)", status, id, app_id);
164
165    btm_status = BTM_ReadLocalVersion (&vers);
166    p_dev = bta_dm_find_peer_device(peer_addr);
167
168	/* Disable/Enable sniff policy on the SCO link if sco Up/Down. Will be removed in 2.2*/
169    if ((btm_status == BTM_SUCCESS) &&
170        (vers.manufacturer == LMP_COMPID_BROADCOM) &&
171        (vers.hci_version < HCI_PROTO_VERSION_2_0) &&
172        ((status == BTA_SYS_SCO_OPEN) || (status == BTA_SYS_SCO_CLOSE)) )
173        {
174        bta_dm_pm_set_sniff_policy(p_dev, (status == BTA_SYS_SCO_OPEN));
175    }
176
177    /* find if there is an power mode entry for the service */
178    for(i=1; i<=p_bta_dm_pm_cfg[0].app_id; i++)
179    {
180
181        if((p_bta_dm_pm_cfg[i].id == id)
182            && ((p_bta_dm_pm_cfg[i].app_id == BTA_ALL_APP_ID ) || (p_bta_dm_pm_cfg[i].app_id == app_id )))
183            break;
184
185    }
186
187    /* if no entries are there for the app_id and subystem in p_bta_dm_pm_spec*/
188    if(i> p_bta_dm_pm_cfg[0].app_id)
189        return;
190
191    bta_dm_pm_stop_timer(peer_addr);
192    /*p_dev = bta_dm_find_peer_device(peer_addr);*/
193
194#if (BTM_SSR_INCLUDED == TRUE)
195    /* set SSR parameters on SYS CONN OPEN */
196    if((BTA_SYS_CONN_OPEN == status) && p_dev && (p_dev->info & BTA_DM_DI_USE_SSR))
197    {
198        index = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].ssr;
199    }
200#endif
201
202    /* if no action for the event */
203    if(p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].actn_tbl[status][0].power_mode == BTA_DM_PM_NO_ACTION)
204    {
205#if (BTM_SSR_INCLUDED == TRUE)
206        if(BTA_DM_PM_SSR0 == index) /* and do not need to set SSR, return. */
207#endif
208        return;
209    }
210
211    for(j=0; j<bta_dm_conn_srvcs.count ; j++)
212    {
213        /* check if an entry already present */
214        if((bta_dm_conn_srvcs.conn_srvc[j].id == id)
215            && (bta_dm_conn_srvcs.conn_srvc[j].app_id == app_id )
216            && !bdcmp(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr))
217            break;
218
219    }
220
221        /* if subsystem has no more preference on the power mode remove
222       the cb */
223    if(p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].actn_tbl[status][0].power_mode == BTA_DM_PM_NO_PREF)
224    {
225
226        if(j != bta_dm_conn_srvcs.count)
227        {
228            bta_dm_conn_srvcs.count--;
229
230            for(; j<bta_dm_conn_srvcs.count ; j++)
231            {
232
233                memcpy(&bta_dm_conn_srvcs.conn_srvc[j], &bta_dm_conn_srvcs.conn_srvc[j+1], sizeof(bta_dm_conn_srvcs.conn_srvc[j]));
234
235            }
236        }
237        else
238        {
239            APPL_TRACE_WARNING0("bta_dm_act no entry for connected service cbs");
240            return;
241        }
242    }
243    else if(j == bta_dm_conn_srvcs.count )
244    {
245        /* check if we have more connected service that cbs */
246        if(bta_dm_conn_srvcs.count == BTA_DM_NUM_CONN_SRVS)
247        {
248            APPL_TRACE_WARNING0("bta_dm_act no more connected service cbs");
249            return;
250        }
251
252        /* fill in a new cb */
253        bta_dm_conn_srvcs.conn_srvc[j].id = id;
254        bta_dm_conn_srvcs.conn_srvc[j].app_id = app_id;
255        bdcpy(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr);
256
257        APPL_TRACE_WARNING2("new conn_srvc id:%d, app_id:%d", id, app_id);
258
259        bta_dm_conn_srvcs.count++;
260        bta_dm_conn_srvcs.conn_srvc[j].state = status;
261    }
262    else
263    {
264        /* no service is added or removed. only updating status. */
265        bta_dm_conn_srvcs.conn_srvc[j].state = status;
266    }
267
268    if(p_dev)
269    {
270        p_dev->pm_mode_attempted = 0;
271        p_dev->pm_mode_failed = 0;
272    }
273
274#if (BTM_SSR_INCLUDED == TRUE)
275    if(p_bta_dm_ssr_spec[index].max_lat)
276    {
277        bta_dm_pm_ssr(peer_addr);
278    }
279#endif
280
281    bta_dm_pm_set_mode(peer_addr, FALSE);
282
283    /* perform the HID link workaround if needed
284    ** 1. If SCO up/down event is received OR
285    ** 2. If HID connection open is received and SCO is already active.
286    **     This will handle the case where HID connects when SCO already active
287    */
288    if ( (btm_status == BTM_SUCCESS) &&
289         ( ((status == BTA_SYS_SCO_OPEN) || (status == BTA_SYS_SCO_CLOSE)) ||
290           ((status == BTA_SYS_CONN_OPEN) && (id == BTA_ID_HH) && bta_dm_pm_is_sco_active()) ) )
291    {
292        BOOLEAN bScoActive;
293        if (status == BTA_SYS_CONN_OPEN)
294            bScoActive = TRUE;
295        else
296            bScoActive = (status == BTA_SYS_SCO_OPEN);
297
298        bta_dm_pm_hid_check(bScoActive);
299    }
300
301}
302
303
304/*******************************************************************************
305**
306** Function         bta_dm_pm_set_mode
307**
308** Description      Set the power mode for the device
309**
310**
311** Returns          void
312**
313*******************************************************************************/
314static void bta_dm_pm_set_mode(BD_ADDR peer_addr, BOOLEAN timed_out )
315{
316
317    tBTA_DM_PM_ACTTION  pm_action = BTA_DM_PM_NO_ACTION;
318    UINT16              timeout = 0;
319    UINT8               i,j;
320    tBTA_DM_PM_ACTTION  failed_pm = 0;
321    tBTA_DM_PEER_DEVICE *p_peer_device = NULL;
322    tBTA_DM_PM_ACTTION   allowed_modes = 0;
323    tBTA_DM_PM_ACTTION   pref_modes = 0;
324    tBTA_DM_PM_CFG      *p_pm_cfg;
325    tBTA_DM_PM_SPEC     *p_pm_spec;
326    tBTA_DM_PM_ACTN     *p_act0, *p_act1;
327    tBTA_DM_SRVCS       *p_srvcs;
328
329
330    if(!bta_dm_cb.device_list.count)
331        return;
332
333    /* see if any attempt to put device in low power mode failed */
334    p_peer_device = bta_dm_find_peer_device(peer_addr);
335    /* if no peer device found return */
336    if (p_peer_device == NULL)
337        return;
338
339    failed_pm = p_peer_device->pm_mode_failed;
340
341    for(i=0; i<bta_dm_conn_srvcs.count ; i++)
342    {
343
344        p_srvcs = &bta_dm_conn_srvcs.conn_srvc[i];
345        if(!bdcmp(p_srvcs->peer_bdaddr, peer_addr))
346        {
347
348            /* p_bta_dm_pm_cfg[0].app_id is the number of entries */
349            for(j=1; j<=p_bta_dm_pm_cfg[0].app_id; j++)
350            {
351                if((p_bta_dm_pm_cfg[j].id == p_srvcs->id)
352                    && ((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID ) ||
353                    (p_bta_dm_pm_cfg[j].app_id == p_srvcs->app_id)))
354                    break;
355            }
356
357            p_pm_cfg = &p_bta_dm_pm_cfg[j];
358            p_pm_spec = &p_bta_dm_pm_spec[p_pm_cfg->spec_idx];
359            p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state][0];
360            p_act1 = &p_pm_spec->actn_tbl[p_srvcs->state][1];
361
362            APPL_TRACE_DEBUG3("bta_dm_pm_set_mode: srvcsid: %d, state: %d, j: %d", p_srvcs->id, p_srvcs->state, j);
363            allowed_modes |= p_pm_spec->allow_mask;
364
365            /* PM actions are in the order of strictness */
366
367            /* first check if the first preference is ok */
368            if(!(failed_pm & p_act0->power_mode))
369            {
370                pref_modes |= p_act0->power_mode;
371
372                if(p_act0->power_mode > pm_action)
373                {
374                    pm_action = p_act0->power_mode;
375                    timeout =  p_act0->timeout;
376
377                }
378            }
379            /* if first preference has already failed, try second preference */
380            else if(!(failed_pm & p_act1->power_mode))
381            {
382                pref_modes |= p_act1->power_mode;
383
384                if(p_act1->power_mode > pm_action)
385                {
386                    pm_action = p_act1->power_mode;
387                    timeout =  p_act1->timeout;
388
389                }
390            }
391        }
392    }
393
394    if(pm_action & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF))
395    {
396
397        /* some service don't like the mode */
398        if(!(allowed_modes & pm_action))
399        {
400
401            /* select the other mode if its allowed and preferred, otherwise 0 which is BTA_DM_PM_NO_ACTION */
402            pm_action =  (allowed_modes & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & pref_modes);
403
404            /* no timeout needed if no action is required */
405            if(pm_action == BTA_DM_PM_NO_ACTION)
406            {
407                timeout = 0;
408            }
409
410        }
411
412
413    }
414
415    if(!timed_out && timeout)
416    {
417
418        for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
419        {
420
421            if(!bta_dm_cb.pm_timer[i].in_use)
422            {
423                bta_dm_cb.pm_timer[i].in_use = TRUE;
424                bdcpy(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr);
425                bta_dm_cb.pm_timer[i].timer.p_cback = bta_dm_pm_timer_cback;
426                bta_sys_start_timer(&bta_dm_cb.pm_timer[i].timer, 0, timeout);
427                APPL_TRACE_DEBUG2("start dm_pm_timer:%d, %d", i, timeout);
428                return;
429
430            }
431
432        }
433
434        /* no more timers */
435        if(i==BTA_DM_NUM_PM_TIMER)
436        {
437            APPL_TRACE_WARNING0("bta_dm_act dm_pm_timer no more");
438            return;
439        }
440    }
441
442    if(pm_action == BTA_DM_PM_NO_ACTION)
443    {
444
445
446    }
447    else if(pm_action == BTA_DM_PM_PARK)
448    {
449        p_peer_device->pm_mode_attempted = BTA_DM_PM_PARK;
450        bta_dm_pm_park(peer_addr);
451
452    }
453    else if(pm_action & BTA_DM_PM_SNIFF)
454    {
455        /* dont initiate SNIFF, if link_policy has it disabled */
456        if (p_peer_device->link_policy & HCI_ENABLE_SNIFF_MODE)
457        {
458	        p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF;
459    	    bta_dm_pm_sniff(p_peer_device, (UINT8)(pm_action & 0x0F) );
460        }
461        else
462        {
463            APPL_TRACE_DEBUG0("bta_dm_pm_set_mode: Link policy disallows SNIFF, ignore request");
464        }
465    }
466    else if(pm_action == BTA_DM_PM_ACTIVE)
467    {
468
469        bta_dm_pm_active(peer_addr);
470
471    }
472
473
474}
475
476
477/*******************************************************************************
478**
479** Function         bta_ag_pm_park
480**
481** Description      Switch to park mode.
482**
483**
484** Returns          TRUE if park attempted, FALSE otherwise.
485**
486*******************************************************************************/
487static BOOLEAN bta_dm_pm_park(BD_ADDR peer_addr)
488{
489
490    tBTM_PM_MODE    mode = BTM_PM_STS_ACTIVE;
491
492    /* if not in park mode, switch to park */
493    BTM_ReadPowerMode(peer_addr, &mode);
494
495    if(mode != BTM_PM_MD_PARK)
496    {
497        BTM_SetPowerMode (bta_dm_cb.pm_id, peer_addr, &p_bta_dm_pm_md[BTA_DM_PM_PARK_IDX]);
498    }
499    return TRUE;
500
501}
502
503
504/*******************************************************************************
505**
506** Function         bta_ag_pm_sniff
507**
508** Description      Switch to sniff mode.
509**
510**
511** Returns          TRUE if sniff attempted, FALSE otherwise.
512**
513*******************************************************************************/
514static BOOLEAN bta_dm_pm_sniff(tBTA_DM_PEER_DEVICE *p_peer_dev, UINT8 index)
515{
516    tBTM_PM_MODE    mode = BTM_PM_STS_ACTIVE;
517    tBTM_PM_PWR_MD  pwr_md;
518    tBTM_STATUS     status;
519
520    BTM_ReadPowerMode(p_peer_dev->peer_bdaddr, &mode);
521
522#if (BTM_SSR_INCLUDED == TRUE)
523    APPL_TRACE_DEBUG3("bta_dm_pm_sniff cur:%d, idx:%d, info:x%x", mode, index, p_peer_dev->info);
524    if (mode != BTM_PM_MD_SNIFF ||
525        (HCI_SNIFF_SUB_RATE_SUPPORTED(BTM_ReadLocalFeatures ()) &&
526         HCI_SNIFF_SUB_RATE_SUPPORTED(BTM_ReadRemoteFeatures (p_peer_dev->peer_bdaddr)) &&
527         !(p_peer_dev->info & BTA_DM_DI_USE_SSR)))
528#else
529    APPL_TRACE_DEBUG2("bta_dm_pm_sniff cur:%d, idx:%d", mode, index);
530    if(mode != BTM_PM_MD_SNIFF)
531#endif
532    {
533        /* if the current mode is not sniff, issue the sniff command.
534         * If sniff, but SSR is not used in this link, still issue the command */
535        memcpy(&pwr_md, &p_bta_dm_pm_md[index], sizeof (tBTM_PM_PWR_MD));
536        if (p_peer_dev->info & BTA_DM_DI_INT_SNIFF)
537        {
538            pwr_md.mode |= BTM_PM_MD_FORCE;
539        }
540        status = BTM_SetPowerMode (bta_dm_cb.pm_id, p_peer_dev->peer_bdaddr, &pwr_md);
541        if (status == BTM_CMD_STORED|| status == BTM_CMD_STARTED)
542        {
543            p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF|BTA_DM_DI_ACP_SNIFF);
544            p_peer_dev->info |= BTA_DM_DI_SET_SNIFF;
545        }
546        else if (status == BTM_SUCCESS)
547        {
548            APPL_TRACE_DEBUG0("bta_dm_pm_sniff BTM_SetPowerMode() returns BTM_SUCCESS");
549            p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF|BTA_DM_DI_ACP_SNIFF|BTA_DM_DI_SET_SNIFF);
550        }
551        else /* error */
552        {
553            APPL_TRACE_ERROR1("bta_dm_pm_sniff BTM_SetPowerMode() returns ERROR status=%d", status);
554            p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF|BTA_DM_DI_ACP_SNIFF|BTA_DM_DI_SET_SNIFF);
555        }
556    }
557    /* else already in sniff and is using SSR, do nothing */
558
559    return TRUE;
560
561}
562
563/*******************************************************************************
564**
565** Function         bta_dm_pm_ssr
566**
567** Description      checks and sends SSR parameters
568**
569** Returns          void
570**
571*******************************************************************************/
572#if (BTM_SSR_INCLUDED == TRUE)
573static void bta_dm_pm_ssr(BD_ADDR peer_addr)
574{
575    tBTA_DM_SSR_SPEC *p_spec, *p_spec_cur;
576    UINT8   i,j;
577    int     ssr = BTA_DM_PM_SSR0;
578
579    /* go through the connected services */
580    for(i=0; i<bta_dm_conn_srvcs.count ; i++)
581    {
582        if(!bdcmp(bta_dm_conn_srvcs.conn_srvc[i].peer_bdaddr, peer_addr))
583        {
584            /* p_bta_dm_pm_cfg[0].app_id is the number of entries */
585            for(j=1; j<=p_bta_dm_pm_cfg[0].app_id; j++)
586            {
587                /* find the associated p_bta_dm_pm_cfg */
588                if((p_bta_dm_pm_cfg[j].id == bta_dm_conn_srvcs.conn_srvc[i].id)
589                    && ((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID )
590                    || (p_bta_dm_pm_cfg[j].app_id == bta_dm_conn_srvcs.conn_srvc[i].app_id)))
591                {
592                    APPL_TRACE_WARNING2("bta_dm_pm_ssr conn_srvc id:%d, app_id:%d",
593                        bta_dm_conn_srvcs.conn_srvc[i].id, bta_dm_conn_srvcs.conn_srvc[i].app_id);
594                    break;
595                }
596            }
597
598            /* find the ssr index with the smallest max latency. */
599            p_spec_cur = &p_bta_dm_ssr_spec[p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr];
600            p_spec = &p_bta_dm_ssr_spec[ssr];
601
602            if (p_spec_cur->max_lat < p_spec->max_lat ||
603                (ssr == BTA_DM_PM_SSR0 && p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr != BTA_DM_PM_SSR0))
604            {
605                ssr = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[j].spec_idx].ssr;
606            }
607
608        }
609    }
610
611    p_spec = &p_bta_dm_ssr_spec[ssr];
612    APPL_TRACE_WARNING2("bta_dm_pm_ssr:%d, lat:%d", ssr, p_spec->max_lat);
613    if(p_spec->max_lat)
614    {
615        /* set the SSR parameters. */
616        BTM_SetSsrParams (peer_addr, p_spec->max_lat,
617            p_spec->min_rmt_to, p_spec->min_loc_to);
618    }
619}
620/*******************************************************************************
621**
622** Function         bta_dm_ssr_cfg_cback
623**
624** Description      Conn change callback from sys for low power management
625**
626**
627** Returns          void
628**
629*******************************************************************************/
630static void bta_dm_ssr_cfg_cback(UINT8 id, UINT8 app_id,
631                                 UINT16 max_lat, UINT16 min_rmt_to)
632{
633    tBTA_DM_SSR_SPEC *p_spec;
634    UINT8   i, index;
635    /* find if there is an power mode entry for the service */
636    for(i=1; i<=p_bta_dm_pm_cfg[0].app_id; i++)
637    {
638
639        if((p_bta_dm_pm_cfg[i].id == id)
640            && ((p_bta_dm_pm_cfg[i].app_id == BTA_ALL_APP_ID ) || (p_bta_dm_pm_cfg[i].app_id == app_id )))
641            break;
642
643    }
644    /* if no entries are there for the app_id and subystem in p_bta_dm_pm_spec*/
645    if(i> p_bta_dm_pm_cfg[0].app_id)
646        return;
647
648    index = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].ssr;
649
650    APPL_TRACE_DEBUG2("SSR parameter changed to: max_latency: %d min_tout: %d", max_lat, min_rmt_to);
651
652    p_spec = &p_bta_dm_ssr_spec[index];
653    p_spec->max_lat = max_lat;
654    p_spec->min_rmt_to = min_rmt_to;
655}
656
657
658#endif
659/*******************************************************************************
660**
661** Function         bta_dm_pm_active
662**
663** Description      Brings connection to active mode
664**
665**
666** Returns          void
667**
668*******************************************************************************/
669void bta_dm_pm_active(BD_ADDR peer_addr)
670{
671    tBTM_PM_PWR_MD  pm;
672
673    memset( (void*)&pm, 0, sizeof(pm));
674
675    /* switch to active mode */
676    pm.mode = BTM_PM_MD_ACTIVE;
677    BTM_SetPowerMode (bta_dm_cb.pm_id, peer_addr, &pm);
678
679
680}
681
682
683/*******************************************************************************
684**
685** Function         bta_dm_pm_btm_cback
686**
687** Description      BTM power manager callback.
688**
689**
690** Returns          void
691**
692*******************************************************************************/
693static void bta_dm_pm_btm_cback(BD_ADDR bd_addr, tBTM_PM_STATUS status, UINT16 value, UINT8 hci_status)
694{
695   tBTA_DM_PM_BTM_STATUS  *p_buf;
696
697   if ((p_buf = (tBTA_DM_PM_BTM_STATUS *) GKI_getbuf(sizeof(tBTA_DM_PM_BTM_STATUS))) != NULL)
698    {
699        p_buf->hdr.event = BTA_DM_PM_BTM_STATUS_EVT;
700        p_buf->status = status;
701        p_buf->value = value;
702        p_buf->hci_status = hci_status;
703        bdcpy(p_buf->bd_addr, bd_addr);
704        bta_sys_sendmsg(p_buf);
705    }
706}
707
708/*******************************************************************************
709**
710** Function         bta_dm_pm_timer_cback
711**
712** Description      Power management timer callback.
713**
714**
715** Returns          void
716**
717*******************************************************************************/
718static void bta_dm_pm_timer_cback(void *p_tle)
719{
720    tBTA_DM_PM_TIMER  *p_buf;
721    UINT8 i;
722
723    APPL_TRACE_WARNING0("dm_pm_timer expires");
724
725    for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
726    {
727
728        if(bta_dm_cb.pm_timer[i].in_use)
729        {
730
731            if(&bta_dm_cb.pm_timer[i].timer == (TIMER_LIST_ENT*) p_tle)
732            {
733                APPL_TRACE_WARNING1("dm_pm_timer expires %d", i);
734                bta_dm_cb.pm_timer[i].in_use = FALSE;
735                break;
736            }
737
738        }
739
740    }
741
742
743    /* no more timers */
744    if(i==BTA_DM_NUM_PM_TIMER)
745    {
746        return;
747    }
748
749    if ((p_buf = (tBTA_DM_PM_TIMER *) GKI_getbuf(sizeof(tBTA_DM_PM_TIMER))) != NULL)
750    {
751        p_buf->hdr.event = BTA_DM_PM_TIMER_EVT;
752        bdcpy(p_buf->bd_addr, bta_dm_cb.pm_timer[i].peer_bdaddr);
753        bta_sys_sendmsg(p_buf);
754    }
755}
756
757/*******************************************************************************
758**
759** Function         bta_dm_pm_btm_status
760**
761** Description      Process pm status event from btm
762**
763**
764** Returns          void
765**
766*******************************************************************************/
767void bta_dm_pm_btm_status(tBTA_DM_MSG *p_data)
768{
769
770    tBTA_DM_PEER_DEVICE *p_dev;
771    tBTA_DM_DEV_INFO    info;
772
773    APPL_TRACE_DEBUG1("bta_dm_pm_btm_status:%d", p_data->pm_status.status);
774    p_dev = bta_dm_find_peer_device(p_data->pm_status.bd_addr);
775    if(NULL == p_dev)
776        return;
777
778    info = p_dev->info;
779    /* check new mode */
780    switch (p_data->pm_status.status)
781    {
782        case BTM_PM_STS_ACTIVE:
783            /* if our sniff or park attempt failed
784            we should not try it again*/
785            if (p_data->pm_status.hci_status != 0)
786            {
787                APPL_TRACE_ERROR1("bta_dm_pm_btm_status  hci_status=%d", p_data->pm_status.hci_status);
788                p_dev->info &= ~(BTA_DM_DI_INT_SNIFF|BTA_DM_DI_ACP_SNIFF|BTA_DM_DI_SET_SNIFF);
789
790                if(p_dev->pm_mode_attempted &(BTA_DM_PM_PARK | BTA_DM_PM_SNIFF))
791                {
792                    p_dev->pm_mode_failed
793                        |= ((BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & p_dev->pm_mode_attempted);
794                    bta_dm_pm_stop_timer(p_data->pm_status.bd_addr);
795                    bta_dm_pm_set_mode(p_data->pm_status.bd_addr, FALSE);
796                }
797            }
798            else
799            {
800#if (BTM_SSR_INCLUDED == TRUE)
801                if(p_dev->prev_low)
802                {
803                    /* need to send the SSR paramaters to controller again */
804                    bta_dm_pm_ssr(p_dev->peer_bdaddr);
805                }
806                p_dev->prev_low = BTM_PM_STS_ACTIVE;
807#endif
808                bta_dm_pm_stop_timer(p_data->pm_status.bd_addr);
809                bta_dm_pm_set_mode(p_data->pm_status.bd_addr, FALSE);
810            }
811            break;
812
813#if (BTM_SSR_INCLUDED == TRUE)
814        case BTM_PM_STS_PARK:
815        case BTM_PM_STS_HOLD:
816            /* save the previous low power mode - for SSR.
817             * SSR parameters are sent to controller on "conn open".
818             * the numbers stay good until park/hold/detach */
819            if(p_dev->info & BTA_DM_DI_USE_SSR)
820                p_dev->prev_low = p_data->pm_status.status;
821            break;
822
823        case BTM_PM_STS_SSR:
824            if(p_data->pm_status.value)
825                p_dev->info |= BTA_DM_DI_USE_SSR;
826            else
827                p_dev->info &= ~BTA_DM_DI_USE_SSR;
828            break;
829#endif
830        case BTM_PM_STS_SNIFF:
831            p_dev->info &= ~(BTA_DM_DI_SET_SNIFF|BTA_DM_DI_INT_SNIFF|BTA_DM_DI_ACP_SNIFF);
832            if (info & BTA_DM_DI_SET_SNIFF)
833                p_dev->info |= BTA_DM_DI_INT_SNIFF;
834            else
835                p_dev->info |= BTA_DM_DI_ACP_SNIFF;
836            break;
837
838        case BTM_PM_STS_ERROR:
839            p_dev->info &= ~BTA_DM_DI_SET_SNIFF;
840            break;
841
842        default:
843            break;
844    }
845
846
847
848}
849
850/*******************************************************************************
851**
852** Function         bta_dm_pm_timer
853**
854** Description      Process pm timer event from btm
855**
856**
857** Returns          void
858**
859*******************************************************************************/
860void bta_dm_pm_timer(tBTA_DM_MSG *p_data)
861{
862
863    APPL_TRACE_WARNING0("proc dm_pm_timer expires");
864    bta_dm_pm_set_mode(p_data->pm_status.bd_addr, TRUE);
865
866
867}
868
869/*******************************************************************************
870**
871** Function         bta_dm_find_peer_device
872**
873** Description      Given an address, find the associated control block.
874**
875** Returns          tBTA_DM_PEER_DEVICE
876**
877*******************************************************************************/
878tBTA_DM_PEER_DEVICE * bta_dm_find_peer_device(BD_ADDR peer_addr)
879{
880    tBTA_DM_PEER_DEVICE *p_dev = NULL;
881    int i;
882
883    for(i=0; i<bta_dm_cb.device_list.count; i++)
884    {
885        if(!bdcmp( bta_dm_cb.device_list.peer_device[i].peer_bdaddr, peer_addr))
886        {
887            p_dev = &bta_dm_cb.device_list.peer_device[i];
888            break;
889        }
890
891    }
892    return p_dev;
893}
894
895/*******************************************************************************
896**
897** Function         bta_dm_is_sco_active
898**
899** Description      Loop through connected services for HFP+State=SCO
900**
901** Returns          BOOLEAN. TRUE if SCO active, else FALSE
902**
903*******************************************************************************/
904static BOOLEAN bta_dm_pm_is_sco_active ()
905{
906    int j;
907    BOOLEAN bScoActive = FALSE;
908
909    for(j=0; j<bta_dm_conn_srvcs.count ; j++)
910    {
911        /* check if an entry already present */
912        if ( (bta_dm_conn_srvcs.conn_srvc[j].id == BTA_ID_AG )  && (bta_dm_conn_srvcs.conn_srvc[j].state == BTA_SYS_SCO_OPEN) )
913        {
914            bScoActive = TRUE;
915            break;
916        }
917    }
918
919    APPL_TRACE_DEBUG1("bta_dm_is_sco_active: SCO active: %d", bScoActive);
920    return bScoActive;
921}
922
923
924/*******************************************************************************
925**
926** Function         bta_dm_pm_hid_check
927**
928** Description      Disables/Enables sniff in link policy based on SCO Up/Down
929**
930** Returns          None
931**
932*******************************************************************************/
933
934static void bta_dm_pm_hid_check(BOOLEAN bScoActive)
935{
936    int j;
937
938    /* if HID is active, disable the link policy */
939    for(j=0; j<bta_dm_conn_srvcs.count ; j++)
940    {
941        /* check if an entry already present */
942        if(bta_dm_conn_srvcs.conn_srvc[j].id == BTA_ID_HH )
943        {
944            APPL_TRACE_DEBUG2 ("SCO status change(Active: %d), modify HID link policy. state: %d",
945                bScoActive, bta_dm_conn_srvcs.conn_srvc[j].state);
946            bta_dm_pm_set_sniff_policy( bta_dm_find_peer_device(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr), bScoActive);
947
948            /* if we had disabled link policy, seems like the hid device stop retrying SNIFF after a few tries. force sniff if needed */
949            if (!bScoActive)
950                bta_dm_pm_set_mode(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, FALSE);
951        }
952    }
953
954}
955
956/*******************************************************************************
957**
958** Function         bta_dm_pm_set_sniff_policy
959**
960** Description      Disables/Enables sniff in link policy for the give device
961**
962** Returns          None
963**
964*******************************************************************************/
965static void bta_dm_pm_set_sniff_policy(tBTA_DM_PEER_DEVICE *p_dev, BOOLEAN bDisable)
966{
967    UINT16 policy_setting;
968
969    if (!p_dev)
970        return;
971
972    if (bDisable)
973    {
974        policy_setting = bta_dm_cb.cur_policy &
975            (HCI_ENABLE_MASTER_SLAVE_SWITCH |
976             HCI_ENABLE_HOLD_MODE  |
977             HCI_ENABLE_PARK_MODE);
978
979    }
980    else
981    {
982        /*  allow sniff after sco is closed */
983         policy_setting= bta_dm_cb.cur_policy;
984    }
985
986    /* if disabling SNIFF, make sure link is Active */
987    if (bDisable)
988        bta_dm_pm_active(p_dev->peer_bdaddr);
989
990    /* update device record and set link policy */
991    p_dev->link_policy = policy_setting;
992    BTM_SetLinkPolicy(p_dev->peer_bdaddr, &policy_setting);
993
994}
995