Home | History | Annotate | Download | only in src
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2014 Google, Inc.
      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 #define LOG_TAG "bt_low_power_manager"
     20 
     21 #include <assert.h>
     22 #include <stdint.h>
     23 
     24 #include "osi/include/alarm.h"
     25 #include "low_power_manager.h"
     26 #include "osi/include/osi.h"
     27 #include "osi/include/log.h"
     28 #include "osi/include/thread.h"
     29 #include "vendor.h"
     30 
     31 typedef enum {
     32   LPM_DISABLED = 0,
     33   LPM_ENABLED,
     34   LPM_ENABLING,
     35   LPM_DISABLING
     36 } low_power_mode_state_t;
     37 
     38 typedef enum {
     39   LPM_WAKE_DEASSERTED = 0,
     40   LPM_WAKE_W4_TX_DONE,
     41   LPM_WAKE_W4_TIMEOUT,
     42   LPM_WAKE_ASSERTED,
     43 } wake_state_t;
     44 
     45 // Our interface and modules we import
     46 static const low_power_manager_t interface;
     47 static const vendor_t *vendor;
     48 
     49 static void vendor_enable_disable_callback(bool success);
     50 
     51 static void event_disable(void *context);
     52 static void event_enable(void *context);
     53 static void event_wake_assert(void *context);
     54 static void event_allow_device_sleep(void *context);
     55 static void event_idle_timeout(void *context);
     56 
     57 static void reset_state();
     58 static void start_idle_timer();
     59 static void stop_idle_timer();
     60 
     61 static thread_fn event_functions[] = {
     62   event_disable,
     63   event_enable,
     64   event_wake_assert,
     65   event_allow_device_sleep
     66 };
     67 
     68 static thread_t *thread;
     69 static low_power_mode_state_t state;
     70 static wake_state_t wake_state;
     71 static uint32_t idle_timeout_ms;
     72 static alarm_t *idle_alarm;
     73 static bool transmit_is_done;
     74 
     75 // Interface functions
     76 
     77 static void init(thread_t *post_thread) {
     78   assert(post_thread != NULL);
     79   thread = post_thread;
     80 
     81   vendor->set_callback(VENDOR_SET_LPM_MODE, vendor_enable_disable_callback);
     82   vendor->send_command(VENDOR_GET_LPM_IDLE_TIMEOUT, &idle_timeout_ms);
     83 
     84   idle_alarm = alarm_new();
     85   if (!idle_alarm) {
     86     LOG_ERROR("%s could not create idle alarm.", __func__);
     87   }
     88 
     89   reset_state();
     90 }
     91 
     92 static void cleanup() {
     93   reset_state();
     94   alarm_free(idle_alarm);
     95   idle_alarm = NULL;
     96 }
     97 
     98 static void post_command(low_power_command_t command) {
     99   if (command > LPM_WAKE_DEASSERT) {
    100     LOG_ERROR("%s unknown low power command %d", __func__, command);
    101     return;
    102   }
    103 
    104   thread_post(thread, event_functions[command], NULL);
    105 }
    106 
    107 static void wake_assert() {
    108   if (state != LPM_DISABLED) {
    109     stop_idle_timer();
    110 
    111     uint8_t new_state = BT_VND_LPM_WAKE_ASSERT;
    112     vendor->send_command(VENDOR_SET_LPM_WAKE_STATE, &new_state);
    113     wake_state = LPM_WAKE_ASSERTED;
    114   }
    115 
    116   // TODO(zachoverflow): investigate this interaction. If someone above
    117   // HCI asserts wake, we'll wait until we transmit before deasserting.
    118   // That doesn't seem quite right.
    119   transmit_is_done = false;
    120 }
    121 
    122 static void transmit_done() {
    123   transmit_is_done = true;
    124   if (wake_state == LPM_WAKE_W4_TX_DONE) {
    125     wake_state = LPM_WAKE_W4_TIMEOUT;
    126     start_idle_timer();
    127   }
    128 }
    129 
    130 // Internal functions
    131 
    132 static void enable(bool enable) {
    133   if (state == LPM_DISABLING) {
    134     if (enable)
    135       LOG_ERROR("%s still processing prior disable request, cannot enable.", __func__);
    136     else
    137       LOG_WARN("%s still processing prior disable request, ignoring new request to disable.", __func__);
    138   } else if (state == LPM_ENABLING) {
    139     if (enable)
    140       LOG_ERROR("%s still processing prior enable request, ignoring new request to enable.", __func__);
    141     else
    142       LOG_WARN("%s still processing prior enable request, cannot disable.", __func__);
    143   } else if (state == LPM_ENABLED && enable) {
    144     LOG_INFO("%s already enabled.", __func__);
    145   } else if (state == LPM_DISABLED && !enable) {
    146     LOG_INFO("%s already disabled.", __func__);
    147   } else {
    148     uint8_t command = enable ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
    149     state = enable ? LPM_ENABLING : LPM_DISABLING;
    150     vendor->send_async_command(VENDOR_SET_LPM_MODE, &command);
    151   }
    152 }
    153 
    154 static void allow_device_sleep() {
    155   if (state == LPM_ENABLED && wake_state == LPM_WAKE_ASSERTED) {
    156     if (transmit_is_done) {
    157       wake_state = LPM_WAKE_W4_TIMEOUT;
    158       start_idle_timer();
    159     } else {
    160       wake_state = LPM_WAKE_W4_TX_DONE;
    161     }
    162   }
    163 }
    164 
    165 static void wake_deassert() {
    166   if (state == LPM_ENABLED && transmit_is_done) {
    167     uint8_t new_state = BT_VND_LPM_WAKE_DEASSERT;
    168     vendor->send_command(VENDOR_SET_LPM_WAKE_STATE, &new_state);
    169     wake_state = LPM_WAKE_DEASSERTED;
    170   }
    171 }
    172 
    173 static void reset_state() {
    174   state = LPM_DISABLED;
    175   wake_state = LPM_WAKE_DEASSERTED;
    176   transmit_is_done = true;
    177   stop_idle_timer();
    178 }
    179 
    180 static void idle_timer_expired(UNUSED_ATTR void *context) {
    181   if (state == LPM_ENABLED && wake_state == LPM_WAKE_W4_TIMEOUT)
    182     thread_post(thread, event_idle_timeout, NULL);
    183 }
    184 
    185 static void start_idle_timer() {
    186   if (state == LPM_ENABLED) {
    187     alarm_set(idle_alarm, idle_timeout_ms, idle_timer_expired, NULL);
    188   }
    189 }
    190 
    191 static void stop_idle_timer() {
    192   alarm_cancel(idle_alarm);
    193 }
    194 
    195 static void event_disable(UNUSED_ATTR void *context) {
    196   enable(false);
    197 }
    198 
    199 static void event_enable(UNUSED_ATTR void *context) {
    200   enable(true);
    201 }
    202 
    203 static void event_wake_assert(UNUSED_ATTR void *context) {
    204   wake_assert();
    205 }
    206 
    207 static void event_allow_device_sleep(UNUSED_ATTR void *context) {
    208   allow_device_sleep();
    209 }
    210 
    211 static void event_idle_timeout(UNUSED_ATTR void *context) {
    212   wake_deassert();
    213 }
    214 
    215 static void vendor_enable_disable_callback(bool success) {
    216   if (success)
    217     state = (state == LPM_ENABLING) ? LPM_ENABLED : LPM_DISABLED;
    218   else
    219     state = (state == LPM_ENABLING) ? LPM_DISABLED : LPM_ENABLED;
    220 
    221   if (state == LPM_DISABLED) {
    222     reset_state();
    223   }
    224 }
    225 
    226 static const low_power_manager_t interface = {
    227   init,
    228   cleanup,
    229   post_command,
    230   wake_assert,
    231   transmit_done
    232 };
    233 
    234 const low_power_manager_t *low_power_manager_get_interface() {
    235   vendor = vendor_get_interface();
    236   return &interface;
    237 }
    238 
    239 const low_power_manager_t *low_power_manager_get_test_interface(const vendor_t *vendor_interface) {
    240   vendor = vendor_interface;
    241   return &interface;
    242 }
    243