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
31typedef enum {
32  LPM_DISABLED = 0,
33  LPM_ENABLED,
34  LPM_ENABLING,
35  LPM_DISABLING
36} low_power_mode_state_t;
37
38typedef 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
46static const low_power_manager_t interface;
47static const vendor_t *vendor;
48
49static void vendor_enable_disable_callback(bool success);
50
51static void event_disable(void *context);
52static void event_enable(void *context);
53static void event_wake_assert(void *context);
54static void event_allow_device_sleep(void *context);
55static void event_idle_timeout(void *context);
56
57static void reset_state();
58static void start_idle_timer();
59static void stop_idle_timer();
60
61static thread_fn event_functions[] = {
62  event_disable,
63  event_enable,
64  event_wake_assert,
65  event_allow_device_sleep
66};
67
68static thread_t *thread;
69static low_power_mode_state_t state;
70static wake_state_t wake_state;
71static uint32_t idle_timeout_ms;
72static alarm_t *idle_alarm;
73static bool transmit_is_done;
74
75// Interface functions
76
77static 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
92static void cleanup() {
93  reset_state();
94  alarm_free(idle_alarm);
95  idle_alarm = NULL;
96}
97
98static 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
107static 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
122static 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
132static 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
154static 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
165static 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
173static void reset_state() {
174  state = LPM_DISABLED;
175  wake_state = LPM_WAKE_DEASSERTED;
176  transmit_is_done = true;
177  stop_idle_timer();
178}
179
180static 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
185static void start_idle_timer() {
186  if (state == LPM_ENABLED) {
187    alarm_set(idle_alarm, idle_timeout_ms, idle_timer_expired, NULL);
188  }
189}
190
191static void stop_idle_timer() {
192  alarm_cancel(idle_alarm);
193}
194
195static void event_disable(UNUSED_ATTR void *context) {
196  enable(false);
197}
198
199static void event_enable(UNUSED_ATTR void *context) {
200  enable(true);
201}
202
203static void event_wake_assert(UNUSED_ATTR void *context) {
204  wake_assert();
205}
206
207static void event_allow_device_sleep(UNUSED_ATTR void *context) {
208  allow_device_sleep();
209}
210
211static void event_idle_timeout(UNUSED_ATTR void *context) {
212  wake_deassert();
213}
214
215static 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
226static const low_power_manager_t interface = {
227  init,
228  cleanup,
229  post_command,
230  wake_assert,
231  transmit_done
232};
233
234const low_power_manager_t *low_power_manager_get_interface() {
235  vendor = vendor_get_interface();
236  return &interface;
237}
238
239const low_power_manager_t *low_power_manager_get_test_interface(const vendor_t *vendor_interface) {
240  vendor = vendor_interface;
241  return &interface;
242}
243