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 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 92static void cleanup() { 93 reset_state(); 94 alarm_free(idle_alarm); 95 idle_alarm = NULL; 96} 97 98static 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 107static 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 122static 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 132static 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 154static 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 165static 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 173static void reset_state() { 174 state = LPM_DISABLED; 175 wake_state = LPM_WAKE_DEASSERTED; 176 transmit_is_done = true; 177 stop_idle_timer(); 178} 179 180static 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 185static void start_idle_timer() { 186 if (state == LPM_ENABLED) { 187 alarm_set(idle_alarm, idle_timeout_ms, idle_timer_expired, NULL); 188 } 189} 190 191static void stop_idle_timer() { 192 alarm_cancel(idle_alarm); 193} 194 195static void event_disable(UNUSED_ATTR void *context) { 196 enable(false); 197} 198 199static void event_enable(UNUSED_ATTR void *context) { 200 enable(true); 201} 202 203static void event_wake_assert(UNUSED_ATTR void *context) { 204 wake_assert(); 205} 206 207static void event_allow_device_sleep(UNUSED_ATTR void *context) { 208 allow_device_sleep(); 209} 210 211static void event_idle_timeout(UNUSED_ATTR void *context) { 212 wake_deassert(); 213} 214 215static 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 226static const low_power_manager_t interface = { 227 init, 228 cleanup, 229 post_command, 230 wake_assert, 231 transmit_done 232}; 233 234const low_power_manager_t *low_power_manager_get_interface() { 235 vendor = vendor_get_interface(); 236 return &interface; 237} 238 239const low_power_manager_t *low_power_manager_get_test_interface(const vendor_t *vendor_interface) { 240 vendor = vendor_interface; 241 return &interface; 242} 243