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
init(thread_t * post_thread)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
cleanup()93 static void cleanup() {
94 reset_state();
95 alarm_free(idle_alarm);
96 idle_alarm = NULL;
97 }
98
post_command(low_power_command_t command)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
wake_assert()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
transmit_done()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
enable(bool enable)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
allow_device_sleep()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
wake_deassert()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
reset_state()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
idle_timer_expired(UNUSED_ATTR void * context)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
start_idle_timer()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
stop_idle_timer()198 static void stop_idle_timer() {
199 alarm_cancel(idle_alarm);
200 }
201
event_disable(UNUSED_ATTR void * context)202 static void event_disable(UNUSED_ATTR void *context) {
203 enable(false);
204 }
205
event_enable(UNUSED_ATTR void * context)206 static void event_enable(UNUSED_ATTR void *context) {
207 enable(true);
208 }
209
event_wake_assert(UNUSED_ATTR void * context)210 static void event_wake_assert(UNUSED_ATTR void *context) {
211 wake_assert();
212 }
213
event_allow_device_sleep(UNUSED_ATTR void * context)214 static void event_allow_device_sleep(UNUSED_ATTR void *context) {
215 allow_device_sleep();
216 }
217
event_idle_timeout(UNUSED_ATTR void * context)218 static void event_idle_timeout(UNUSED_ATTR void *context) {
219 wake_deassert();
220 }
221
vendor_enable_disable_callback(bool success)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
low_power_manager_get_interface()241 const low_power_manager_t *low_power_manager_get_interface() {
242 vendor = vendor_get_interface();
243 return &interface;
244 }
245
low_power_manager_get_test_interface(const vendor_t * vendor_interface)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