/*
 * Copyright 2012 The Android Open Source Project
 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * 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:      bt_vendor_qcom.c
 *
 *  Description:   vendor specific library implementation
 *
 ******************************************************************************/
#define LOG_TAG "bt_vendor"
#define BLUETOOTH_MAC_ADDR_BOOT_PROPERTY "ro.boot.btmacaddr"

#include "bt_vendor_lib.h"
#include "bt_vendor_persist.h"
#include "bt_vendor_qcom.h"
#include "hci_smd.h"
#include "hci_uart.h"
#include "hw_rome.h"

#include <fcntl.h>
#include <linux/un.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>

#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <utils/Log.h>
#define WAIT_TIMEOUT 200000
#define BT_VND_OP_GET_LINESPEED 30

#define STOP_WCNSS_FILTER 0xDD
#define STOP_WAIT_TIMEOUT   1000

#define SOC_INIT_PROPERTY "wc_transport.soc_initialized"

#define BT_VND_FILTER_START "wc_transport.start_hci"

#define CMD_TIMEOUT  0x22

static void wait_for_patch_download(bool is_ant_req);
static bool is_debug_force_special_bytes(void);
int connect_to_local_socket(char* name);
/******************************************************************************
**  Externs
******************************************************************************/
extern int hw_config(int nState);
extern int is_hw_ready();
extern int chipset_ver;

/******************************************************************************
**  Variables
******************************************************************************/
struct bt_qcom_struct q;
pthread_mutex_t q_lock = PTHREAD_MUTEX_INITIALIZER;

int userial_clock_operation(int fd, int cmd);
int ath3k_init(int fd, int speed, int init_speed, char *bdaddr, struct termios *ti);
int userial_vendor_get_baud(void);
int readTrpState();
void lpm_set_ar3k(uint8_t pio, uint8_t action, uint8_t polarity);
bool is_download_progress();

static const tUSERIAL_CFG userial_init_cfg =
{
    (USERIAL_DATABITS_8 | USERIAL_PARITY_NONE | USERIAL_STOPBITS_1),
    USERIAL_BAUD_115200
};

#if (HW_NEED_END_WITH_HCI_RESET == TRUE)
void __hw_epilog_process(void);
#endif

#ifdef WIFI_BT_STATUS_SYNC
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include "cutils/properties.h"

static const char WIFI_PROP_NAME[]    = "wlan.driver.status";
static const char SERVICE_PROP_NAME[]    = "bluetooth.hsic_ctrl";
static const char BT_STATUS_NAME[]    = "bluetooth.enabled";
static const char WIFI_SERVICE_PROP[] = "wlan.hsic_ctrl";

#define WIFI_BT_STATUS_LOCK    "/data/connectivity/wifi_bt_lock"
int isInit=0;
#endif /* WIFI_BT_STATUS_SYNC */
bool is_soc_initialized(void);

/******************************************************************************
**  Local type definitions
******************************************************************************/

/******************************************************************************
**  Functions
******************************************************************************/
#ifdef WIFI_BT_STATUS_SYNC
int bt_semaphore_create(void)
{
    int fd;

    fd = open(WIFI_BT_STATUS_LOCK, O_RDONLY);

    if (fd < 0)
        ALOGE("can't create file\n");

    return fd;
}

int bt_semaphore_get(int fd)
{
    int ret;

    if (fd < 0)
        return -1;

    ret = flock(fd, LOCK_EX);
    if (ret != 0) {
        ALOGE("can't hold lock: %s\n", strerror(errno));
        return -1;
    }

    return ret;
}

int bt_semaphore_release(int fd)
{
    int ret;

    if (fd < 0)
        return -1;

    ret = flock(fd, LOCK_UN);
    if (ret != 0) {
        ALOGE("can't release lock: %s\n", strerror(errno));
        return -1;
    }

    return ret;
}

int bt_semaphore_destroy(int fd)
{
    if (fd < 0)
        return -1;

    return close (fd);
}

int bt_wait_for_service_done(void)
{
    char service_status[PROPERTY_VALUE_MAX];
    int count = 30;

    ALOGE("%s: check\n", __func__);

    /* wait for service done */
    while (count-- > 0) {
        property_get(WIFI_SERVICE_PROP, service_status, NULL);

        if (strcmp(service_status, "") != 0) {
            usleep(200000);
        } else {
            break;
        }
    }

    return 0;
}

#endif /* WIFI_BT_STATUS_SYNC */

/** Get Bluetooth SoC type from system setting */
static int get_bt_soc_type()
{
    int ret = 0;
    char bt_soc_type[PROPERTY_VALUE_MAX];

    ALOGI("bt-vendor : get_bt_soc_type");

    ret = property_get("qcom.bluetooth.soc", bt_soc_type, NULL);
    if (ret != 0) {
        ALOGI("qcom.bluetooth.soc set to %s\n", bt_soc_type);
        if (!strncasecmp(bt_soc_type, "rome", sizeof("rome"))) {
            return BT_SOC_ROME;
        }
        else if (!strncasecmp(bt_soc_type, "cherokee", sizeof("cherokee"))) {
            return BT_SOC_CHEROKEE;
        }
        else if (!strncasecmp(bt_soc_type, "ath3k", sizeof("ath3k"))) {
            return BT_SOC_AR3K;
        }
        else if (!strncasecmp(bt_soc_type, "cherokee", sizeof("cherokee"))) {
            return BT_SOC_CHEROKEE;
        }
        else {
            ALOGI("qcom.bluetooth.soc not set, so using default.\n");
            return BT_SOC_DEFAULT;
        }
    }
    else {
        ALOGE("%s: Failed to get soc type", __FUNCTION__);
        ret = BT_SOC_DEFAULT;
    }

    return ret;
}

bool can_perform_action(char action) {
    bool can_perform = false;
    char ref_count[PROPERTY_VALUE_MAX];
    char inProgress[PROPERTY_VALUE_MAX] = {'\0'};
    int value, ret;

    property_get("wc_transport.ref_count", ref_count, "0");

    value = atoi(ref_count);
    ALOGV("%s: ref_count: %s\n",__func__,  ref_count);

    if(action == '1') {
        ALOGV("%s: on : value is: %d", __func__, value);
        if(value == 1)
        {
            if ((is_soc_initialized() == true)
               || is_download_progress() || get_bt_soc_type() == BT_SOC_CHEROKEE)
          {
            //value++;
            ALOGE("%s: on : value is already 1", __func__);
          }
        }
        else
        {
             value++;
        }

        if (value == 1)
            can_perform = true;
        else if (value > 3)
            return false;
    }
    else {
        ALOGV("%s: off : value is: %d", __func__, value);
        if (--value <= 0) {
            ALOGE("%s: BT turn off twice before BT On(ref_count=%d)\n",
                    __func__, value);
            value = 0;
            can_perform = true;
        }
    }

    snprintf(ref_count, 3, "%d", value);
    ALOGV("%s: updated ref_count is: %s", __func__, ref_count);

    ret  = property_set("wc_transport.ref_count", ref_count);
    if (ret < 0) {
        ALOGE("%s: Error while updating property: %d\n", __func__, ret);
        return false;
    }
    ALOGV("%s returning %d", __func__, can_perform);
    return can_perform;
}

void stop_hci_filter() {
       char value[PROPERTY_VALUE_MAX] = {'\0'};
       int retval, filter_ctrl, i;
       char stop_val = STOP_WCNSS_FILTER;
       int soc_type = BT_SOC_DEFAULT;

       ALOGV("%s: Entry ", __func__);

       property_get("wc_transport.hci_filter_status", value, "0");
       if (strcmp(value, "0") == 0) {
           ALOGI("%s: hci_filter has been stopped already", __func__);
       }
       else {
           filter_ctrl = connect_to_local_socket("wcnssfilter_ctrl");
           if (filter_ctrl < 0) {
               ALOGI("%s: Error while connecting to CTRL_SOCK, filter should stopped: %d",
                     __func__, filter_ctrl);
           }
           else {
               retval = write(filter_ctrl, &stop_val, 1);
               if (retval != 1) {
                   ALOGI("%s: problem writing to CTRL_SOCK, ignore: %d", __func__, retval);
                   //Ignore and fallback
               }

               close(filter_ctrl);
           }
       }

       /* Ensure Filter is closed by checking the status before
          RFKILL 0 operation. this should ideally comeout very
          quick */
       for(i=0; i<500; i++) {
           property_get(BT_VND_FILTER_START, value, "false");
           if (strcmp(value, "false") == 0) {
               ALOGI("%s: WCNSS_FILTER stopped", __func__);
               usleep(STOP_WAIT_TIMEOUT * 10);
               break;
           } else {
               /*sleep of 1ms, This should give enough time for FILTER to
               exit with all necessary cleanup*/
               usleep(STOP_WAIT_TIMEOUT);
           }
       }

       ALOGV("%s: Exit ", __func__);
}

int start_hci_filter() {
       ALOGV("%s: Entry ", __func__);
       int i, init_success = -1;
       char value[PROPERTY_VALUE_MAX] = {'\0'};

       property_get(BT_VND_FILTER_START, value, false);

       if (strcmp(value, "true") == 0) {
           ALOGI("%s: hci_filter has been started already", __func__);
           //Filter should have been started OR in the process of initializing
           //Make sure of hci_filter_status and return the state based on it
       } else {
           property_set("wc_transport.clean_up","0");
           property_set("wc_transport.hci_filter_status", "0");
           property_set(BT_VND_FILTER_START, "true");
           ALOGV("%s: %s set to true ", __func__, BT_VND_FILTER_START );
       }

       /*If there are back to back ON requests from different clients,
         All client should come and stuck in this while loop till FILTER
         comesup and ready to accept the connections */
       //sched_yield();
       for(i=0; i<45; i++) {
          property_get("wc_transport.hci_filter_status", value, "0");
          if (strcmp(value, "1") == 0) {
               init_success = 1;
               break;
           } else {
               usleep(WAIT_TIMEOUT);
           }
        }
        ALOGV("start_hcifilter status:%d after %f seconds \n", init_success, 0.2*i);

        ALOGV("%s: Exit ", __func__);
        return init_success;
}

/*
 * Bluetooth Controller power up or shutdown, this function is called with
 * q_lock held and q is non-NULL
 */
static int bt_powerup(int en )
{
    char rfkill_type[64], *enable_ldo_path = NULL;
    char type[16], enable_ldo[6];
    int fd = 0, size, i, ret, fd_ldo, fd_btpower;

    char disable[PROPERTY_VALUE_MAX];
    char state;
    char on = (en)?'1':'0';

#ifdef WIFI_BT_STATUS_SYNC
    char wifi_status[PROPERTY_VALUE_MAX];
    int lock_fd;
#endif /*WIFI_BT_STATUS_SYNC*/

    ALOGI("bt_powerup: %c", on);

    /* Check if rfkill has been disabled */
    ret = property_get("ro.rfkilldisabled", disable, "0");
    if (!ret ){
        ALOGE("Couldn't get ro.rfkilldisabled (%d)", ret);
        return -1;
    }
    /* In case rfkill disabled, then no control power*/
    if (strcmp(disable, "1") == 0) {
        ALOGI("ro.rfkilldisabled : %s", disable);
        return -1;
    }

#ifdef WIFI_BT_STATUS_SYNC
    lock_fd = bt_semaphore_create();
    bt_semaphore_get(lock_fd);
    bt_wait_for_service_done();
#endif

    /* Assign rfkill_id and find bluetooth rfkill state path*/
    for(i = 0; (q.rfkill_id == -1) && (q.rfkill_state == NULL); i++)
    {
        snprintf(rfkill_type, sizeof(rfkill_type), "/sys/class/rfkill/rfkill%d/type", i);
        if ((fd = open(rfkill_type, O_RDONLY)) < 0)
        {
            ALOGE("open(%s) failed: %s (%d)\n", rfkill_type, strerror(errno), errno);

#ifdef WIFI_BT_STATUS_SYNC
            bt_semaphore_release(lock_fd);
            bt_semaphore_destroy(lock_fd);
#endif
            return -1;
        }

        size = read(fd, &type, sizeof(type));
        close(fd);

        if ((size >= 9) && !memcmp(type, "bluetooth", 9))
        {
            asprintf(&q.rfkill_state, "/sys/class/rfkill/rfkill%d/state", q.rfkill_id = i);
            break;
        }
    }

    /* Get rfkill State to control */
    if (q.rfkill_state != NULL)
    {
        if ((fd = open(q.rfkill_state, O_RDWR)) < 0)
        {
            ALOGE("open(%s) for write failed: %s (%d)", q.rfkill_state, strerror(errno), errno);
#ifdef WIFI_BT_STATUS_SYNC
            bt_semaphore_release(lock_fd);
            bt_semaphore_destroy(lock_fd);
#endif

            return -1;
        }
    }
    if(can_perform_action(on) == false) {
        ALOGE("%s:can't perform action as it is being used by other clients", __func__);
#ifdef WIFI_BT_STATUS_SYNC
            bt_semaphore_release(lock_fd);
            bt_semaphore_destroy(lock_fd);
#endif
            goto done;
    }
    ret = asprintf(&enable_ldo_path, "/sys/class/rfkill/rfkill%d/device/extldo", q.rfkill_id);
    if( (ret < 0 ) || (enable_ldo_path == NULL) )
    {
        ALOGE("Memory Allocation failure");
        return -1;
    }
    if ((fd_ldo = open(enable_ldo_path, O_RDWR)) < 0) {
        ALOGE("open(%s) failed: %s (%d)", enable_ldo_path, strerror(errno), errno);
        return -1;
    }
    size = read(fd_ldo, &enable_ldo, sizeof(enable_ldo));
    close(fd_ldo);
    if (size <= 0) {
        ALOGE("read(%s) failed: %s (%d)", enable_ldo_path, strerror(errno), errno);
        return -1;
    }
    if (!memcmp(enable_ldo, "true", 4)) {
        ALOGI("External LDO has been configured");
        ret = property_set("wc_transport.extldo", "enabled");
        if (ret < 0) {
            ALOGI("%s: Not able to set property wc_transport.extldo\n", __func__);
        }
        q.enable_extldo = TRUE;
    }

    if(on == '0'){
        ALOGE("Stopping HCI filter as part of CTRL:OFF");
        stop_hci_filter();
        property_set("wc_transport.soc_initialized", "0");
    }

    if (q.soc_type >= BT_SOC_CHEROKEE && q.soc_type < BT_SOC_RESERVED) {
       ALOGI("open bt power devnode,send ioctl power op  :%d ",en);
       fd_btpower = open(BT_PWR_CNTRL_DEVICE, O_RDWR, O_NONBLOCK);
       if (fd_btpower < 0) {
           ALOGE("\nfailed to open bt device error = (%s)\n",strerror(errno));
#ifdef WIFI_BT_STATUS_SYNC
           bt_semaphore_release(lock_fd);
           bt_semaphore_destroy(lock_fd);
#endif
           return -1;
       }
       ret = ioctl(fd_btpower, BT_CMD_PWR_CTRL, (unsigned long)en);
        if (ret < 0) {
            ALOGE(" ioctl failed to power control:%d error =(%s)",ret,strerror(errno));
        }
        close(fd_btpower);
    } else {
       ALOGI("Write %c to rfkill\n", on);
       /* Write value to control rfkill */
       if(fd >= 0) {
           if ((size = write(fd, &on, 1)) < 0) {
               ALOGE("write(%s) failed: %s (%d)", q.rfkill_state, strerror(errno), errno);
#ifdef WIFI_BT_STATUS_SYNC
               bt_semaphore_release(lock_fd);
               bt_semaphore_destroy(lock_fd);
#endif
               return -1;
           }
       }
   }
#ifdef WIFI_BT_STATUS_SYNC
    /* query wifi status */
    property_get(WIFI_PROP_NAME, wifi_status, "");

    ALOGE("bt get wifi status: %s, isInit: %d\n",  wifi_status, isInit);

    /* If wlan driver is not loaded, and bt is changed from off => on */
    if (strncmp(wifi_status, "unloaded", strlen("unloaded")) == 0 || strlen(wifi_status) == 0) {
        if (on == '1') {
            ALOGI("%s: BT_VND_PWR_ON\n", __func__);
            if(property_set(SERVICE_PROP_NAME, "load_wlan") < 0) {
                ALOGE("%s Property setting failed", SERVICE_PROP_NAME);
                close(fd);
                bt_semaphore_release(lock_fd);
                bt_semaphore_destroy(lock_fd);
                return -1;
            }
        }
        else if (isInit == 0 && on == '0') {
            ALOGI("%s: BT_VND_PWR_OFF\n", __func__);
            if(property_set(SERVICE_PROP_NAME, "unbind_hsic") < 0) {
                ALOGE("%s Property setting failed", SERVICE_PROP_NAME);
                close(fd);
                bt_semaphore_release(lock_fd);
                bt_semaphore_destroy(lock_fd);
                return -1;
            }
       }
    }

    if (isInit == 0 && on == '0')
        property_set(BT_STATUS_NAME, "false");
    else if (on == '1')
        property_set(BT_STATUS_NAME, "true");

    bt_semaphore_release(lock_fd);
    bt_semaphore_destroy(lock_fd);
#endif /* WIFI_BT_STATUS_SYNC */

done:
    if (fd >= 0)
        close(fd);
    return 0;
}

static inline void soc_init(int soc_type)
{
    switch (soc_type)
    {
    case BT_SOC_CHEROKEE:
    case BT_SOC_ROME:
    case BT_SOC_AR3K:
        ALOGI("bt-vendor : Initializing UART transport layer");
        userial_vendor_init();
        break;
    case BT_SOC_DEFAULT:
        break;
    default:
        ALOGE("Unknown soc yype: %d", soc_type);
        break;
    }
}

/* Copy BD Address as little-endian byte order */
static inline void le2bd(unsigned char *src, unsigned char *dst)
{
    int i;
    for (i = 0; i < 6; i++)
        dst[i] = src[5-i];
}

static inline void print_bdaddr(unsigned char *addr)
{
    ALOGI("BD Address: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0], addr[1],
            addr[2], addr[3], addr[4], addr[5]);
}

/*****************************************************************************
**
**   BLUETOOTH VENDOR INTERFACE LIBRARY FUNCTIONS
**
*****************************************************************************/

static int init(const bt_vendor_callbacks_t *cb, unsigned char *bdaddr)
{
    char prop[PROPERTY_VALUE_MAX] = {0};
    int ret = BT_STATUS_SUCCESS, i;

    ALOGI("++%s", __FUNCTION__);

    if (!cb || !bdaddr) {
        ALOGE("Invalid input args cb %p bdaddr %p", cb, bdaddr);
        ret = -BT_STATUS_INVAL;
        goto out;
    }

    q.rfkill_id = -1;
    q.enable_extldo = FALSE;
    q.cb = (bt_vendor_callbacks_t*)cb;
    q.ant_fd = -1;
    q.soc_type = get_bt_soc_type();
    soc_init(q.soc_type);

    le2bd(bdaddr, q.bdaddr);
    print_bdaddr(q.bdaddr);
    snprintf(prop, sizeof(prop), "%02x:%02x:%02x:%02x:%02x:%02x",
             q.bdaddr[0], q.bdaddr[1], q.bdaddr[2],
             q.bdaddr[3], q.bdaddr[4], q.bdaddr[5]);
    ret = property_set("wc_transport.stack_bdaddr", prop);
    if (ret < 0) {
        ALOGE("Failed to set wc_transport.stack_bdaddr prop, ret = %d", ret);
        ret = -BT_STATUS_PROP_FAILURE;
        goto out;
    }

/* TODO: Move these fields inside bt_qcom context */
#ifdef WIFI_BT_STATUS_SYNC
    isInit = 1;
#endif /* WIFI_BT_STATUS_SYNC */

    /* Everything successful */
    return ret;

out:
    ALOGI("--%s ret %d", __FUNCTION__, ret);
    return ret;
}

#ifdef READ_BT_ADDR_FROM_PROP
static bool validate_tok(char* bdaddr_tok) {
    int i = 0;
    bool ret;

    if (strlen(bdaddr_tok) != 2) {
        ret = FALSE;
        ALOGE("Invalid token length");
    } else {
        ret = TRUE;
        for (i=0; i<2; i++) {
            if ((bdaddr_tok[i] >= '0' && bdaddr_tok[i] <= '9') ||
                (bdaddr_tok[i] >= 'A' && bdaddr_tok[i] <= 'F') ||
                (bdaddr_tok[i] >= 'a' && bdaddr_tok[i] <= 'f')) {
                ret = TRUE;
                ALOGV("%s: tok %s @ %d is good", __func__, bdaddr_tok, i);
             } else {
                ret = FALSE;
                ALOGE("invalid character in tok: %s at ind: %d", bdaddr_tok, i);
                break;
             }
        }
    }
    return ret;
}
#endif /*READ_BT_ADDR_FROM_PROP*/

int connect_to_local_socket(char* name) {
       socklen_t len; int sk = -1;

       ALOGE("%s: ACCEPT ", __func__);
       sk  = socket(AF_LOCAL, SOCK_STREAM, 0);
       if (sk < 0) {
           ALOGE("Socket creation failure");
           return -1;
       }

        if(socket_local_client_connect(sk, name,
            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM) < 0)
        {
             ALOGE("failed to connect (%s)", strerror(errno));
             close(sk);
             sk = -1;
        } else {
                ALOGE("%s: Connection succeeded\n", __func__);
        }
        return sk;
}

bool is_soc_initialized() {
    bool init = false;
    char init_value[PROPERTY_VALUE_MAX];
    int ret;

    ALOGI("bt-vendor : is_soc_initialized");

    ret = property_get(SOC_INIT_PROPERTY, init_value, NULL);
    if (ret != 0) {
        ALOGI("%s set to %s\n", SOC_INIT_PROPERTY, init_value);
        if (!strncasecmp(init_value, "1", sizeof("1"))) {
            init = true;
        }
    }
    else {
        ALOGE("%s: Failed to get %s", __FUNCTION__, SOC_INIT_PROPERTY);
    }

    return init;
}

/* flavor of op without locks */
static int op(bt_vendor_opcode_t opcode, void *param)
{
    int retval = BT_STATUS_SUCCESS;
    int nCnt = 0;
    int nState = -1;
    bool is_ant_req = false;
    bool is_fm_req = false;
    char wipower_status[PROPERTY_VALUE_MAX];
    char emb_wp_mode[PROPERTY_VALUE_MAX];
    char bt_version[PROPERTY_VALUE_MAX];
    char lpm_config[PROPERTY_VALUE_MAX];
    bool ignore_boot_prop = TRUE;
#ifdef READ_BT_ADDR_FROM_PROP
    int i = 0;
    static char bd_addr[PROPERTY_VALUE_MAX];
    uint8_t local_bd_addr_from_prop[6];
    char* tok;
#endif
    bool skip_init = true;
    int  opcode_init = opcode;
    ALOGV("++%s opcode %d", __FUNCTION__, opcode);

    switch(opcode_init)
    {
#ifdef FM_OVER_UART
        case FM_VND_OP_POWER_CTRL:
            {
              is_fm_req = true;
              if (is_soc_initialized()) {
                  // add any FM specific actions  if needed in future
                  break;
              }
            }
#endif
        case BT_VND_OP_POWER_CTRL:
            {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                nState = *(int *) param;
                ALOGI("bt-vendor : BT_VND_OP_POWER_CTRL: %s",
                        (nState == BT_VND_PWR_ON)? "On" : "Off" );

                switch(q.soc_type)
                {
                    case BT_SOC_DEFAULT:
                        if (readTrpState())
                        {
                           ALOGI("bt-vendor : resetting BT status");
                           hw_config(BT_VND_PWR_OFF);
                        }
                        retval = hw_config(nState);
                        if(nState == BT_VND_PWR_ON
                           && retval == 0
                           && is_hw_ready() == TRUE){
                            retval = 0;
                        }
                        else {
                            retval = -1;
                        }
                        break;
                    case BT_SOC_ROME:
                    case BT_SOC_AR3K:
                    case BT_SOC_CHEROKEE:
                        /* BT Chipset Power Control through Device Tree Node */
                        pthread_mutex_lock(&q_lock);
                        retval = bt_powerup(nState);
                        pthread_mutex_unlock(&q_lock);
                    default:
                        break;
                }
            }
            break;

        case BT_VND_OP_FW_CFG: {
                /* call hciattach to initalize the stack */
                if (q.soc_type == BT_SOC_ROME) {
                    if (is_soc_initialized()) {
                        ALOGI("Bluetooth FW and transport layer are initialized");
                        q.cb->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
                    } else {
                        ALOGE("bt_vendor_cbacks is null or SoC not initialized");
                        ALOGE("Error : hci, smd initialization Error");
                        retval = -1;
                    }
                } else {
                    ALOGI("Bluetooth FW and transport layer are initialized");
                    q.cb->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
                }
        }
            break;

        case BT_VND_OP_SCO_CFG:
            q.cb->scocfg_cb(BT_VND_OP_RESULT_SUCCESS); //dummy
            break;
#ifdef ENABLE_ANT
        case BT_VND_OP_ANT_USERIAL_OPEN:
                ALOGI("bt-vendor : BT_VND_OP_ANT_USERIAL_OPEN");
                is_ant_req = true;
                goto userial_open;
#endif
#ifdef FM_OVER_UART
        case BT_VND_OP_FM_USERIAL_OPEN:
                ALOGI("bt-vendor : BT_VND_OP_FM_USERIAL_OPEN");
                is_fm_req = true;
                goto userial_open;
#endif
userial_open:
        case BT_VND_OP_USERIAL_OPEN:
            {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                int (*fd_array)[] = (int (*)[]) param;
                int idx, fd = -1, fd_filter = -1;
                ALOGI("bt-vendor : BT_VND_OP_USERIAL_OPEN");
                switch(q.soc_type)
                {
                    case BT_SOC_DEFAULT:
                        {
                            if(bt_hci_init_transport(q.fd) != -1){
                                int (*fd_array)[] = (int (*) []) param;

                                    (*fd_array)[CH_CMD] = q.fd[0];
                                    (*fd_array)[CH_EVT] = q.fd[0];
                                    (*fd_array)[CH_ACL_OUT] = q.fd[1];
                                    (*fd_array)[CH_ACL_IN] = q.fd[1];
                            }
                            else {
                                retval = -1;
                                break;
                            }
                            retval = 2;
                        }
                        break;
                    case BT_SOC_AR3K:
                        {
                            fd = userial_vendor_open((tUSERIAL_CFG *) &userial_init_cfg);
                            if (fd != -1) {
                                for (idx=0; idx < CH_MAX; idx++)
                                    (*fd_array)[idx] = fd;
                                     retval = 1;
                            }
                            else {
                                retval = -1;
                                break;
                            }

                            /* Vendor Specific Process should happened during userial_open process
                                After userial_open, rx read thread is running immediately,
                                so it will affect VS event read process.
                            */
                            if(ath3k_init(fd,3000000,115200,NULL,&vnd_userial.termios)<0)
                                retval = -1;
                        }
                        break;
                    case BT_SOC_ROME:
                        {
                            wait_for_patch_download(is_ant_req);
                            property_get("ro.bluetooth.emb_wp_mode", emb_wp_mode, false);
                            if (!is_soc_initialized()) {
                                char* dlnd_inprog = is_ant_req ? "ant" : "bt";
                                if (property_set("wc_transport.patch_dnld_inprog", dlnd_inprog) < 0) {
                                    ALOGE("%s: Failed to set dnld_inprog %s", __FUNCTION__, dlnd_inprog);
                                }

                                fd = userial_vendor_open((tUSERIAL_CFG *) &userial_init_cfg);
                                if (fd < 0) {
                                    ALOGE("userial_vendor_open returns err");
                                    retval = -1;
                                    break;
                                }

                                /* Clock on */
                                userial_clock_operation(fd, USERIAL_OP_CLK_ON);

                                if(strcmp(emb_wp_mode, "true") == 0) {
                                    property_get("ro.bluetooth.wipower", wipower_status, false);
                                    if(strcmp(wipower_status, "true") == 0) {
                                        check_embedded_mode(fd);
                                    } else {
                                        ALOGI("Wipower not enabled");
                                    }
                                }
                                ALOGV("rome_soc_init is started");
                                property_set("wc_transport.soc_initialized", "0");
#ifdef READ_BT_ADDR_FROM_PROP
                                /*Give priority to read BD address from boot property*/
                                ignore_boot_prop = FALSE;
                                if (property_get(BLUETOOTH_MAC_ADDR_BOOT_PROPERTY, bd_addr, NULL)) {
                                    ALOGV("BD address read from Boot property: %s\n", bd_addr);
                                    tok =  strtok(bd_addr, ":");
                                    while (tok != NULL) {
                                        ALOGV("bd add [%d]: %d ", i, strtol(tok, NULL, 16));
                                        if (i>=6) {
                                            ALOGE("bd property of invalid length");
                                            ignore_boot_prop = TRUE;
                                            break;
                                        }
                                        if (i == 6 && !ignore_boot_prop) {
                                            ALOGV("Valid BD address read from prop");
                                            memcpy(q.bdaddr, local_bd_addr_from_prop, sizeof(vnd_local_bd_addr));
                                            ignore_boot_prop = FALSE;
                                        } else {
                                            ALOGE("There are not enough tokens in BD addr");
                                            ignore_boot_prop = TRUE;
                                            break;
                                        }
                                        local_bd_addr_from_prop[5-i] = strtol(tok, NULL, 16);
                                        tok = strtok(NULL, ":");
                                        i++;
                                    }
                                    if (i == 6 && !ignore_boot_prop) {
                                        ALOGV("Valid BD address read from prop");
                                        memcpy(vnd_local_bd_addr, local_bd_addr_from_prop, sizeof(vnd_local_bd_addr));
                                        ignore_boot_prop = FALSE;
                                    } else {
                                        ALOGE("There are not enough tokens in BD addr");
                                        ignore_boot_prop = TRUE;
                                    }
                                }
                                else {
                                     ALOGE("BD address boot property not set");
                                     ignore_boot_prop = TRUE;
                                }
#endif //READ_BT_ADDR_FROM_PROP
#ifdef BT_NV_SUPPORT
                                    /* Always read BD address from NV file */
                                if(ignore_boot_prop && !bt_vendor_nv_read(1, q.bdaddr))
                                {
                                   /* Since the BD address is configured in boot time We should not be here */
                                   ALOGI("Failed to read BD address. Use the one from bluedroid stack/ftm");
                                }
#endif //BT_NV_SUPPORT
                                if(rome_soc_init(fd, (char*)q.bdaddr)<0) {
                                    retval = -1;
                                } else {
                                    ALOGV("rome_soc_init is completed");
                                    property_set("wc_transport.soc_initialized", "1");
                                    skip_init = false;
                                }
                            }
                            if (property_set("wc_transport.patch_dnld_inprog", "null") < 0) {
                                ALOGE("%s: Failed to set property", __FUNCTION__);
                            }

                            property_set("wc_transport.clean_up","0");
                            if (retval != -1) {

                                retval = start_hci_filter();
                                if (retval < 0) {
                                    ALOGE("%s: WCNSS_FILTER wouldn't have started in time\n", __func__);
                                } else {
#ifdef ENABLE_ANT
                                    if (is_ant_req) {
                                        ALOGI("%s: connect to ant channel", __func__);
                                        q.ant_fd = fd_filter = connect_to_local_socket("ant_sock");
                                    }
                                    else
#endif
                                    {
                                        ALOGI("%s: connect to bt channel", __func__);
                                        vnd_userial.fd = fd_filter = connect_to_local_socket("bt_sock");
                                    }

                                    if (fd_filter != -1) {
                                        ALOGI("%s: received the socket fd: %d is_ant_req: %d is_fm_req: %d\n",
                                                             __func__, fd_filter, is_ant_req,is_fm_req);
                                        if((strcmp(emb_wp_mode, "true") == 0) && !is_ant_req && !is_fm_req) {
                                             if (chipset_ver >= ROME_VER_3_0) {
                                                /* get rome supported feature request */
                                                ALOGE("%s: %x08 %0x", __FUNCTION__,chipset_ver, ROME_VER_3_0);
                                                rome_get_addon_feature_list(fd_filter);
                                            }
                                        }
                                        if (!skip_init) {
                                            /*Skip if already sent*/
                                            enable_controller_log(fd_filter, (is_ant_req || is_fm_req) );
                                            skip_init = true;
                                        }
                                        for (idx=0; idx < CH_MAX; idx++)
                                            (*fd_array)[idx] = fd_filter;
                                            retval = 1;
                                    }
                                    else {
                                        if (is_ant_req)
                                            ALOGE("Unable to connect to ANT Server Socket!!!");
                                        else
                                            ALOGE("Unable to connect to BT Server Socket!!!");
                                        retval = -1;
                                    }
                                }
                            } else {
                                if (q.soc_type == BT_SOC_ROME)
                                    ALOGE("Failed to initialize ROME Controller!!!");
                            }

                            if (fd >= 0) {
                                userial_clock_operation(fd, USERIAL_OP_CLK_OFF);
                                 /*Close the UART port*/
                                 close(fd);
                            }
                        }
                        break;
                    case BT_SOC_CHEROKEE:
                        {
                            property_get("ro.bluetooth.emb_wp_mode", emb_wp_mode, false);
                            retval = start_hci_filter();
                            if (retval < 0) {
                                ALOGE("WCNSS_FILTER wouldn't have started in time\n");
                                /*
                                 Set the following property to -1 so that the SSR cleanup routine
                                 can reset SOC.
                                 */
                                property_set("wc_transport.hci_filter_status", "-1");
                                property_set("wc_transport.start_hci", "false");
                                bt_powerup(0);
                            } else {
#ifdef ENABLE_ANT
                                if (is_ant_req) {
                                    ALOGI("%s: connect to ant channel", __func__);
                                    q.ant_fd = fd_filter = connect_to_local_socket("ant_sock");
                                }
                                else
#endif
#ifdef FM_OVER_UART
                                if (is_fm_req && (q.soc_type >=BT_SOC_ROME && q.soc_type < BT_SOC_RESERVED)) {
                                    ALOGI("%s: connect to fm channel", __func__);
                                    q.fm_fd = fd_filter = connect_to_local_socket("fm_sock");
                                }
                                else
#endif
                                {
                                    ALOGI("%s: connect to bt channel", __func__);
                                    vnd_userial.fd = fd_filter = connect_to_local_socket("bt_sock");

                                }
                                if (fd_filter != -1) {
                                    ALOGV("%s: received the socket fd: %d \n",
                                                             __func__, fd_filter);

                                    for (idx=0; idx < CH_MAX; idx++) {
                                        (*fd_array)[idx] = fd_filter;
                                    }
                                    retval = 1;
                                }
                                else {
#ifdef ENABLE_ANT
                                    if (is_ant_req)
                                        ALOGE("Unable to connect to ANT Server Socket!!!");
                                    else
#endif
#ifdef FM_OVER_UART
                                    if (is_fm_req)
                                        ALOGE("Unable to connect to FM Server Socket!!!");
                                    else
#endif
                                        ALOGE("Unable to connect to BT Server Socket!!!");
                                    retval = -1;
                                }
                            }
                        }
                        break;
                    default:
                        ALOGE("Unknown soc_type: 0x%x", q.soc_type);
                        break;
                  }
            } break;
#ifdef ENABLE_ANT
        case BT_VND_OP_ANT_USERIAL_CLOSE:
            {
                pthread_mutex_lock(&q_lock);
                ALOGI("bt-vendor : BT_VND_OP_ANT_USERIAL_CLOSE");
                property_set("wc_transport.clean_up","1");
                if (q.ant_fd != -1) {
                    ALOGE("closing ant_fd");
                    close(q.ant_fd);
                    q.ant_fd = -1;
                }
                pthread_mutex_unlock(&q_lock);
            }
            break;
#endif
#ifdef FM_OVER_UART
        case BT_VND_OP_FM_USERIAL_CLOSE:
            {
                ALOGI("bt-vendor : BT_VND_OP_FM_USERIAL_CLOSE");
                property_set("wc_transport.clean_up","1");
                if (q.fm_fd != -1) {
                    ALOGE("closing fm_fd");
                    close(q.fm_fd);
                    q.fm_fd = -1;
                }
                break;
            }
#endif
        case BT_VND_OP_USERIAL_CLOSE:
            {
                ALOGI("bt-vendor : BT_VND_OP_USERIAL_CLOSE soc_type: %d", q.soc_type);
                switch(q.soc_type)
                {
                    case BT_SOC_DEFAULT:
                        bt_hci_deinit_transport(q.fd);
                        break;
                    case BT_SOC_ROME:
                    case BT_SOC_AR3K:
                    case BT_SOC_CHEROKEE:
                    {
                        pthread_mutex_lock(&q_lock);
                        property_set("wc_transport.clean_up","1");
                        userial_vendor_close();
                        pthread_mutex_unlock(&q_lock);
                        break;
                    }
                    default:
                        ALOGE("Unknown soc_type: 0x%x", q.soc_type);
                        break;
                }
            }
            break;

        case BT_VND_OP_GET_LPM_IDLE_TIMEOUT:
            {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                uint32_t *timeout_ms = (uint32_t *) param;
                *timeout_ms = 1000;
            }

            break;

        case BT_VND_OP_LPM_SET_MODE:
            if (q.soc_type == BT_SOC_AR3K) {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                uint8_t *mode = (uint8_t *) param;

                if (*mode) {
                    lpm_set_ar3k(UPIO_LPM_MODE, UPIO_ASSERT, 0);
                }
                else {
                    lpm_set_ar3k(UPIO_LPM_MODE, UPIO_DEASSERT, 0);
                }
                q.cb->lpm_cb(BT_VND_OP_RESULT_SUCCESS);
            } else {
                int lpm_result = BT_VND_OP_RESULT_SUCCESS;

                property_get("persist.service.bdroid.lpmcfg", lpm_config, "all");
                ALOGI("%s: property_get: persist.service.bdroid.lpmcfg: %s",
                            __func__, lpm_config);

                if (!strcmp(lpm_config, "all")) {
                    // respond with success since we want to hold wake lock through LPM
                    lpm_result = BT_VND_OP_RESULT_SUCCESS;
                }
                else {
                    lpm_result = BT_VND_OP_RESULT_FAIL;
                }

                q.cb->lpm_cb(lpm_result);
            }
            break;

        case BT_VND_OP_LPM_WAKE_SET_STATE: {
            switch(q.soc_type) {
            case BT_SOC_CHEROKEE:
            case BT_SOC_ROME: {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                uint8_t *state = (uint8_t *) param;
                uint8_t wake_assert = (*state == BT_VND_LPM_WAKE_ASSERT) ? \
                            BT_VND_LPM_WAKE_ASSERT : BT_VND_LPM_WAKE_DEASSERT;

                if (wake_assert == 0)
                    ALOGV("ASSERT: Waking up BT-Device");
                else if (wake_assert == 1)
                    ALOGV("DEASSERT: Allowing BT-Device to Sleep");

#ifdef QCOM_BT_SIBS_ENABLE
                ALOGI("Invoking HCI H4 callback function");
                q.cb->lpm_set_state_cb(wake_assert);
#endif
            }
            break;
            case BT_SOC_AR3K: {
                if (!param) {
                    ALOGE("opcode = %d: param is null", opcode_init);
                    break;
                }
                uint8_t *state = (uint8_t *) param;
                uint8_t wake_assert = (*state == BT_VND_LPM_WAKE_ASSERT) ? \
                                                UPIO_ASSERT : UPIO_DEASSERT;
                lpm_set_ar3k(UPIO_BT_WAKE, wake_assert, 0);
            }
            case BT_SOC_DEFAULT:
                break;
            default:
                ALOGE("Unknown soc_type: 0x%x", q.soc_type);
                break;
            }
        }
            break;
        case BT_VND_OP_EPILOG: {
#if (HW_NEED_END_WITH_HCI_RESET == FALSE)
            q.cb->epilog_cb(BT_VND_OP_RESULT_SUCCESS);
#else
                switch(q.soc_type)
                {
                  case BT_SOC_CHEROKEE:
                  case BT_SOC_ROME:
                       {
                           char value[PROPERTY_VALUE_MAX] = {'\0'};
                           property_get("wc_transport.hci_filter_status", value, "0");
                           if(is_soc_initialized()&& (strcmp(value,"1") == 0))
                           {
                              __hw_epilog_process();
                           }
                           else
                           {
                                q.cb->epilog_cb(BT_VND_OP_RESULT_SUCCESS);
                           }
                       }
                       break;
                  default:
                       __hw_epilog_process();
                       break;
                }
#endif
            }
            break;
        case BT_VND_OP_GET_LINESPEED:
            {
                retval = -1;
                if(!is_soc_initialized()) {
                     ALOGE("BT_VND_OP_GET_LINESPEED: error"
                         " - transport driver not initialized!");
                     break;
                }

                switch(q.soc_type)
                {
                    case BT_SOC_CHEROKEE:
                            retval = 3200000;
                        break;
                    case BT_SOC_ROME:
                            retval = 3000000;
                        break;
                    default:
                        retval = userial_vendor_get_baud();
                        break;
                 }
                break;
            }
    }

out:
    ALOGV("--%s", __FUNCTION__);
    return retval;
}

static void ssr_cleanup(int reason)
{
    int pwr_state = BT_VND_PWR_OFF;
    int ret;
    unsigned char trig_ssr = 0xEE;
#ifndef ENABLE_ANT
    (void)reason;  // unused
#endif

    ALOGI("++%s", __FUNCTION__);

    if (property_set("wc_transport.patch_dnld_inprog", "null") < 0) {
        ALOGE("Failed to set property");
    }

    if (q.soc_type >= BT_SOC_ROME && q.soc_type < BT_SOC_RESERVED) {
#ifdef ENABLE_ANT
        /*Indicate to filter by sending special byte */
        if (reason == CMD_TIMEOUT) {
            trig_ssr = 0xEE;
            ret = write (vnd_userial.fd, &trig_ssr, 1);
            ALOGI("Trig_ssr is being sent to BT socket, ret %d err %s",
                        ret, strerror(errno));

            if (is_debug_force_special_bytes()) {
                /*
                 * Then we should send special byte to crash SOC in
                 * WCNSS_Filter, so we do not need to power off UART here.
                 */
                goto out;
            }
        }

        /* Close both ANT channel */
        op(BT_VND_OP_ANT_USERIAL_CLOSE, NULL);
#endif
        /* Close both BT channel */
        op(BT_VND_OP_USERIAL_CLOSE, NULL);

#ifdef FM_OVER_UART
        op(BT_VND_OP_FM_USERIAL_CLOSE, NULL);
#endif
        /*CTRL OFF twice to make sure hw
         * turns off*/
#ifdef ENABLE_ANT
        op(BT_VND_OP_POWER_CTRL, &pwr_state);
#endif
    }
    /*Generally switching of chip should be enough*/
    op(BT_VND_OP_POWER_CTRL, &pwr_state);

out:
    ALOGI("--%s", __FUNCTION__);
}

/** Closes the interface */
static void cleanup(void)
{
    ALOGI("cleanup");

    pthread_mutex_lock(&q_lock);
    q.cb = NULL;
    pthread_mutex_unlock(&q_lock);

#ifdef WIFI_BT_STATUS_SYNC
    isInit = 0;
#endif /* WIFI_BT_STATUS_SYNC */
}

/* Check for one of the cients ANT/BT patch download is already in
** progress if yes wait till complete
*/
void wait_for_patch_download(bool is_ant_req) {
    ALOGV("%s:", __FUNCTION__);
    char inProgress[PROPERTY_VALUE_MAX] = {'\0'};
    while (1) {
        property_get("wc_transport.patch_dnld_inprog", inProgress, "null");

        if(is_ant_req && !(strcmp(inProgress,"bt"))) {
           //ANT request, wait for BT to finish
           usleep(50000);
        }
        else if(!is_ant_req && !(strcmp(inProgress,"ant"))) {
          //BT request, wait for ANT to finish
           usleep(50000);
        }
        else {
           ALOGI("%s: patch download completed", __FUNCTION__);
           break;
        }
    }
}

bool is_download_progress () {
    char inProgress[PROPERTY_VALUE_MAX] = {'\0'};
    bool retval = false;

    ALOGV("%s:", __FUNCTION__);

    if ((q.soc_type = get_bt_soc_type()) < 0) {
        ALOGE("%s: Failed to detect BT SOC Type", __FUNCTION__);
        return -1;
    }

    switch(q.soc_type)
    {
        case BT_SOC_ROME:
            ALOGI("%s: ROME case", __func__);
            property_get("wc_transport.patch_dnld_inprog", inProgress, "null");
            if(strcmp(inProgress,"null") == 0) {
                retval = false;
            } else {
                 retval = true;
            }
            break;
        case BT_SOC_CHEROKEE:
            ALOGI("%s: CHEROKEE case", __func__);
            break;
        case BT_SOC_DEFAULT:
            break;
        default:
            ALOGE("Unknown btSocType: 0x%x", q.soc_type);
            break;
    }
    return retval;
}

static bool is_debug_force_special_bytes() {
    int ret = 0;
    char value[PROPERTY_VALUE_MAX] = {'\0'};
    bool enabled = false;
#ifdef ENABLE_DBG_FLAGS
    enabled = true;
#endif

    ret = property_get("wc_transport.force_special_byte", value, NULL);

    if (ret) {
        enabled = (strcmp(value, "false") ==0) ? false : true;
        ALOGV("%s: wc_transport.force_special_byte: %s, enabled: %d ",
            __func__, value, enabled);
    }

    return enabled;
}

// Entry point of DLib
const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = {
    sizeof(bt_vendor_interface_t),
    init,
    op,
    cleanup
};