1 /******************************************************************************
2 *
3 * Copyright (C) 2009-2012 Broadcom Corporation
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 /******************************************************************************
20 *
21 * Filename: lpm.c
22 *
23 * Description: Contains low power mode implementation
24 *
25 ******************************************************************************/
26
27 #define LOG_TAG "bt_lpm"
28
29 #include <utils/Log.h>
30 #include <signal.h>
31 #include <time.h>
32 #include "bt_hci_bdroid.h"
33 #include "bt_vendor_lib.h"
34 #include "bt_utils.h"
35 #include "vendor.h"
36
37 /******************************************************************************
38 ** Constants & Macros
39 ******************************************************************************/
40
41 #ifndef BTLPM_DBG
42 #define BTLPM_DBG FALSE
43 #endif
44
45 #if (BTLPM_DBG == TRUE)
46 #define BTLPMDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
47 #else
48 #define BTLPMDBG(param, ...) {}
49 #endif
50
51 #ifndef DEFAULT_LPM_IDLE_TIMEOUT
52 #define DEFAULT_LPM_IDLE_TIMEOUT 3000
53 #endif
54
55 /******************************************************************************
56 ** Externs
57 ******************************************************************************/
58
59 /******************************************************************************
60 ** Local type definitions
61 ******************************************************************************/
62
63 /* Low power mode state */
64 enum {
65 LPM_DISABLED = 0, /* initial state */
66 LPM_ENABLED,
67 LPM_ENABLING,
68 LPM_DISABLING
69 };
70
71 /* LPM WAKE state */
72 enum {
73 LPM_WAKE_DEASSERTED = 0, /* initial state */
74 LPM_WAKE_W4_TX_DONE,
75 LPM_WAKE_W4_TIMEOUT,
76 LPM_WAKE_ASSERTED
77 };
78
79 /* low power mode control block */
80 typedef struct
81 {
82 uint8_t state; /* Low power mode state */
83 uint8_t wake_state; /* LPM WAKE state */
84 uint8_t no_tx_data;
85 uint8_t timer_created;
86 timer_t timer_id;
87 uint32_t timeout_ms;
88 } bt_lpm_cb_t;
89
90
91 /******************************************************************************
92 ** Static variables
93 ******************************************************************************/
94
95 static bt_lpm_cb_t bt_lpm_cb;
96
97 /******************************************************************************
98 ** LPM Static Functions
99 ******************************************************************************/
100
101 /*******************************************************************************
102 **
103 ** Function lpm_idle_timeout
104 **
105 ** Description Timeout thread of transport idle timer
106 **
107 ** Returns None
108 **
109 *******************************************************************************/
lpm_idle_timeout(union sigval arg)110 static void lpm_idle_timeout(union sigval arg)
111 {
112 UNUSED(arg);
113 BTLPMDBG("..lpm_idle_timeout..");
114
115 if ((bt_lpm_cb.state == LPM_ENABLED) && \
116 (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT))
117 {
118 bthc_idle_timeout();
119 }
120 }
121
122 /*******************************************************************************
123 **
124 ** Function lpm_start_transport_idle_timer
125 **
126 ** Description Launch transport idle timer
127 **
128 ** Returns None
129 **
130 *******************************************************************************/
lpm_start_transport_idle_timer(void)131 static void lpm_start_transport_idle_timer(void)
132 {
133 int status;
134 struct itimerspec ts;
135 struct sigevent se;
136
137 if (bt_lpm_cb.state != LPM_ENABLED)
138 return;
139
140 if (bt_lpm_cb.timer_created == FALSE)
141 {
142 se.sigev_notify = SIGEV_THREAD;
143 se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id;
144 se.sigev_notify_function = lpm_idle_timeout;
145 se.sigev_notify_attributes = NULL;
146
147 status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id);
148
149 if (status == 0)
150 bt_lpm_cb.timer_created = TRUE;
151 }
152
153 if (bt_lpm_cb.timer_created == TRUE)
154 {
155 ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000;
156 ts.it_value.tv_nsec = 1000000*(bt_lpm_cb.timeout_ms%1000);
157 ts.it_interval.tv_sec = 0;
158 ts.it_interval.tv_nsec = 0;
159
160 status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
161 if (status == -1)
162 ALOGE("[START] Failed to set LPM idle timeout");
163 }
164 }
165
166 /*******************************************************************************
167 **
168 ** Function lpm_stop_transport_idle_timer
169 **
170 ** Description Launch transport idle timer
171 **
172 ** Returns None
173 **
174 *******************************************************************************/
lpm_stop_transport_idle_timer(void)175 static void lpm_stop_transport_idle_timer(void)
176 {
177 int status;
178 struct itimerspec ts;
179
180 if (bt_lpm_cb.timer_created == TRUE)
181 {
182 ts.it_value.tv_sec = 0;
183 ts.it_value.tv_nsec = 0;
184 ts.it_interval.tv_sec = 0;
185 ts.it_interval.tv_nsec = 0;
186
187 status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
188 if (status == -1)
189 ALOGE("[STOP] Failed to set LPM idle timeout");
190 }
191 }
192
193 /*******************************************************************************
194 **
195 ** Function lpm_vnd_cback
196 **
197 ** Description Callback of vendor specific result for lpm enable/disable
198 ** rquest
199 **
200 ** Returns None
201 **
202 *******************************************************************************/
lpm_vnd_cback(uint8_t vnd_result)203 void lpm_vnd_cback(uint8_t vnd_result)
204 {
205 if (vnd_result == 0)
206 {
207 /* Status == Success */
208 bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
209 LPM_ENABLED : LPM_DISABLED;
210 }
211 else
212 {
213 bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
214 LPM_DISABLED : LPM_ENABLED;
215 }
216
217 if (bt_hc_cbacks)
218 {
219 if (bt_lpm_cb.state == LPM_ENABLED)
220 bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
221 else
222 bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
223 }
224
225 if (bt_lpm_cb.state == LPM_DISABLED)
226 {
227 if (bt_lpm_cb.timer_created == TRUE)
228 {
229 timer_delete(bt_lpm_cb.timer_id);
230 }
231
232 memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
233 }
234 }
235
236
237 /*****************************************************************************
238 ** Low Power Mode Interface Functions
239 *****************************************************************************/
240
241 /*******************************************************************************
242 **
243 ** Function lpm_init
244 **
245 ** Description Init LPM
246 **
247 ** Returns None
248 **
249 *******************************************************************************/
lpm_init(void)250 void lpm_init(void)
251 {
252 memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
253 vendor_send_command(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &bt_lpm_cb.timeout_ms);
254 }
255
256 /*******************************************************************************
257 **
258 ** Function lpm_cleanup
259 **
260 ** Description Clean up
261 **
262 ** Returns None
263 **
264 *******************************************************************************/
lpm_cleanup(void)265 void lpm_cleanup(void)
266 {
267 if (bt_lpm_cb.timer_created == TRUE)
268 {
269 timer_delete(bt_lpm_cb.timer_id);
270 }
271 }
272
273 /*******************************************************************************
274 **
275 ** Function lpm_enable
276 **
277 ** Description Enalbe/Disable LPM
278 **
279 ** Returns None
280 **
281 *******************************************************************************/
lpm_enable(uint8_t turn_on)282 void lpm_enable(uint8_t turn_on)
283 {
284 if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED))
285 {
286 ALOGW("Still busy on processing prior LPM enable/disable request...");
287 return;
288 }
289
290 if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED))
291 {
292 ALOGI("LPM is already on!!!");
293 if (bt_hc_cbacks)
294 bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
295 }
296 else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED))
297 {
298 ALOGI("LPM is already off!!!");
299 if (bt_hc_cbacks)
300 bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
301 }
302
303 uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
304 bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING;
305 vendor_send_command(BT_VND_OP_LPM_SET_MODE, &lpm_cmd);
306 }
307
308 /*******************************************************************************
309 **
310 ** Function lpm_tx_done
311 **
312 ** Description This function is to inform the lpm module
313 ** if data is waiting in the Tx Q or not.
314 **
315 ** IsTxDone: TRUE if All data in the Tx Q are gone
316 ** FALSE if any data is still in the Tx Q.
317 ** Typicaly this function must be called
318 ** before USERIAL Write and in the Tx Done routine
319 **
320 ** Returns None
321 **
322 *******************************************************************************/
lpm_tx_done(uint8_t is_tx_done)323 void lpm_tx_done(uint8_t is_tx_done)
324 {
325 bt_lpm_cb.no_tx_data = is_tx_done;
326
327 if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE))
328 {
329 bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
330 lpm_start_transport_idle_timer();
331 }
332 }
333
334 /*******************************************************************************
335 **
336 ** Function lpm_wake_assert
337 **
338 ** Description Called to wake up Bluetooth chip.
339 ** Normally this is called when there is data to be sent
340 ** over UART.
341 **
342 ** Returns TRUE/FALSE
343 **
344 *******************************************************************************/
lpm_wake_assert(void)345 void lpm_wake_assert(void)
346 {
347 if (bt_lpm_cb.state != LPM_DISABLED)
348 {
349 BTLPMDBG("LPM WAKE assert");
350
351 /* Calling vendor-specific part */
352 uint8_t state = BT_VND_LPM_WAKE_ASSERT;
353 vendor_send_command(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
354
355 lpm_stop_transport_idle_timer();
356
357 bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED;
358 }
359
360 lpm_tx_done(FALSE);
361 }
362
363 /*******************************************************************************
364 **
365 ** Function lpm_allow_bt_device_sleep
366 **
367 ** Description Start LPM idle timer if allowed
368 **
369 ** Returns None
370 **
371 *******************************************************************************/
lpm_allow_bt_device_sleep(void)372 void lpm_allow_bt_device_sleep(void)
373 {
374 if ((bt_lpm_cb.state == LPM_ENABLED) && \
375 (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED))
376 {
377 if(bt_lpm_cb.no_tx_data == TRUE)
378 {
379 bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
380 lpm_start_transport_idle_timer();
381 }
382 else
383 {
384 bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE;
385 }
386 }
387 }
388
389 /*******************************************************************************
390 **
391 ** Function lpm_wake_deassert
392 **
393 ** Description Deassert wake if allowed
394 **
395 ** Returns None
396 **
397 *******************************************************************************/
lpm_wake_deassert(void)398 void lpm_wake_deassert(void)
399 {
400 if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE))
401 {
402 BTLPMDBG("LPM WAKE deassert");
403
404 uint8_t state = BT_VND_LPM_WAKE_DEASSERT;
405 vendor_send_command(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
406 bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED;
407 }
408 }
409