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