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: upio.c 22 * 23 * Description: Contains I/O functions, like 24 * rfkill control 25 * BT_WAKE/HOST_WAKE control 26 * 27 ******************************************************************************/ 28 29#define LOG_TAG "bt_upio" 30 31#include <utils/Log.h> 32#include <fcntl.h> 33#include <errno.h> 34#include <string.h> 35#include <cutils/properties.h> 36#include "bt_vendor_brcm.h" 37#include "upio.h" 38#include "userial_vendor.h" 39 40/****************************************************************************** 41** Constants & Macros 42******************************************************************************/ 43 44#ifndef UPIO_DBG 45#define UPIO_DBG FALSE 46#endif 47 48#if (UPIO_DBG == TRUE) 49#define UPIODBG(param, ...) {ALOGD(param, ## __VA_ARGS__);} 50#else 51#define UPIODBG(param, ...) {} 52#endif 53 54/****************************************************************************** 55** Local type definitions 56******************************************************************************/ 57 58#if (BT_WAKE_VIA_PROC == TRUE) 59 60/* proc fs node for enable/disable lpm mode */ 61#ifndef VENDOR_LPM_PROC_NODE 62#define VENDOR_LPM_PROC_NODE "/proc/bluetooth/sleep/lpm" 63#endif 64 65/* proc fs node for notifying write request */ 66#ifndef VENDOR_BTWRITE_PROC_NODE 67#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite" 68#endif 69 70/* 71 * Maximum btwrite assertion holding time without consecutive btwrite kicking. 72 * This value is correlative(shorter) to the in-activity timeout period set in 73 * the bluesleep LPM code. The current value used in bluesleep is 10sec. 74 */ 75#ifndef PROC_BTWRITE_TIMER_TIMEOUT_MS 76#define PROC_BTWRITE_TIMER_TIMEOUT_MS 8000 77#endif 78 79/* lpm proc control block */ 80typedef struct 81{ 82 uint8_t btwrite_active; 83 uint8_t timer_created; 84 timer_t timer_id; 85 uint32_t timeout_ms; 86} vnd_lpm_proc_cb_t; 87 88static vnd_lpm_proc_cb_t lpm_proc_cb; 89#endif 90 91/****************************************************************************** 92** Static variables 93******************************************************************************/ 94 95static uint8_t upio_state[UPIO_MAX_COUNT]; 96static int rfkill_id = -1; 97static int bt_emul_enable = 0; 98static char *rfkill_state_path = NULL; 99 100/****************************************************************************** 101** Static functions 102******************************************************************************/ 103 104/* for friendly debugging outpout string */ 105static char *lpm_mode[] = { 106 "UNKNOWN", 107 "disabled", 108 "enabled" 109}; 110 111static char *lpm_state[] = { 112 "UNKNOWN", 113 "de-asserted", 114 "asserted" 115}; 116 117/***************************************************************************** 118** Bluetooth On/Off Static Functions 119*****************************************************************************/ 120static int is_emulator_context(void) 121{ 122 char value[PROPERTY_VALUE_MAX]; 123 124 property_get("ro.kernel.qemu", value, "0"); 125 UPIODBG("is_emulator_context : %s", value); 126 if (strcmp(value, "1") == 0) { 127 return 1; 128 } 129 return 0; 130} 131 132static int is_rfkill_disabled(void) 133{ 134 char value[PROPERTY_VALUE_MAX]; 135 136 property_get("ro.rfkilldisabled", value, "0"); 137 UPIODBG("is_rfkill_disabled ? [%s]", value); 138 139 if (strcmp(value, "1") == 0) { 140 return UPIO_BT_POWER_ON; 141 } 142 143 return UPIO_BT_POWER_OFF; 144} 145 146static int init_rfkill() 147{ 148 char path[64]; 149 char buf[16]; 150 int fd, sz, id; 151 152 if (is_rfkill_disabled()) 153 return -1; 154 155 for (id = 0; ; id++) 156 { 157 snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); 158 fd = open(path, O_RDONLY); 159 if (fd < 0) 160 { 161 ALOGE("init_rfkill : open(%s) failed: %s (%d)\n", \ 162 path, strerror(errno), errno); 163 return -1; 164 } 165 166 sz = read(fd, &buf, sizeof(buf)); 167 close(fd); 168 169 if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) 170 { 171 rfkill_id = id; 172 break; 173 } 174 } 175 176 asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id); 177 return 0; 178} 179 180/***************************************************************************** 181** LPM Static Functions 182*****************************************************************************/ 183 184#if (BT_WAKE_VIA_PROC == TRUE) 185/******************************************************************************* 186** 187** Function proc_btwrite_timeout 188** 189** Description Timeout thread of proc/.../btwrite assertion holding timer 190** 191** Returns None 192** 193*******************************************************************************/ 194static void proc_btwrite_timeout(union sigval arg) 195{ 196 UPIODBG("..%s..", __FUNCTION__); 197 lpm_proc_cb.btwrite_active = FALSE; 198 /* drive LPM down; this timer should fire only when BT is awake; */ 199 upio_set(UPIO_BT_WAKE, UPIO_DEASSERT, 1); 200} 201 202/****************************************************************************** 203 ** 204 ** Function upio_start_stop_timer 205 ** 206 ** Description Arm user space timer in case lpm is left asserted 207 ** 208 ** Returns None 209 ** 210 *****************************************************************************/ 211void upio_start_stop_timer(int action) { 212 struct itimerspec ts; 213 214 if (action == UPIO_ASSERT) { 215 lpm_proc_cb.btwrite_active = TRUE; 216 if (lpm_proc_cb.timer_created == TRUE) { 217 ts.it_value.tv_sec = PROC_BTWRITE_TIMER_TIMEOUT_MS/1000; 218 ts.it_value.tv_nsec = 1000000*(PROC_BTWRITE_TIMER_TIMEOUT_MS%1000); 219 ts.it_interval.tv_sec = 0; 220 ts.it_interval.tv_nsec = 0; 221 } 222 } else { 223 /* unarm timer if writing 0 to lpm; reduce unnecessary user space wakeup */ 224 memset(&ts, 0, sizeof(ts)); 225 } 226 227 if (timer_settime(lpm_proc_cb.timer_id, 0, &ts, 0) == 0) { 228 UPIODBG("%s : timer_settime success", __FUNCTION__); 229 } else { 230 UPIODBG("%s : timer_settime failed", __FUNCTION__); 231 } 232} 233#endif 234 235/***************************************************************************** 236** UPIO Interface Functions 237*****************************************************************************/ 238 239/******************************************************************************* 240** 241** Function upio_init 242** 243** Description Initialization 244** 245** Returns None 246** 247*******************************************************************************/ 248void upio_init(void) 249{ 250 memset(upio_state, UPIO_UNKNOWN, UPIO_MAX_COUNT); 251#if (BT_WAKE_VIA_PROC == TRUE) 252 memset(&lpm_proc_cb, 0, sizeof(vnd_lpm_proc_cb_t)); 253#endif 254} 255 256/******************************************************************************* 257** 258** Function upio_cleanup 259** 260** Description Clean up 261** 262** Returns None 263** 264*******************************************************************************/ 265void upio_cleanup(void) 266{ 267#if (BT_WAKE_VIA_PROC == TRUE) 268 if (lpm_proc_cb.timer_created == TRUE) 269 timer_delete(lpm_proc_cb.timer_id); 270 271 lpm_proc_cb.timer_created = FALSE; 272#endif 273} 274 275/******************************************************************************* 276** 277** Function upio_set_bluetooth_power 278** 279** Description Interact with low layer driver to set Bluetooth power 280** on/off. 281** 282** Returns 0 : SUCCESS or Not-Applicable 283** <0 : ERROR 284** 285*******************************************************************************/ 286int upio_set_bluetooth_power(int on) 287{ 288 int sz; 289 int fd = -1; 290 int ret = -1; 291 char buffer = '0'; 292 293 switch(on) 294 { 295 case UPIO_BT_POWER_OFF: 296 buffer = '0'; 297 break; 298 299 case UPIO_BT_POWER_ON: 300 buffer = '1'; 301 break; 302 } 303 304 if (is_emulator_context()) 305 { 306 /* if new value is same as current, return -1 */ 307 if (bt_emul_enable == on) 308 return ret; 309 310 UPIODBG("set_bluetooth_power [emul] %d", on); 311 312 bt_emul_enable = on; 313 return 0; 314 } 315 316 /* check if we have rfkill interface */ 317 if (is_rfkill_disabled()) 318 return 0; 319 320 if (rfkill_id == -1) 321 { 322 if (init_rfkill()) 323 return ret; 324 } 325 326 fd = open(rfkill_state_path, O_WRONLY); 327 328 if (fd < 0) 329 { 330 ALOGE("set_bluetooth_power : open(%s) for write failed: %s (%d)", 331 rfkill_state_path, strerror(errno), errno); 332 return ret; 333 } 334 335 sz = write(fd, &buffer, 1); 336 337 if (sz < 0) { 338 ALOGE("set_bluetooth_power : write(%s) failed: %s (%d)", 339 rfkill_state_path, strerror(errno),errno); 340 } 341 else 342 ret = 0; 343 344 if (fd >= 0) 345 close(fd); 346 347 return ret; 348} 349 350 351/******************************************************************************* 352** 353** Function upio_set 354** 355** Description Set i/o based on polarity 356** 357** Returns None 358** 359*******************************************************************************/ 360void upio_set(uint8_t pio, uint8_t action, uint8_t polarity) 361{ 362 int rc; 363#if (BT_WAKE_VIA_PROC == TRUE) 364 int fd = -1; 365 char buffer; 366#endif 367 368 UPIODBG("%s : pio %d action %d, polarity %d", __FUNCTION__, pio, action, polarity); 369 370 switch (pio) 371 { 372 case UPIO_LPM_MODE: 373 if (upio_state[UPIO_LPM_MODE] == action) 374 { 375 UPIODBG("LPM is %s already", lpm_mode[action]); 376 return; 377 } 378 379 upio_state[UPIO_LPM_MODE] = action; 380 381#if (BT_WAKE_VIA_PROC == TRUE) 382 fd = open(VENDOR_LPM_PROC_NODE, O_WRONLY); 383 384 if (fd < 0) 385 { 386 ALOGE("upio_set : open(%s) for write failed: %s (%d)", 387 VENDOR_LPM_PROC_NODE, strerror(errno), errno); 388 return; 389 } 390 391 if (action == UPIO_ASSERT) 392 { 393 buffer = '1'; 394 } 395 else 396 { 397 buffer = '0'; 398 399 // delete btwrite assertion holding timer 400 if (lpm_proc_cb.timer_created == TRUE) 401 { 402 timer_delete(lpm_proc_cb.timer_id); 403 lpm_proc_cb.timer_created = FALSE; 404 } 405 } 406 407 if (write(fd, &buffer, 1) < 0) 408 { 409 ALOGE("upio_set : write(%s) failed: %s (%d)", 410 VENDOR_LPM_PROC_NODE, strerror(errno),errno); 411 } 412#if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0) 413 else 414 { 415 if (action == UPIO_ASSERT) 416 { 417 // create btwrite assertion holding timer 418 if (lpm_proc_cb.timer_created == FALSE) 419 { 420 int status; 421 struct sigevent se; 422 423 se.sigev_notify = SIGEV_THREAD; 424 se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id; 425 se.sigev_notify_function = proc_btwrite_timeout; 426 se.sigev_notify_attributes = NULL; 427 428 status = timer_create(CLOCK_MONOTONIC, &se, 429 &lpm_proc_cb.timer_id); 430 431 if (status == 0) 432 lpm_proc_cb.timer_created = TRUE; 433 } 434 } 435 } 436#endif 437 438 if (fd >= 0) 439 close(fd); 440#endif 441 break; 442 443 case UPIO_BT_WAKE: 444 if (upio_state[UPIO_BT_WAKE] == action) 445 { 446 UPIODBG("BT_WAKE is %s already", lpm_state[action]); 447 448#if (BT_WAKE_VIA_PROC == TRUE) 449 if (lpm_proc_cb.btwrite_active == TRUE) 450 /* 451 * The proc btwrite node could have not been updated for 452 * certain time already due to heavy downstream path flow. 453 * In this case, we want to explicity touch proc btwrite 454 * node to keep the bt_wake assertion in the LPM kernel 455 * driver. The current kernel bluesleep LPM code starts 456 * a 10sec internal in-activity timeout timer before it 457 * attempts to deassert BT_WAKE line. 458 */ 459 return; 460#else 461 return; 462#endif 463 } 464 465 upio_state[UPIO_BT_WAKE] = action; 466 467#if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE) 468 469 userial_vendor_ioctl( ( (action==UPIO_ASSERT) ? \ 470 USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE),\ 471 NULL); 472 473#elif (BT_WAKE_VIA_PROC == TRUE) 474 475 /* 476 * Kick proc btwrite node only at UPIO_ASSERT 477 */ 478#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == FALSE) 479 if (action == UPIO_DEASSERT) 480 return; 481#endif 482 fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY); 483 484 if (fd < 0) 485 { 486 ALOGE("upio_set : open(%s) for write failed: %s (%d)", 487 VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno); 488 return; 489 } 490#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE) 491 if (action == UPIO_DEASSERT) 492 buffer = '0'; 493 else 494#endif 495 buffer = '1'; 496 497 if (write(fd, &buffer, 1) < 0) 498 { 499 ALOGE("upio_set : write(%s) failed: %s (%d)", 500 VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno); 501 } 502#if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0) 503 else 504 { 505 /* arm user space timer based on action */ 506 upio_start_stop_timer(action); 507 } 508#endif 509 510#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE) 511 lpm_proc_cb.btwrite_active = TRUE; 512#endif 513 514 UPIODBG("%s: proc btwrite assertion, buffer: %c, timer_armed %d %d", 515 __FUNCTION__, buffer, lpm_proc_cb.btwrite_active, lpm_proc_cb.timer_created); 516 517 if (fd >= 0) 518 close(fd); 519#endif 520 521 break; 522 523 case UPIO_HOST_WAKE: 524 UPIODBG("upio_set: UPIO_HOST_WAKE"); 525 break; 526 } 527} 528 529 530