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