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 
     35 /******************************************************************************
     36 **  Constants & Macros
     37 ******************************************************************************/
     38 
     39 #ifndef BTLPM_DBG
     40 #define BTLPM_DBG FALSE
     41 #endif
     42 
     43 #if (BTLPM_DBG == TRUE)
     44 #define BTLPMDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
     45 #else
     46 #define BTLPMDBG(param, ...) {}
     47 #endif
     48 
     49 #ifndef DEFAULT_LPM_IDLE_TIMEOUT
     50 #define DEFAULT_LPM_IDLE_TIMEOUT    3000
     51 #endif
     52 
     53 /******************************************************************************
     54 **  Externs
     55 ******************************************************************************/
     56 
     57 extern bt_vendor_interface_t *bt_vnd_if;
     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     BTLPMDBG("..lpm_idle_timeout..");
    113 
    114     if ((bt_lpm_cb.state == LPM_ENABLED) && \
    115         (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT))
    116     {
    117         bthc_signal_event(HC_EVENT_LPM_IDLE_TIMEOUT);
    118     }
    119 }
    120 
    121 /*******************************************************************************
    122 **
    123 ** Function        lpm_start_transport_idle_timer
    124 **
    125 ** Description     Launch transport idle timer
    126 **
    127 ** Returns         None
    128 **
    129 *******************************************************************************/
    130 static void lpm_start_transport_idle_timer(void)
    131 {
    132     int status;
    133     struct itimerspec ts;
    134     struct sigevent se;
    135 
    136     if (bt_lpm_cb.state != LPM_ENABLED)
    137         return;
    138 
    139     if (bt_lpm_cb.timer_created == FALSE)
    140     {
    141         se.sigev_notify = SIGEV_THREAD;
    142         se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id;
    143         se.sigev_notify_function = lpm_idle_timeout;
    144         se.sigev_notify_attributes = NULL;
    145 
    146         status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id);
    147 
    148         if (status == 0)
    149             bt_lpm_cb.timer_created = TRUE;
    150     }
    151 
    152     if (bt_lpm_cb.timer_created == TRUE)
    153     {
    154         ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000;
    155         ts.it_value.tv_nsec = 1000*(bt_lpm_cb.timeout_ms%1000);
    156         ts.it_interval.tv_sec = 0;
    157         ts.it_interval.tv_nsec = 0;
    158 
    159         status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
    160         if (status == -1)
    161             ALOGE("[START] Failed to set LPM idle timeout");
    162     }
    163 }
    164 
    165 /*******************************************************************************
    166 **
    167 ** Function        lpm_stop_transport_idle_timer
    168 **
    169 ** Description     Launch transport idle timer
    170 **
    171 ** Returns         None
    172 **
    173 *******************************************************************************/
    174 static void lpm_stop_transport_idle_timer(void)
    175 {
    176     int status;
    177     struct itimerspec ts;
    178 
    179     if (bt_lpm_cb.timer_created == TRUE)
    180     {
    181         ts.it_value.tv_sec = 0;
    182         ts.it_value.tv_nsec = 0;
    183         ts.it_interval.tv_sec = 0;
    184         ts.it_interval.tv_nsec = 0;
    185 
    186         status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
    187         if (status == -1)
    188             ALOGE("[STOP] Failed to set LPM idle timeout");
    189     }
    190 }
    191 
    192 /*******************************************************************************
    193 **
    194 ** Function         lpm_vnd_cback
    195 **
    196 ** Description      Callback of vendor specific result for lpm enable/disable
    197 **                  rquest
    198 **
    199 ** Returns          None
    200 **
    201 *******************************************************************************/
    202 void lpm_vnd_cback(uint8_t vnd_result)
    203 {
    204     if (vnd_result == 0)
    205     {
    206         /* Status == Success */
    207         bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
    208                           LPM_ENABLED : LPM_DISABLED;
    209     }
    210     else
    211     {
    212         bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
    213                           LPM_DISABLED : LPM_ENABLED;
    214     }
    215 
    216     if (bt_hc_cbacks)
    217     {
    218         if (bt_lpm_cb.state == LPM_ENABLED)
    219             bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
    220         else
    221             bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
    222     }
    223 
    224     if (bt_lpm_cb.state == LPM_DISABLED)
    225     {
    226         if (bt_lpm_cb.timer_created == TRUE)
    227         {
    228             timer_delete(bt_lpm_cb.timer_id);
    229         }
    230 
    231         memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
    232     }
    233 }
    234 
    235 
    236 /*****************************************************************************
    237 **   Low Power Mode Interface Functions
    238 *****************************************************************************/
    239 
    240 /*******************************************************************************
    241 **
    242 ** Function        lpm_init
    243 **
    244 ** Description     Init LPM
    245 **
    246 ** Returns         None
    247 **
    248 *******************************************************************************/
    249 void lpm_init(void)
    250 {
    251     memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
    252 
    253     /* Calling vendor-specific part */
    254     if (bt_vnd_if)
    255         bt_vnd_if->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &(bt_lpm_cb.timeout_ms));
    256     else
    257         bt_lpm_cb.timeout_ms = DEFAULT_LPM_IDLE_TIMEOUT;
    258 }
    259 
    260 /*******************************************************************************
    261 **
    262 ** Function        lpm_cleanup
    263 **
    264 ** Description     Clean up
    265 **
    266 ** Returns         None
    267 **
    268 *******************************************************************************/
    269 void lpm_cleanup(void)
    270 {
    271     if (bt_lpm_cb.timer_created == TRUE)
    272     {
    273         timer_delete(bt_lpm_cb.timer_id);
    274     }
    275 }
    276 
    277 /*******************************************************************************
    278 **
    279 ** Function        lpm_enable
    280 **
    281 ** Description     Enalbe/Disable LPM
    282 **
    283 ** Returns         None
    284 **
    285 *******************************************************************************/
    286 void lpm_enable(uint8_t turn_on)
    287 {
    288     if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED))
    289     {
    290         ALOGW("Still busy on processing prior LPM enable/disable request...");
    291         return;
    292     }
    293 
    294     if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED))
    295     {
    296         ALOGI("LPM is already on!!!");
    297         if (bt_hc_cbacks)
    298             bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
    299     }
    300     else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED))
    301     {
    302         ALOGI("LPM is already off!!!");
    303         if (bt_hc_cbacks)
    304             bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
    305     }
    306 
    307     /* Calling vendor-specific part */
    308     if (bt_vnd_if)
    309     {
    310         uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
    311         bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING;
    312         bt_vnd_if->op(BT_VND_OP_LPM_SET_MODE, &lpm_cmd);
    313     }
    314 }
    315 
    316 /*******************************************************************************
    317 **
    318 ** Function          lpm_tx_done
    319 **
    320 ** Description       This function is to inform the lpm module
    321 **                   if data is waiting in the Tx Q or not.
    322 **
    323 **                   IsTxDone: TRUE if All data in the Tx Q are gone
    324 **                             FALSE if any data is still in the Tx Q.
    325 **                   Typicaly this function must be called
    326 **                   before USERIAL Write and in the Tx Done routine
    327 **
    328 ** Returns           None
    329 **
    330 *******************************************************************************/
    331 void lpm_tx_done(uint8_t is_tx_done)
    332 {
    333     bt_lpm_cb.no_tx_data = is_tx_done;
    334 
    335     if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE))
    336     {
    337         bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
    338         lpm_start_transport_idle_timer();
    339     }
    340 }
    341 
    342 /*******************************************************************************
    343 **
    344 ** Function        lpm_wake_assert
    345 **
    346 ** Description     Called to wake up Bluetooth chip.
    347 **                 Normally this is called when there is data to be sent
    348 **                 over UART.
    349 **
    350 ** Returns         TRUE/FALSE
    351 **
    352 *******************************************************************************/
    353 void lpm_wake_assert(void)
    354 {
    355     if (bt_lpm_cb.state != LPM_DISABLED)
    356     {
    357         BTLPMDBG("LPM WAKE assert");
    358 
    359         /* Calling vendor-specific part */
    360         if (bt_vnd_if)
    361         {
    362             uint8_t state = BT_VND_LPM_WAKE_ASSERT;
    363             bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
    364         }
    365 
    366         lpm_stop_transport_idle_timer();
    367 
    368         bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED;
    369     }
    370 
    371     lpm_tx_done(FALSE);
    372 }
    373 
    374 /*******************************************************************************
    375 **
    376 ** Function        lpm_allow_bt_device_sleep
    377 **
    378 ** Description     Start LPM idle timer if allowed
    379 **
    380 ** Returns         None
    381 **
    382 *******************************************************************************/
    383 void lpm_allow_bt_device_sleep(void)
    384 {
    385     if ((bt_lpm_cb.state == LPM_ENABLED) && \
    386         (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED))
    387     {
    388         if(bt_lpm_cb.no_tx_data == TRUE)
    389         {
    390             bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
    391             lpm_start_transport_idle_timer();
    392         }
    393         else
    394         {
    395             bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE;
    396         }
    397     }
    398 }
    399 
    400 /*******************************************************************************
    401 **
    402 ** Function         lpm_wake_deassert
    403 **
    404 ** Description      Deassert wake if allowed
    405 **
    406 ** Returns          None
    407 **
    408 *******************************************************************************/
    409 void lpm_wake_deassert(void)
    410 {
    411     if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE))
    412     {
    413         BTLPMDBG("LPM WAKE deassert");
    414 
    415         /* Calling vendor-specific part */
    416         if (bt_vnd_if)
    417         {
    418             uint8_t state = BT_VND_LPM_WAKE_DEASSERT;
    419             bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
    420         }
    421 
    422         bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED;
    423     }
    424 }
    425 
    426