diff options
Diffstat (limited to 'hci/src/lpm.c')
-rw-r--r-- | hci/src/lpm.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/hci/src/lpm.c b/hci/src/lpm.c new file mode 100644 index 0000000..c07b37c --- /dev/null +++ b/hci/src/lpm.c @@ -0,0 +1,453 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * This program is the proprietary software of Broadcom Corporation and/or its + * licensors, and may only be used, duplicated, modified or distributed + * pursuant to the terms and conditions of a separate, written license + * agreement executed between you and Broadcom (an "Authorized License"). + * Except as set forth in an Authorized License, Broadcom grants no license + * (express or implied), right to use, or waiver of any kind with respect to + * the Software, and Broadcom expressly reserves all rights in and to the + * Software and all intellectual property rights therein. + * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS + * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE + * ALL USE OF THE SOFTWARE. + * + * Except as expressly set forth in the Authorized License, + * + * 1. This program, including its structure, sequence and organization, + * constitutes the valuable trade secrets of Broadcom, and you shall + * use all reasonable efforts to protect the confidentiality thereof, + * and to use this information only in connection with your use of + * Broadcom integrated circuit products. + * + * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED + * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, + * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, + * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY + * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, + * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, + * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT + * OF USE OR PERFORMANCE OF THE SOFTWARE. + * + * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR + * ITS LICENSORS BE LIABLE FOR + * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY + * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO + * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR + * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE + * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE + * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF + * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Filename: lpm.c + * + * Description: Contains low power mode implementation + * + ******************************************************************************/ + +#define LOG_TAG "bt_lpm" + +#include <utils/Log.h> +#include <signal.h> +#include <time.h> +#include "bt_hci_bdroid.h" +#include "bt_vendor_lib.h" + +/****************************************************************************** +** Constants & Macros +******************************************************************************/ + +#ifndef BTLPM_DBG +#define BTLPM_DBG FALSE +#endif + +#if (BTLPM_DBG == TRUE) +#define BTLPMDBG(param, ...) {LOGD(param, ## __VA_ARGS__);} +#else +#define BTLPMDBG(param, ...) {} +#endif + +#ifndef DEFAULT_LPM_IDLE_TIMEOUT +#define DEFAULT_LPM_IDLE_TIMEOUT 3000 +#endif + +/****************************************************************************** +** Externs +******************************************************************************/ + +extern bt_vendor_interface_t *bt_vnd_if; + +/****************************************************************************** +** Local type definitions +******************************************************************************/ + +/* Low power mode state */ +enum { + LPM_DISABLED = 0, /* initial state */ + LPM_ENABLED, + LPM_ENABLING, + LPM_DISABLING +}; + +/* LPM WAKE state */ +enum { + LPM_WAKE_DEASSERTED = 0, /* initial state */ + LPM_WAKE_W4_TX_DONE, + LPM_WAKE_W4_TIMEOUT, + LPM_WAKE_ASSERTED +}; + +/* low power mode control block */ +typedef struct +{ + uint8_t state; /* Low power mode state */ + uint8_t wake_state; /* LPM WAKE state */ + uint8_t no_tx_data; + uint8_t timer_created; + timer_t timer_id; + uint32_t timeout_ms; +} bt_lpm_cb_t; + + +/****************************************************************************** +** Static variables +******************************************************************************/ + +static bt_lpm_cb_t bt_lpm_cb; + +/****************************************************************************** +** LPM Static Functions +******************************************************************************/ + +/******************************************************************************* +** +** Function lpm_idle_timeout +** +** Description Timeout thread of transport idle timer +** +** Returns None +** +*******************************************************************************/ +static void lpm_idle_timeout(union sigval arg) +{ + BTLPMDBG("..lpm_idle_timeout.."); + + if ((bt_lpm_cb.state == LPM_ENABLED) && \ + (bt_lpm_cb.wake_state == LPM_WAKE_W4_TIMEOUT)) + { + bthc_signal_event(HC_EVENT_LPM_IDLE_TIMEOUT); + } +} + +/******************************************************************************* +** +** Function lpm_start_transport_idle_timer +** +** Description Launch transport idle timer +** +** Returns None +** +*******************************************************************************/ +static void lpm_start_transport_idle_timer(void) +{ + int status; + struct itimerspec ts; + struct sigevent se; + + if (bt_lpm_cb.state != LPM_ENABLED) + return; + + if (bt_lpm_cb.timer_created == FALSE) + { + se.sigev_notify = SIGEV_THREAD; + se.sigev_value.sival_ptr = &bt_lpm_cb.timer_id; + se.sigev_notify_function = lpm_idle_timeout; + se.sigev_notify_attributes = NULL; + + status = timer_create(CLOCK_MONOTONIC, &se, &bt_lpm_cb.timer_id); + + if (status == 0) + bt_lpm_cb.timer_created = TRUE; + } + + if (bt_lpm_cb.timer_created == TRUE) + { + ts.it_value.tv_sec = bt_lpm_cb.timeout_ms/1000; + ts.it_value.tv_nsec = 1000*(bt_lpm_cb.timeout_ms%1000); + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0); + if (status == -1) + LOGE("[START] Failed to set LPM idle timeout"); + } +} + +/******************************************************************************* +** +** Function lpm_stop_transport_idle_timer +** +** Description Launch transport idle timer +** +** Returns None +** +*******************************************************************************/ +static void lpm_stop_transport_idle_timer(void) +{ + int status; + struct itimerspec ts; + + if (bt_lpm_cb.timer_created == TRUE) + { + ts.it_value.tv_sec = 0; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + status = timer_settime(bt_lpm_cb.timer_id, 0, &ts, 0); + if (status == -1) + LOGE("[STOP] Failed to set LPM idle timeout"); + } +} + +/******************************************************************************* +** +** Function lpm_vnd_cback +** +** Description Callback of vendor specific result for lpm enable/disable +** rquest +** +** Returns None +** +*******************************************************************************/ +void lpm_vnd_cback(uint8_t vnd_result) +{ + if (vnd_result == 0) + { + /* Status == Success */ + bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \ + LPM_ENABLED : LPM_DISABLED; + } + else + { + bt_lpm_cb.state = (bt_lpm_cb.state == LPM_ENABLING) ? \ + LPM_DISABLED : LPM_ENABLED; + } + + if (bt_hc_cbacks) + { + if (bt_lpm_cb.state == LPM_ENABLED) + bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED); + else + bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED); + } + + if (bt_lpm_cb.state == LPM_DISABLED) + { + if (bt_lpm_cb.timer_created == TRUE) + { + timer_delete(bt_lpm_cb.timer_id); + } + + memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t)); + } +} + + +/***************************************************************************** +** Low Power Mode Interface Functions +*****************************************************************************/ + +/******************************************************************************* +** +** Function lpm_init +** +** Description Init LPM +** +** Returns None +** +*******************************************************************************/ +void lpm_init(void) +{ + memset(&bt_lpm_cb, 0, sizeof(bt_lpm_cb_t)); + + /* Calling vendor-specific part */ + if (bt_vnd_if) + bt_vnd_if->op(BT_VND_OP_GET_LPM_IDLE_TIMEOUT, &(bt_lpm_cb.timeout_ms)); + else + bt_lpm_cb.timeout_ms = DEFAULT_LPM_IDLE_TIMEOUT; +} + +/******************************************************************************* +** +** Function lpm_cleanup +** +** Description Clean up +** +** Returns None +** +*******************************************************************************/ +void lpm_cleanup(void) +{ + if (bt_lpm_cb.timer_created == TRUE) + { + timer_delete(bt_lpm_cb.timer_id); + } +} + +/******************************************************************************* +** +** Function lpm_enable +** +** Description Enalbe/Disable LPM +** +** Returns None +** +*******************************************************************************/ +void lpm_enable(uint8_t turn_on) +{ + if ((bt_lpm_cb.state!=LPM_DISABLED) && (bt_lpm_cb.state!=LPM_ENABLED)) + { + LOGW("Still busy on processing prior LPM enable/disable request..."); + return; + } + + if ((turn_on == TRUE) && (bt_lpm_cb.state == LPM_ENABLED)) + { + LOGI("LPM is already on!!!"); + if (bt_hc_cbacks) + bt_hc_cbacks->lpm_cb(BT_HC_LPM_ENABLED); + } + else if ((turn_on == FALSE) && (bt_lpm_cb.state == LPM_DISABLED)) + { + LOGI("LPM is already off!!!"); + if (bt_hc_cbacks) + bt_hc_cbacks->lpm_cb(BT_HC_LPM_DISABLED); + } + + /* Calling vendor-specific part */ + if (bt_vnd_if) + { + bt_lpm_cb.state = (turn_on) ? LPM_ENABLING : LPM_DISABLING; + bt_vnd_if->op(BT_VND_OP_LPM_SET_MODE, &turn_on); + } +} + +/******************************************************************************* +** +** Function lpm_tx_done +** +** Description This function is to inform the lpm module +** if data is waiting in the Tx Q or not. +** +** IsTxDone: TRUE if All data in the Tx Q are gone +** FALSE if any data is still in the Tx Q. +** Typicaly this function must be called +** before USERIAL Write and in the Tx Done routine +** +** Returns None +** +*******************************************************************************/ +void lpm_tx_done(uint8_t is_tx_done) +{ + bt_lpm_cb.no_tx_data = is_tx_done; + + if ((bt_lpm_cb.wake_state==LPM_WAKE_W4_TX_DONE) && (is_tx_done==TRUE)) + { + bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT; + lpm_start_transport_idle_timer(); + } +} + +/******************************************************************************* +** +** Function lpm_wake_assert +** +** Description Called to wake up Bluetooth chip. +** Normally this is called when there is data to be sent +** over UART. +** +** Returns TRUE/FALSE +** +*******************************************************************************/ +void lpm_wake_assert(void) +{ + if (bt_lpm_cb.state != LPM_DISABLED) + { + BTLPMDBG("LPM WAKE assert"); + + /* Calling vendor-specific part */ + if (bt_vnd_if) + { + uint8_t state = BT_VND_LPM_WAKE_ASSERT; + bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state); + } + + lpm_stop_transport_idle_timer(); + + bt_lpm_cb.wake_state = LPM_WAKE_ASSERTED; + } + + lpm_tx_done(FALSE); +} + +/******************************************************************************* +** +** Function lpm_allow_bt_device_sleep +** +** Description Start LPM idle timer if allowed +** +** Returns None +** +*******************************************************************************/ +void lpm_allow_bt_device_sleep(void) +{ + if ((bt_lpm_cb.state == LPM_ENABLED) && \ + (bt_lpm_cb.wake_state == LPM_WAKE_ASSERTED)) + { + if(bt_lpm_cb.no_tx_data == TRUE) + { + bt_lpm_cb.wake_state = LPM_WAKE_W4_TIMEOUT; + lpm_start_transport_idle_timer(); + } + else + { + bt_lpm_cb.wake_state = LPM_WAKE_W4_TX_DONE; + } + } +} + +/******************************************************************************* +** +** Function lpm_wake_deassert +** +** Description Deassert wake if allowed +** +** Returns None +** +*******************************************************************************/ +void lpm_wake_deassert(void) +{ + if ((bt_lpm_cb.state == LPM_ENABLED) && (bt_lpm_cb.no_tx_data == TRUE)) + { + BTLPMDBG("LPM WAKE deassert"); + + /* Calling vendor-specific part */ + if (bt_vnd_if) + { + uint8_t state = BT_VND_LPM_WAKE_DEASSERT; + bt_vnd_if->op(BT_VND_OP_LPM_WAKE_SET_STATE, &state); + } + + bt_lpm_cb.wake_state = LPM_WAKE_DEASSERTED; + } +} + |