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