• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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