• 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 #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