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