Home | History | Annotate | Download | only in src
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2009-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  *  Filename:      lpm.c
     22  *
     23  *  Description:   Contains low power mode implementation
     24  *
     25  ******************************************************************************/
     26 
     27 #define LOG_TAG "bt_lpm"
     28 
     29 #include <utils/Log.h>
     30 #include <signal.h>
     31 #include <time.h>
     32 #include "bt_hci_bdroid.h"
     33 #include "bt_vendor_lib.h"
     34 #include "bt_utils.h"
     35 #include "vendor.h"
     36 
     37 /******************************************************************************
     38 **  Constants & Macros
     39 ******************************************************************************/
     40 
     41 #ifndef BTLPM_DBG
     42 #define BTLPM_DBG FALSE
     43 #endif
     44 
     45 #if (BTLPM_DBG == TRUE)
     46 #define BTLPMDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
     47 #else
     48 #define BTLPMDBG(param, ...) {}
     49 #endif
     50 
     51 #ifndef DEFAULT_LPM_IDLE_TIMEOUT
     52 #define DEFAULT_LPM_IDLE_TIMEOUT    3000
     53 #endif
     54 
     55 /******************************************************************************
     56 **  Externs
     57 ******************************************************************************/
     58 
     59 /******************************************************************************
     60 **  Local type definitions
     61 ******************************************************************************/
     62 
     63 /* Low power mode state */
     64 enum {
     65     LPM_DISABLED = 0,                    /* initial state */
     66     LPM_ENABLED,
     67     LPM_ENABLING,
     68     LPM_DISABLING
     69 };
     70 
     71 /* LPM WAKE state */
     72 enum {
     73     LPM_WAKE_DEASSERTED = 0,              /* initial state */
     74     LPM_WAKE_W4_TX_DONE,
     75     LPM_WAKE_W4_TIMEOUT,
     76     LPM_WAKE_ASSERTED
     77 };
     78 
     79 /* low power mode control block */
     80 typedef struct
     81 {
     82     uint8_t state;                          /* Low power mode state */
     83     uint8_t wake_state;                     /* LPM WAKE state */
     84     uint8_t no_tx_data;
     85     uint8_t timer_created;
     86     timer_t timer_id;
     87     uint32_t timeout_ms;
     88 } bt_lpm_cb_t;
     89 
     90 
     91 /******************************************************************************
     92 **  Static variables
     93 ******************************************************************************/
     94 
     95 static bt_lpm_cb_t bt_lpm_cb;
     96 
     97 /******************************************************************************
     98 **   LPM Static Functions
     99 ******************************************************************************/
    100 
    101 /*******************************************************************************
    102 **
    103 ** Function        lpm_idle_timeout
    104 **
    105 ** Description     Timeout thread of transport idle timer
    106 **
    107 ** Returns         None
    108 **
    109 *******************************************************************************/
    110 static void lpm_idle_timeout(union sigval arg)
    111 {
    112     UNUSED(arg);
    113     BTLPMDBG("..lpm_idle_timeout..");
    114 
    115     if ((bt_lpm_cb.state == LPM_ENABLED) && \
    116         (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT))
    117     {
    118         bthc_idle_timeout();
    119     }
    120 }
    121 
    122 /*******************************************************************************
    123 **
    124 ** Function        lpm_start_transport_idle_timer
    125 **
    126 ** Description     Launch transport idle timer
    127 **
    128 ** Returns         None
    129 **
    130 *******************************************************************************/
    131 static void lpm_start_transport_idle_timer(void)
    132 {
    133     int status;
    134     struct itimerspec ts;
    135     struct sigevent se;
    136 
    137     if (bt_lpm_cb.state != LPM_ENABLED)
    138         return;
    139 
    140     if (bt_lpm_cb.timer_created == FALSE)
    141     {
    142         se.sigev_notify = SIGEV_THREAD;
    143         se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id;
    144         se.sigev_notify_function = lpm_idle_timeout;
    145         se.sigev_notify_attributes = NULL;
    146 
    147         status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id);
    148 
    149         if (status == 0)
    150             bt_lpm_cb.timer_created = TRUE;
    151     }
    152 
    153     if (bt_lpm_cb.timer_created == TRUE)
    154     {
    155         ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000;
    156         ts.it_value.tv_nsec = 1000000*(bt_lpm_cb.timeout_ms%1000);
    157         ts.it_interval.tv_sec = 0;
    158         ts.it_interval.tv_nsec = 0;
    159 
    160         status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
    161         if (status == -1)
    162             ALOGE("[START] Failed to set LPM idle timeout");
    163     }
    164 }
    165 
    166 /*******************************************************************************
    167 **
    168 ** Function        lpm_stop_transport_idle_timer
    169 **
    170 ** Description     Launch transport idle timer
    171 **
    172 ** Returns         None
    173 **
    174 *******************************************************************************/
    175 static void lpm_stop_transport_idle_timer(void)
    176 {
    177     int status;
    178     struct itimerspec ts;
    179 
    180     if (bt_lpm_cb.timer_created == TRUE)
    181     {
    182         ts.it_value.tv_sec = 0;
    183         ts.it_value.tv_nsec = 0;
    184         ts.it_interval.tv_sec = 0;
    185         ts.it_interval.tv_nsec = 0;
    186 
    187         status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
    188         if (status == -1)
    189             ALOGE("[STOP] Failed to set LPM idle timeout");
    190     }
    191 }
    192 
    193 /*******************************************************************************
    194 **
    195 ** Function         lpm_vnd_cback
    196 **
    197 ** Description      Callback of vendor specific result for lpm enable/disable
    198 **                  rquest
    199 **
    200 ** Returns          None
    201 **
    202 *******************************************************************************/
    203 void lpm_vnd_cback(uint8_t vnd_result)
    204 {
    205     if (vnd_result == 0)
    206     {
    207         /* Status == Success */
    208         bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
    209                           LPM_ENABLED : LPM_DISABLED;
    210     }
    211     else
    212     {
    213         bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
    214                           LPM_DISABLED : LPM_ENABLED;
    215     }
    216 
    217     if (bt_hc_cbacks)
    218     {
    219         if (bt_lpm_cb.state == LPM_ENABLED)
    220             bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
    221         else
    222             bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
    223     }
    224 
    225     if (bt_lpm_cb.state == LPM_DISABLED)
    226     {
    227         if (bt_lpm_cb.timer_created == TRUE)
    228         {
    229             timer_delete(bt_lpm_cb.timer_id);
    230         }
    231 
    232         memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
    233     }
    234 }
    235 
    236 
    237 /*****************************************************************************
    238 **   Low Power Mode Interface Functions
    239 *****************************************************************************/
    240 
    241 /*******************************************************************************
    242 **
    243 ** Function        lpm_init
    244 **
    245 ** Description     Init LPM
    246 **
    247 ** Returns         None
    248 **
    249 *******************************************************************************/
    250 void lpm_init(void)
    251 {
    252     memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
    253     vendor_send_command(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &bt_lpm_cb.timeout_ms);
    254 }
    255 
    256 /*******************************************************************************
    257 **
    258 ** Function        lpm_cleanup
    259 **
    260 ** Description     Clean up
    261 **
    262 ** Returns         None
    263 **
    264 *******************************************************************************/
    265 void lpm_cleanup(void)
    266 {
    267     if (bt_lpm_cb.timer_created == TRUE)
    268     {
    269         timer_delete(bt_lpm_cb.timer_id);
    270     }
    271 }
    272 
    273 /*******************************************************************************
    274 **
    275 ** Function        lpm_enable
    276 **
    277 ** Description     Enalbe/Disable LPM
    278 **
    279 ** Returns         None
    280 **
    281 *******************************************************************************/
    282 void lpm_enable(uint8_t turn_on)
    283 {
    284     if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED))
    285     {
    286         ALOGW("Still busy on processing prior LPM enable/disable request...");
    287         return;
    288     }
    289 
    290     if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED))
    291     {
    292         ALOGI("LPM is already on!!!");
    293         if (bt_hc_cbacks)
    294             bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
    295     }
    296     else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED))
    297     {
    298         ALOGI("LPM is already off!!!");
    299         if (bt_hc_cbacks)
    300             bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
    301     }
    302 
    303     uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
    304     bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING;
    305     vendor_send_command(BT_VND_OP_LPM_SET_MODE, &lpm_cmd);
    306 }
    307 
    308 /*******************************************************************************
    309 **
    310 ** Function          lpm_tx_done
    311 **
    312 ** Description       This function is to inform the lpm module
    313 **                   if data is waiting in the Tx Q or not.
    314 **
    315 **                   IsTxDone: TRUE if All data in the Tx Q are gone
    316 **                             FALSE if any data is still in the Tx Q.
    317 **                   Typicaly this function must be called
    318 **                   before USERIAL Write and in the Tx Done routine
    319 **
    320 ** Returns           None
    321 **
    322 *******************************************************************************/
    323 void lpm_tx_done(uint8_t is_tx_done)
    324 {
    325     bt_lpm_cb.no_tx_data = is_tx_done;
    326 
    327     if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE))
    328     {
    329         bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
    330         lpm_start_transport_idle_timer();
    331     }
    332 }
    333 
    334 /*******************************************************************************
    335 **
    336 ** Function        lpm_wake_assert
    337 **
    338 ** Description     Called to wake up Bluetooth chip.
    339 **                 Normally this is called when there is data to be sent
    340 **                 over UART.
    341 **
    342 ** Returns         TRUE/FALSE
    343 **
    344 *******************************************************************************/
    345 void lpm_wake_assert(void)
    346 {
    347     if (bt_lpm_cb.state != LPM_DISABLED)
    348     {
    349         BTLPMDBG("LPM WAKE assert");
    350 
    351         /* Calling vendor-specific part */
    352         uint8_t state = BT_VND_LPM_WAKE_ASSERT;
    353         vendor_send_command(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
    354 
    355         lpm_stop_transport_idle_timer();
    356 
    357         bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED;
    358     }
    359 
    360     lpm_tx_done(FALSE);
    361 }
    362 
    363 /*******************************************************************************
    364 **
    365 ** Function        lpm_allow_bt_device_sleep
    366 **
    367 ** Description     Start LPM idle timer if allowed
    368 **
    369 ** Returns         None
    370 **
    371 *******************************************************************************/
    372 void lpm_allow_bt_device_sleep(void)
    373 {
    374     if ((bt_lpm_cb.state == LPM_ENABLED) && \
    375         (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED))
    376     {
    377         if(bt_lpm_cb.no_tx_data == TRUE)
    378         {
    379             bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
    380             lpm_start_transport_idle_timer();
    381         }
    382         else
    383         {
    384             bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE;
    385         }
    386     }
    387 }
    388 
    389 /*******************************************************************************
    390 **
    391 ** Function         lpm_wake_deassert
    392 **
    393 ** Description      Deassert wake if allowed
    394 **
    395 ** Returns          None
    396 **
    397 *******************************************************************************/
    398 void lpm_wake_deassert(void)
    399 {
    400     if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE))
    401     {
    402         BTLPMDBG("LPM WAKE deassert");
    403 
    404         uint8_t state = BT_VND_LPM_WAKE_DEASSERT;
    405         vendor_send_command(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
    406         bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED;
    407     }
    408 }
    409