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