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