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