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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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 *******************************************************************************/ 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