diff options
Diffstat (limited to 'libbt/src/upio.c')
-rw-r--r-- | libbt/src/upio.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/libbt/src/upio.c b/libbt/src/upio.c new file mode 100644 index 0000000..2de171c --- /dev/null +++ b/libbt/src/upio.c @@ -0,0 +1,487 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Filename: upio.c + * + * Description: Contains I/O functions, like + * rfkill control + * BT_WAKE/HOST_WAKE control + * + ******************************************************************************/ + +#define LOG_TAG "bt_upio" + +#include <utils/Log.h> +#include <fcntl.h> +#include <errno.h> +#include <cutils/properties.h> +#include "bt_vendor_brcm.h" +#include "upio.h" +#include "userial_vendor.h" + +/****************************************************************************** +** Constants & Macros +******************************************************************************/ + +#ifndef UPIO_DBG +#define UPIO_DBG FALSE +#endif + +#if (UPIO_DBG == TRUE) +#define UPIODBG(param, ...) {ALOGD(param, ## __VA_ARGS__);} +#else +#define UPIODBG(param, ...) {} +#endif + +/****************************************************************************** +** Local type definitions +******************************************************************************/ + +#if (BT_WAKE_VIA_PROC == TRUE) + +/* proc fs node for enable/disable lpm mode */ +#ifndef VENDOR_LPM_PROC_NODE +#define VENDOR_LPM_PROC_NODE "/proc/bluetooth/sleep/lpm" +#endif + +/* proc fs node for notifying write request */ +#ifndef VENDOR_BTWRITE_PROC_NODE +#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite" +#endif + +/* + * Maximum btwrite assertion holding time without consecutive btwrite kicking. + * This value is correlative(shorter) to the in-activity timeout period set in + * the bluesleep LPM code. The current value used in bluesleep is 10sec. + */ +#ifndef PROC_BTWRITE_TIMER_TIMEOUT_MS +#define PROC_BTWRITE_TIMER_TIMEOUT_MS 8000 +#endif + +/* lpm proc control block */ +typedef struct +{ + uint8_t btwrite_active; + uint8_t timer_created; + timer_t timer_id; + uint32_t timeout_ms; +} vnd_lpm_proc_cb_t; + +static vnd_lpm_proc_cb_t lpm_proc_cb; +#endif + +/****************************************************************************** +** Static variables +******************************************************************************/ + +static uint8_t upio_state[UPIO_MAX_COUNT]; +static int rfkill_id = -1; +static int bt_emul_enable = 0; +static char *rfkill_state_path = NULL; + +/****************************************************************************** +** Static functions +******************************************************************************/ + +/* for friendly debugging outpout string */ +static char *lpm_mode[] = { + "UNKNOWN", + "disabled", + "enabled" +}; + +static char *lpm_state[] = { + "UNKNOWN", + "de-asserted", + "asserted" +}; + +/***************************************************************************** +** Bluetooth On/Off Static Functions +*****************************************************************************/ +static int is_emulator_context(void) +{ + char value[PROPERTY_VALUE_MAX]; + + property_get("ro.kernel.qemu", value, "0"); + UPIODBG("is_emulator_context : %s", value); + if (strcmp(value, "1") == 0) { + return 1; + } + return 0; +} + +static int is_rfkill_disabled(void) +{ + char value[PROPERTY_VALUE_MAX]; + + property_get("ro.rfkilldisabled", value, "0"); + UPIODBG("is_rfkill_disabled ? [%s]", value); + + if (strcmp(value, "1") == 0) { + return UPIO_BT_POWER_ON; + } + + return UPIO_BT_POWER_OFF; +} + +static int init_rfkill() +{ + char path[64]; + char buf[16]; + int fd, sz, id; + + if (is_rfkill_disabled()) + return -1; + + for (id = 0; ; id++) + { + snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); + fd = open(path, O_RDONLY); + if (fd < 0) + { + ALOGE("init_rfkill : open(%s) failed: %s (%d)\n", \ + path, strerror(errno), errno); + return -1; + } + + sz = read(fd, &buf, sizeof(buf)); + close(fd); + + if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) + { + rfkill_id = id; + break; + } + } + + asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id); + return 0; +} + +/***************************************************************************** +** LPM Static Functions +*****************************************************************************/ + +#if (BT_WAKE_VIA_PROC == TRUE) +/******************************************************************************* +** +** Function proc_btwrite_timeout +** +** Description Timeout thread of proc/.../btwrite assertion holding timer +** +** Returns None +** +*******************************************************************************/ +static void proc_btwrite_timeout(union sigval arg) +{ + UPIODBG("..%s..", __FUNCTION__); + lpm_proc_cb.btwrite_active = FALSE; +} +#endif + +/***************************************************************************** +** UPIO Interface Functions +*****************************************************************************/ + +/******************************************************************************* +** +** Function upio_init +** +** Description Initialization +** +** Returns None +** +*******************************************************************************/ +void upio_init(void) +{ + memset(upio_state, UPIO_UNKNOWN, UPIO_MAX_COUNT); +#if (BT_WAKE_VIA_PROC == TRUE) + memset(&lpm_proc_cb, 0, sizeof(vnd_lpm_proc_cb_t)); +#endif +} + +/******************************************************************************* +** +** Function upio_cleanup +** +** Description Clean up +** +** Returns None +** +*******************************************************************************/ +void upio_cleanup(void) +{ +#if (BT_WAKE_VIA_PROC == TRUE) + if (lpm_proc_cb.timer_created == TRUE) + timer_delete(lpm_proc_cb.timer_id); + + lpm_proc_cb.timer_created = FALSE; +#endif +} + +/******************************************************************************* +** +** Function upio_set_bluetooth_power +** +** Description Interact with low layer driver to set Bluetooth power +** on/off. +** +** Returns 0 : SUCCESS or Not-Applicable +** <0 : ERROR +** +*******************************************************************************/ +int upio_set_bluetooth_power(int on) +{ + int sz; + int fd = -1; + int ret = -1; + char buffer = '0'; + + switch(on) + { + case UPIO_BT_POWER_OFF: + buffer = '0'; + break; + + case UPIO_BT_POWER_ON: + buffer = '1'; + break; + } + + if (is_emulator_context()) + { + /* if new value is same as current, return -1 */ + if (bt_emul_enable == on) + return ret; + + UPIODBG("set_bluetooth_power [emul] %d", on); + + bt_emul_enable = on; + return 0; + } + + /* check if we have rfkill interface */ + if (is_rfkill_disabled()) + return 0; + + if (rfkill_id == -1) + { + if (init_rfkill()) + return ret; + } + + fd = open(rfkill_state_path, O_WRONLY); + + if (fd < 0) + { + ALOGE("set_bluetooth_power : open(%s) for write failed: %s (%d)", + rfkill_state_path, strerror(errno), errno); + return ret; + } + + sz = write(fd, &buffer, 1); + + if (sz < 0) { + ALOGE("set_bluetooth_power : write(%s) failed: %s (%d)", + rfkill_state_path, strerror(errno),errno); + } + else + ret = 0; + + if (fd >= 0) + close(fd); + + return ret; +} + + +/******************************************************************************* +** +** Function upio_set +** +** Description Set i/o based on polarity +** +** Returns None +** +*******************************************************************************/ +void upio_set(uint8_t pio, uint8_t action, uint8_t polarity) +{ + int rc; +#if (BT_WAKE_VIA_PROC == TRUE) + int fd = -1; + char buffer; +#endif + + switch (pio) + { + case UPIO_LPM_MODE: + if (upio_state[UPIO_LPM_MODE] == action) + { + UPIODBG("LPM is %s already", lpm_mode[action]); + return; + } + + upio_state[UPIO_LPM_MODE] = action; + +#if (BT_WAKE_VIA_PROC == TRUE) + fd = open(VENDOR_LPM_PROC_NODE, O_WRONLY); + + if (fd < 0) + { + ALOGE("upio_set : open(%s) for write failed: %s (%d)", + VENDOR_LPM_PROC_NODE, strerror(errno), errno); + return; + } + + if (action == UPIO_ASSERT) + { + buffer = '1'; + } + else + { + buffer = '0'; + + // delete btwrite assertion holding timer + if (lpm_proc_cb.timer_created == TRUE) + { + timer_delete(lpm_proc_cb.timer_id); + lpm_proc_cb.timer_created = FALSE; + } + } + + if (write(fd, &buffer, 1) < 0) + { + ALOGE("upio_set : write(%s) failed: %s (%d)", + VENDOR_LPM_PROC_NODE, strerror(errno),errno); + } + else + { + if (action == UPIO_ASSERT) + { + // create btwrite assertion holding timer + if (lpm_proc_cb.timer_created == FALSE) + { + int status; + struct sigevent se; + + se.sigev_notify = SIGEV_THREAD; + se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id; + se.sigev_notify_function = proc_btwrite_timeout; + se.sigev_notify_attributes = NULL; + + status = timer_create(CLOCK_MONOTONIC, &se, + &lpm_proc_cb.timer_id); + + if (status == 0) + lpm_proc_cb.timer_created = TRUE; + } + } + } + + if (fd >= 0) + close(fd); +#endif + break; + + case UPIO_BT_WAKE: + if (upio_state[UPIO_BT_WAKE] == action) + { + UPIODBG("BT_WAKE is %s already", lpm_state[action]); + +#if (BT_WAKE_VIA_PROC == TRUE) + if (lpm_proc_cb.btwrite_active == TRUE) + /* + * The proc btwrite node could have not been updated for + * certain time already due to heavy downstream path flow. + * In this case, we want to explicity touch proc btwrite + * node to keep the bt_wake assertion in the LPM kernel + * driver. The current kernel bluesleep LPM code starts + * a 10sec internal in-activity timeout timer before it + * attempts to deassert BT_WAKE line. + */ +#endif + return; + } + + upio_state[UPIO_BT_WAKE] = action; + +#if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE) + + userial_vendor_ioctl( ( (action==UPIO_ASSERT) ? \ + USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE),\ + NULL); + +#elif (BT_WAKE_VIA_PROC == TRUE) + + /* + * Kick proc btwrite node only at UPIO_ASSERT + */ + if (action == UPIO_DEASSERT) + return; + + fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY); + + if (fd < 0) + { + ALOGE("upio_set : open(%s) for write failed: %s (%d)", + VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno); + return; + } + + buffer = '1'; + + if (write(fd, &buffer, 1) < 0) + { + ALOGE("upio_set : write(%s) failed: %s (%d)", + VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno); + } + else + { + lpm_proc_cb.btwrite_active = TRUE; + + if (lpm_proc_cb.timer_created == TRUE) + { + struct itimerspec ts; + + ts.it_value.tv_sec = PROC_BTWRITE_TIMER_TIMEOUT_MS/1000; + ts.it_value.tv_nsec = 1000*(PROC_BTWRITE_TIMER_TIMEOUT_MS%1000); + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + timer_settime(lpm_proc_cb.timer_id, 0, &ts, 0); + } + } + + UPIODBG("proc btwrite assertion"); + + if (fd >= 0) + close(fd); +#endif + + break; + + case UPIO_HOST_WAKE: + UPIODBG("upio_set: UPIO_HOST_WAKE"); + break; + } +} + + |