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