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
35 /******************************************************************************
36 ** Constants & Macros
37 ******************************************************************************/
38
39 #ifndef BTLPM_DBG
40 #define BTLPM_DBG FALSE
41 #endif
42
43 #if (BTLPM_DBG == TRUE)
44 #define BTLPMDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
45 #else
46 #define BTLPMDBG(param, ...) {}
47 #endif
48
49 #ifndef DEFAULT_LPM_IDLE_TIMEOUT
50 #define DEFAULT_LPM_IDLE_TIMEOUT 3000
51 #endif
52
53 /******************************************************************************
54 ** Externs
55 ******************************************************************************/
56
57 extern bt_vendor_interface_t *bt_vnd_if;
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 BTLPMDBG("..lpm_idle_timeout..");
113
114 if ((bt_lpm_cb.state == LPM_ENABLED) && \
115 (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT))
116 {
117 bthc_signal_event(HC_EVENT_LPM_IDLE_TIMEOUT);
118 }
119 }
120
121 /*******************************************************************************
122 **
123 ** Function lpm_start_transport_idle_timer
124 **
125 ** Description Launch transport idle timer
126 **
127 ** Returns None
128 **
129 *******************************************************************************/
lpm_start_transport_idle_timer(void)130 static void lpm_start_transport_idle_timer(void)
131 {
132 int status;
133 struct itimerspec ts;
134 struct sigevent se;
135
136 if (bt_lpm_cb.state != LPM_ENABLED)
137 return;
138
139 if (bt_lpm_cb.timer_created == FALSE)
140 {
141 se.sigev_notify = SIGEV_THREAD;
142 se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id;
143 se.sigev_notify_function = lpm_idle_timeout;
144 se.sigev_notify_attributes = NULL;
145
146 status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id);
147
148 if (status == 0)
149 bt_lpm_cb.timer_created = TRUE;
150 }
151
152 if (bt_lpm_cb.timer_created == TRUE)
153 {
154 ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000;
155 ts.it_value.tv_nsec = 1000*(bt_lpm_cb.timeout_ms%1000);
156 ts.it_interval.tv_sec = 0;
157 ts.it_interval.tv_nsec = 0;
158
159 status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
160 if (status == -1)
161 ALOGE("[START] Failed to set LPM idle timeout");
162 }
163 }
164
165 /*******************************************************************************
166 **
167 ** Function lpm_stop_transport_idle_timer
168 **
169 ** Description Launch transport idle timer
170 **
171 ** Returns None
172 **
173 *******************************************************************************/
lpm_stop_transport_idle_timer(void)174 static void lpm_stop_transport_idle_timer(void)
175 {
176 int status;
177 struct itimerspec ts;
178
179 if (bt_lpm_cb.timer_created == TRUE)
180 {
181 ts.it_value.tv_sec = 0;
182 ts.it_value.tv_nsec = 0;
183 ts.it_interval.tv_sec = 0;
184 ts.it_interval.tv_nsec = 0;
185
186 status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0);
187 if (status == -1)
188 ALOGE("[STOP] Failed to set LPM idle timeout");
189 }
190 }
191
192 /*******************************************************************************
193 **
194 ** Function lpm_vnd_cback
195 **
196 ** Description Callback of vendor specific result for lpm enable/disable
197 ** rquest
198 **
199 ** Returns None
200 **
201 *******************************************************************************/
lpm_vnd_cback(uint8_t vnd_result)202 void lpm_vnd_cback(uint8_t vnd_result)
203 {
204 if (vnd_result == 0)
205 {
206 /* Status == Success */
207 bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
208 LPM_ENABLED : LPM_DISABLED;
209 }
210 else
211 {
212 bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \
213 LPM_DISABLED : LPM_ENABLED;
214 }
215
216 if (bt_hc_cbacks)
217 {
218 if (bt_lpm_cb.state == LPM_ENABLED)
219 bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
220 else
221 bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
222 }
223
224 if (bt_lpm_cb.state == LPM_DISABLED)
225 {
226 if (bt_lpm_cb.timer_created == TRUE)
227 {
228 timer_delete(bt_lpm_cb.timer_id);
229 }
230
231 memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
232 }
233 }
234
235
236 /*****************************************************************************
237 ** Low Power Mode Interface Functions
238 *****************************************************************************/
239
240 /*******************************************************************************
241 **
242 ** Function lpm_init
243 **
244 ** Description Init LPM
245 **
246 ** Returns None
247 **
248 *******************************************************************************/
lpm_init(void)249 void lpm_init(void)
250 {
251 memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t));
252
253 /* Calling vendor-specific part */
254 if (bt_vnd_if)
255 bt_vnd_if->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &(bt_lpm_cb.timeout_ms));
256 else
257 bt_lpm_cb.timeout_ms = DEFAULT_LPM_IDLE_TIMEOUT;
258 }
259
260 /*******************************************************************************
261 **
262 ** Function lpm_cleanup
263 **
264 ** Description Clean up
265 **
266 ** Returns None
267 **
268 *******************************************************************************/
lpm_cleanup(void)269 void lpm_cleanup(void)
270 {
271 if (bt_lpm_cb.timer_created == TRUE)
272 {
273 timer_delete(bt_lpm_cb.timer_id);
274 }
275 }
276
277 /*******************************************************************************
278 **
279 ** Function lpm_enable
280 **
281 ** Description Enalbe/Disable LPM
282 **
283 ** Returns None
284 **
285 *******************************************************************************/
lpm_enable(uint8_t turn_on)286 void lpm_enable(uint8_t turn_on)
287 {
288 if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED))
289 {
290 ALOGW("Still busy on processing prior LPM enable/disable request...");
291 return;
292 }
293
294 if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED))
295 {
296 ALOGI("LPM is already on!!!");
297 if (bt_hc_cbacks)
298 bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED);
299 }
300 else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED))
301 {
302 ALOGI("LPM is already off!!!");
303 if (bt_hc_cbacks)
304 bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED);
305 }
306
307 /* Calling vendor-specific part */
308 if (bt_vnd_if)
309 {
310 uint8_t lpm_cmd = (turn_on) ? BT_VND_LPM_ENABLE : BT_VND_LPM_DISABLE;
311 bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING;
312 bt_vnd_if->op(BT_VND_OP_LPM_SET_MODE, &lpm_cmd);
313 }
314 }
315
316 /*******************************************************************************
317 **
318 ** Function lpm_tx_done
319 **
320 ** Description This function is to inform the lpm module
321 ** if data is waiting in the Tx Q or not.
322 **
323 ** IsTxDone: TRUE if All data in the Tx Q are gone
324 ** FALSE if any data is still in the Tx Q.
325 ** Typicaly this function must be called
326 ** before USERIAL Write and in the Tx Done routine
327 **
328 ** Returns None
329 **
330 *******************************************************************************/
lpm_tx_done(uint8_t is_tx_done)331 void lpm_tx_done(uint8_t is_tx_done)
332 {
333 bt_lpm_cb.no_tx_data = is_tx_done;
334
335 if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE))
336 {
337 bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
338 lpm_start_transport_idle_timer();
339 }
340 }
341
342 /*******************************************************************************
343 **
344 ** Function lpm_wake_assert
345 **
346 ** Description Called to wake up Bluetooth chip.
347 ** Normally this is called when there is data to be sent
348 ** over UART.
349 **
350 ** Returns TRUE/FALSE
351 **
352 *******************************************************************************/
lpm_wake_assert(void)353 void lpm_wake_assert(void)
354 {
355 if (bt_lpm_cb.state != LPM_DISABLED)
356 {
357 BTLPMDBG("LPM WAKE assert");
358
359 /* Calling vendor-specific part */
360 if (bt_vnd_if)
361 {
362 uint8_t state = BT_VND_LPM_WAKE_ASSERT;
363 bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
364 }
365
366 lpm_stop_transport_idle_timer();
367
368 bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED;
369 }
370
371 lpm_tx_done(FALSE);
372 }
373
374 /*******************************************************************************
375 **
376 ** Function lpm_allow_bt_device_sleep
377 **
378 ** Description Start LPM idle timer if allowed
379 **
380 ** Returns None
381 **
382 *******************************************************************************/
lpm_allow_bt_device_sleep(void)383 void lpm_allow_bt_device_sleep(void)
384 {
385 if ((bt_lpm_cb.state == LPM_ENABLED) && \
386 (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED))
387 {
388 if(bt_lpm_cb.no_tx_data == TRUE)
389 {
390 bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT;
391 lpm_start_transport_idle_timer();
392 }
393 else
394 {
395 bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE;
396 }
397 }
398 }
399
400 /*******************************************************************************
401 **
402 ** Function lpm_wake_deassert
403 **
404 ** Description Deassert wake if allowed
405 **
406 ** Returns None
407 **
408 *******************************************************************************/
lpm_wake_deassert(void)409 void lpm_wake_deassert(void)
410 {
411 if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE))
412 {
413 BTLPMDBG("LPM WAKE deassert");
414
415 /* Calling vendor-specific part */
416 if (bt_vnd_if)
417 {
418 uint8_t state = BT_VND_LPM_WAKE_DEASSERT;
419 bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state);
420 }
421
422 bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED;
423 }
424 }
425
426