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