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   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 
cleanup()92 static void cleanup() {
93   reset_state();
94   alarm_free(idle_alarm);
95   idle_alarm = NULL;
96 }
97 
post_command(low_power_command_t command)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 
wake_assert()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 
transmit_done()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 
enable(bool enable)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 
allow_device_sleep()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 
wake_deassert()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 
reset_state()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 
idle_timer_expired(UNUSED_ATTR void * context)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 
start_idle_timer()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 
stop_idle_timer()191 static void stop_idle_timer() {
192   alarm_cancel(idle_alarm);
193 }
194 
event_disable(UNUSED_ATTR void * context)195 static void event_disable(UNUSED_ATTR void *context) {
196   enable(false);
197 }
198 
event_enable(UNUSED_ATTR void * context)199 static void event_enable(UNUSED_ATTR void *context) {
200   enable(true);
201 }
202 
event_wake_assert(UNUSED_ATTR void * context)203 static void event_wake_assert(UNUSED_ATTR void *context) {
204   wake_assert();
205 }
206 
event_allow_device_sleep(UNUSED_ATTR void * context)207 static void event_allow_device_sleep(UNUSED_ATTR void *context) {
208   allow_device_sleep();
209 }
210 
event_idle_timeout(UNUSED_ATTR void * context)211 static void event_idle_timeout(UNUSED_ATTR void *context) {
212   wake_deassert();
213 }
214 
vendor_enable_disable_callback(bool success)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 
low_power_manager_get_interface()234 const low_power_manager_t *low_power_manager_get_interface() {
235   vendor = vendor_get_interface();
236   return &interface;
237 }
238 
low_power_manager_get_test_interface(const vendor_t * vendor_interface)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