/******************************************************************************
 *
 *  Copyright (C) 2014  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.
 *
 ******************************************************************************/

#include <string.h>
#include "bt_target.h"

#if (BLE_INCLUDED == TRUE)
#if BLE_BATCH_SCAN_INCLUDED == TRUE
#include "bt_types.h"
#include "hcimsgs.h"
#include "btu.h"
#include "btm_int.h"
#include "bt_utils.h"
#include "hcidefs.h"
#include "btm_ble_api.h"
#include "vendor_ble.h"

#define BTM_BLE_ADV_FILT_META_HDR_LENGTH 3
#define BTM_BLE_ADV_FILT_FEAT_SELN_LEN 13

#define BTM_BLE_PF_BIT_TO_MASK(x)          (UINT16)(1 << (x))


tBTM_BLE_ADV_FILTER_CB btm_ble_adv_filt_cb;
tBTM_BLE_VSC_CB cmn_ble_vsc_cb;
static const BD_ADDR     na_bda= {0};

static UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action,
                                  UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr, UINT8 num_available);

#define BTM_BLE_SET_SCAN_PF_OPCODE(x, y) (((x)<<4)|y)
#define BTM_BLE_GET_SCAN_PF_SUBCODE(x)    ((x) >> 4)
#define BTM_BLE_GET_SCAN_PF_ACTION(x)    ((x) & 0x0f)
#define BTM_BLE_INVALID_COUNTER     0xff


/* length of each multi adv sub command */
#define BTM_BLE_ADV_FILTER_ENB_LEN                       3

/* length of each batch scan command */
#define BTM_BLE_ADV_FILTER_CLEAR_LEN            3
#define BTM_BLE_ADV_FILTER_LEN     2

#define BTM_BLE_ADV_FILT_CB_EVT_MASK       0xF0
#define BTM_BLE_ADV_FILT_SUBCODE_MASK      0x0F


/*******************************************************************************
**
** Function         btm_ble_advfilt_enq_op_q
**
** Description      enqueue an adv filter operation in q to check command complete
**                  status
**
** Returns          void
**
*******************************************************************************/
void btm_ble_advfilt_enq_op_q(UINT8 action, UINT8 cond_type, tBTM_BLE_REF_VALUE ref)
{
    btm_ble_adv_filt_cb.op_q.action_condtype[btm_ble_adv_filt_cb.op_q.next_idx] =
                                (action |(cond_type << 4));
    btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.next_idx] = ref;
    BTM_TRACE_DEBUG("btm_ble_advfilt_enq_op_q: act_condtype:%d, action:%d, Cond_type:%d",
        btm_ble_adv_filt_cb.op_q.action_condtype[btm_ble_adv_filt_cb.op_q.next_idx], action, cond_type);
    btm_ble_adv_filt_cb.op_q.next_idx = (btm_ble_adv_filt_cb.op_q.next_idx + 1)
                    % BTM_BLE_PF_TYPE_MAX;
}

/*******************************************************************************
**
** Function         btm_ble_advfilt_deq_op_q
**
** Description      dequeue an adv filter operation from q when command complete
**                  is received
**
** Returns          void
**
*******************************************************************************/
void btm_ble_advfilt_deq_op_q(UINT8 *p_action,UINT8 *p_cond_type, tBTM_BLE_REF_VALUE *p_ref)
{
    *p_cond_type =
        (btm_ble_adv_filt_cb.op_q.action_condtype[btm_ble_adv_filt_cb.op_q.pending_idx] >> 4);
    *p_action =
        (btm_ble_adv_filt_cb.op_q.action_condtype[btm_ble_adv_filt_cb.op_q.pending_idx]
                & BTM_BLE_ADV_FILT_SUBCODE_MASK);
    *p_ref = btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.pending_idx];
    btm_ble_adv_filt_cb.op_q.pending_idx = (btm_ble_adv_filt_cb.op_q.pending_idx + 1)
        % BTM_BLE_PF_TYPE_MAX;
}

/*******************************************************************************
**
** Function         btm_ble_ocf_to_condtype
**
** Description      Convert OCF to cond type
**
** Returns          Returns ocf value
**
*******************************************************************************/
UINT8 btm_ble_ocf_to_condtype(UINT8 ocf)
{
    UINT8 cond_type = 0;

    switch(ocf)
    {
        case BTM_BLE_META_PF_FEAT_SEL:
           cond_type = BTM_BLE_META_PF_FEAT_SEL;
           break;
        case BTM_BLE_META_PF_ADDR:
          cond_type = BTM_BLE_PF_ADDR_FILTER;
          break;
        case BTM_BLE_META_PF_UUID:
          cond_type = BTM_BLE_PF_SRVC_UUID;
          break;
        case BTM_BLE_META_PF_SOL_UUID:
           cond_type = BTM_BLE_PF_SRVC_SOL_UUID;
           break;
        case BTM_BLE_META_PF_LOCAL_NAME:
           cond_type = BTM_BLE_PF_LOCAL_NAME;
           break;
        case BTM_BLE_META_PF_MANU_DATA:
           cond_type = BTM_BLE_PF_MANU_DATA;
           break;
        case BTM_BLE_META_PF_SRVC_DATA:
           cond_type = BTM_BLE_PF_SRVC_DATA_PATTERN;
           break;
        case BTM_BLE_META_PF_ALL:
           cond_type = BTM_BLE_PF_TYPE_ALL;
           break;
        default:
           cond_type = BTM_BLE_PF_TYPE_MAX;
           break;
    }
    return cond_type;
}

/*******************************************************************************
**
** Function         btm_ble_scan_pf_cmpl_cback
**
** Description      the BTM BLE customer feature VSC complete callback for ADV PF filtering
**
** Returns          pointer to the counter if found; NULL otherwise.
**
*******************************************************************************/
void btm_ble_scan_pf_cmpl_cback(tBTM_VSC_CMPL *p_params)
{
    UINT8  status = 0;
    UINT8  *p = p_params->p_param_buf, op_subcode = 0, action = 0xff;
    UINT16  evt_len = p_params->param_len;
    UINT8   cond_type = BTM_BLE_PF_TYPE_MAX;
    UINT8   num_avail = 0;
    tBTM_BLE_REF_VALUE ref_value = 0;

    if (evt_len < 3 || evt_len > 4)
    {
        BTM_TRACE_ERROR("cannot interpret APCF callback status = %d, length = %d", status, evt_len);
        btm_ble_advfilt_deq_op_q(&action, &cond_type, &ref_value);
        return;
    }

    STREAM_TO_UINT8(status, p);

    btm_ble_advfilt_deq_op_q(&action, &cond_type, &ref_value);

    STREAM_TO_UINT8(op_subcode, p);
    STREAM_TO_UINT8(action, p);

    /* Ignore the event, if it is not the same one expected */
    if(3 == evt_len)
    {
        if(cond_type != op_subcode)
        {
             BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback-Incorrect opcode :%d, %d, %d, %d, %d, %d",
                                        cond_type, op_subcode, action, evt_len, ref_value, status);
             return;
        }
        else
        {
            btm_ble_adv_filt_cb.p_filt_stat_cback(action, status, ref_value);
            BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback enabled/disabled, %d, %d, %d, %d",
                                         cond_type, action, status, ref_value);
            return;
        }
    }

    if (4 == evt_len && cond_type != btm_ble_ocf_to_condtype(op_subcode))
    {
        BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback-Incorrect opcode: %d, %d, %d, %d, %d",
                                cond_type, op_subcode, action, status, ref_value);
        return;
    }

    STREAM_TO_UINT8(num_avail, p);
    switch (op_subcode)
    {
        case BTM_BLE_META_PF_ADDR:
        case BTM_BLE_META_PF_UUID:
        case BTM_BLE_META_PF_SOL_UUID:
        case BTM_BLE_META_PF_LOCAL_NAME:
        case BTM_BLE_META_PF_MANU_DATA:
        case BTM_BLE_META_PF_SRVC_DATA:
           BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback: %d, %d, %d, %d, %d", cond_type, action,
                                                        status, ref_value, num_avail);

           if (HCI_SUCCESS == status)
           {
               if (memcmp(&btm_ble_adv_filt_cb.cur_filter_target.bda, &na_bda, BD_ADDR_LEN) == 0)
                   btm_ble_cs_update_pf_counter(action, cond_type, NULL, num_avail);
               else
                   btm_ble_cs_update_pf_counter(action, cond_type,
                            &btm_ble_adv_filt_cb.cur_filter_target, num_avail);
           }

           /* send ADV PF operation complete */
           btm_ble_adv_filt_cb.op_type = 0;
           btm_ble_adv_filt_cb.p_scan_cfg_cback(action, cond_type, num_avail, status, ref_value);
           break;

        case BTM_BLE_META_PF_FEAT_SEL:
            if(NULL != btm_ble_adv_filt_cb.p_filt_param_cback)
               btm_ble_adv_filt_cb.p_filt_param_cback(action, num_avail, ref_value, status);
            BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback-Feat sel event: %d, %d, %d, %d, %d",
                                cond_type, action, status, ref_value, num_avail);
            break;

        default:
            BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback: unknown operation: %d", op_subcode);
            break;
    }
}

/*******************************************************************************
**
** Function         btm_ble_find_addr_filter_counter
**
** Description      find the per bd address ADV payload filter counter by BD_ADDR.
**
** Returns          pointer to the counter if found; NULL otherwise.
**
*******************************************************************************/
tBTM_BLE_PF_COUNT* btm_ble_find_addr_filter_counter(tBLE_BD_ADDR *p_le_bda)
{
    UINT8               i;
    tBTM_BLE_PF_COUNT   *p_addr_filter = &btm_ble_adv_filt_cb.addr_filter_count[1];

    if (p_le_bda == NULL)
        return &btm_ble_adv_filt_cb.addr_filter_count[0];

    for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++)
    {
        if (p_addr_filter->in_use &&
            memcmp(p_le_bda->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0)
        {
            return p_addr_filter;
        }
    }
    return NULL;
}

/*******************************************************************************
**
** Function         btm_ble_alloc_addr_filter_counter
**
** Description      allocate the per device adv payload filter counter.
**
** Returns          pointer to the counter if allocation succeed; NULL otherwise.
**
*******************************************************************************/
tBTM_BLE_PF_COUNT * btm_ble_alloc_addr_filter_counter(BD_ADDR bd_addr)
{
    UINT8               i;
    tBTM_BLE_PF_COUNT   *p_addr_filter = &btm_ble_adv_filt_cb.addr_filter_count[1];

    for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++)
    {
        if (memcmp(na_bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0)
        {
            memcpy(p_addr_filter->bd_addr, bd_addr, BD_ADDR_LEN);
            p_addr_filter->in_use = TRUE;
            return p_addr_filter;
        }
    }
    return NULL;
}
/*******************************************************************************
**
** Function         btm_ble_dealloc_addr_filter_counter
**
** Description      de-allocate the per device adv payload filter counter.
**
** Returns          TRUE if deallocation succeed; FALSE otherwise.
**
*******************************************************************************/
BOOLEAN btm_ble_dealloc_addr_filter_counter(tBLE_BD_ADDR *p_bd_addr, UINT8 filter_type)
{
    UINT8               i;
    tBTM_BLE_PF_COUNT   *p_addr_filter = &btm_ble_adv_filt_cb.addr_filter_count[1];
    BOOLEAN             found = FALSE;

    if (BTM_BLE_PF_TYPE_ALL == filter_type && NULL == p_bd_addr)
        memset(&btm_ble_adv_filt_cb.addr_filter_count[0], 0, sizeof(tBTM_BLE_PF_COUNT));

    for (i = 0; i < cmn_ble_vsc_cb.max_filter; i ++, p_addr_filter ++)
    {
        if ((p_addr_filter->in_use) && (NULL == p_bd_addr ||
            (NULL != p_bd_addr &&
            memcmp(p_bd_addr->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0)))
        {
            found = TRUE;
            memset(p_addr_filter, 0, sizeof(tBTM_BLE_PF_COUNT));

            if (NULL != p_bd_addr) break;
        }
    }
    return found;
}

/*******************************************************************************
**
** Function         btm_ble_update_pf_local_name
**
** Description      this function update(add,delete or clear) the adv lcoal name filtering condition.
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_update_pf_local_name(tBTM_BLE_SCAN_COND_OP action,
                                         tBTM_BLE_PF_FILT_INDEX filt_index,
                                         tBTM_BLE_PF_COND_PARAM *p_cond)
{
    tBTM_BLE_PF_LOCAL_NAME_COND *p_local_name = (p_cond == NULL) ? NULL : &p_cond->local_name;
    UINT8       param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH],
                *p = param,
                len = BTM_BLE_ADV_FILT_META_HDR_LENGTH;
    tBTM_STATUS st = BTM_ILLEGAL_VALUE;

    memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH);

    UINT8_TO_STREAM(p, BTM_BLE_META_PF_LOCAL_NAME);
    UINT8_TO_STREAM(p, action);

    /* Filter index */
    UINT8_TO_STREAM(p, filt_index);

    if (BTM_BLE_SCAN_COND_ADD == action ||
        BTM_BLE_SCAN_COND_DELETE == action)
    {
        if (NULL == p_local_name)
            return st;

        if (p_local_name->data_len > BTM_BLE_PF_STR_LEN_MAX)
            p_local_name->data_len = BTM_BLE_PF_STR_LEN_MAX;

        ARRAY_TO_STREAM(p, p_local_name->p_data, p_local_name->data_len);
        len += p_local_name->data_len;
    }

    /* send local name filter */
    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                              len,
                              param,
                              btm_ble_scan_pf_cmpl_cback))
            != BTM_NO_RESOURCES)
    {
        memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
    }
    else
    {
        BTM_TRACE_ERROR("Local Name PF filter update failed");
    }

    return st;
}


/*******************************************************************************
**
** Function         btm_ble_update_srvc_data_change
**
** Description      this function update(add/remove) service data change filter.
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_update_srvc_data_change(tBTM_BLE_SCAN_COND_OP action,
                                       tBTM_BLE_PF_FILT_INDEX filt_index,
                                       tBTM_BLE_PF_COND_PARAM *p_cond)
{
    tBTM_STATUS st = BTM_ILLEGAL_VALUE;
    tBLE_BD_ADDR   *p_bd_addr = p_cond ? &p_cond->target_addr : NULL;
    UINT8           num_avail = (action == BTM_BLE_SCAN_COND_ADD) ? 0 : 1;

    if (btm_ble_cs_update_pf_counter (action, BTM_BLE_PF_SRVC_DATA, p_bd_addr, num_avail)
                    != BTM_BLE_INVALID_COUNTER)
        st = BTM_SUCCESS;

    return st;
}

/*******************************************************************************
**
** Function         btm_ble_update_pf_manu_data
**
** Description      this function update(add,delete or clear) the adv manufacturer
**                  data filtering condition.
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_update_pf_manu_data(tBTM_BLE_SCAN_COND_OP action,
                                        tBTM_BLE_PF_FILT_INDEX filt_index,
                                        tBTM_BLE_PF_COND_PARAM *p_data,
                                        tBTM_BLE_PF_COND_TYPE cond_type)
{
    tBTM_BLE_PF_MANU_COND *p_manu_data = (p_data == NULL) ? NULL : &p_data->manu_data;
    tBTM_BLE_PF_SRVC_PATTERN_COND *p_srvc_data = (p_data == NULL) ? NULL : &p_data->srvc_data;

    UINT8 param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH],
          *p = param,
          len = BTM_BLE_ADV_FILT_META_HDR_LENGTH;
    tBTM_STATUS st = BTM_ILLEGAL_VALUE;

    if(NULL!= p_data && 0 == p_data->manu_data.data_len && 0 == p_data->srvc_data.data_len)
        return st;

    memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX
                    + BTM_BLE_ADV_FILT_META_HDR_LENGTH);

    if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type)
    {
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_SRVC_DATA);
    }
    else
    {
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_MANU_DATA);
    }

    UINT8_TO_STREAM(p, action);

    /* Filter index */
    UINT8_TO_STREAM(p, filt_index);

    if (BTM_BLE_SCAN_COND_ADD == action ||
        BTM_BLE_SCAN_COND_DELETE == action)
    {
        if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type)
        {
            if (NULL == p_srvc_data)
                return st;
            if (p_srvc_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2))
                p_srvc_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2);

            if(p_srvc_data->data_len > 0)
            {
                ARRAY_TO_STREAM(p, p_srvc_data->p_pattern, p_srvc_data->data_len);
                len += (p_srvc_data->data_len);
                ARRAY_TO_STREAM(p, p_srvc_data->p_pattern_mask, p_srvc_data->data_len);
            }

            len += (p_srvc_data->data_len);
            BTM_TRACE_DEBUG("Service data length: %d", len);
        }
        else
        {
            if (NULL == p_manu_data)
                return st;
           if (p_manu_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2))
               p_manu_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2);

           UINT16_TO_STREAM(p, p_manu_data->company_id);

           if(p_manu_data->data_len > 0)
                ARRAY_TO_STREAM(p, p_manu_data->p_pattern, p_manu_data->data_len);
           len += (p_manu_data->data_len + 2);

           if (p_manu_data->company_id_mask != 0)
           {
               UINT16_TO_STREAM (p, p_manu_data->company_id_mask);
           }
           else
           {
               memset(p, 0xff, 2);
               p += 2;
           }

           if(p_manu_data->data_len > 0)
              ARRAY_TO_STREAM(p, p_manu_data->p_pattern_mask, p_manu_data->data_len);
           len += (p_manu_data->data_len + 2);

           BTM_TRACE_DEBUG("Manuf data length: %d", len);
        }
    }

    /* send manufacturer*/
    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                              len,
                              param,
                              btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES)
    {
        memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
    }
    else
    {
        BTM_TRACE_ERROR("manufacturer data PF filter update failed");
    }

    return st;
}

/*******************************************************************************
**
** Function         btm_ble_cs_update_pf_counter
**
** Description      this function is to update the adv data payload filter counter
**
** Returns          current number of the counter; BTM_BLE_INVALID_COUNTER if
**                  counter update failed.
**
*******************************************************************************/
UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action,
                                  UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr,
                                  UINT8 num_available)
{
    tBTM_BLE_PF_COUNT   *p_addr_filter = NULL;
    UINT8               *p_counter = NULL;

    BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb);

    if (cond_type > BTM_BLE_PF_TYPE_ALL)
    {
        BTM_TRACE_ERROR("unknown PF filter condition type %d", cond_type);
        return BTM_BLE_INVALID_COUNTER;
    }

    /* for these three types of filter, always generic */
    if (BTM_BLE_PF_ADDR_FILTER == cond_type ||
        BTM_BLE_PF_MANU_DATA == cond_type ||
        BTM_BLE_PF_LOCAL_NAME == cond_type ||
        BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type)
        p_bd_addr = NULL;

    if ((p_addr_filter = btm_ble_find_addr_filter_counter(p_bd_addr)) == NULL &&
        BTM_BLE_SCAN_COND_ADD == action)
    {
        p_addr_filter = btm_ble_alloc_addr_filter_counter(p_bd_addr->bda);
    }

    if (NULL != p_addr_filter)
    {
        /* all filter just cleared */
        if ((BTM_BLE_PF_TYPE_ALL == cond_type && BTM_BLE_SCAN_COND_CLEAR == action) ||
            /* or bd address filter been deleted */
            (BTM_BLE_PF_ADDR_FILTER == cond_type &&
             (BTM_BLE_SCAN_COND_DELETE == action || BTM_BLE_SCAN_COND_CLEAR == action)))
        {
            btm_ble_dealloc_addr_filter_counter(p_bd_addr, cond_type);
        }
        /* if not feature selection, update new addition/reduction of the filter counter */
        else if (cond_type != BTM_BLE_PF_TYPE_ALL)
        {
            p_counter = p_addr_filter->pf_counter;
            if(num_available > 0)
               p_counter[cond_type] += 1;

            /* update corresponding feature mask */
            if (num_available > 0)
                p_addr_filter->feat_mask |= (BTM_BLE_PF_BIT_TO_MASK(cond_type));
            else
                p_addr_filter->feat_mask &= ~(BTM_BLE_PF_BIT_TO_MASK(cond_type));

            BTM_TRACE_DEBUG("counter = %d, maxfilt = %d, num_avbl=%d, feat_mask = %d",
                p_counter[cond_type], cmn_ble_vsc_cb.max_filter, num_available,
                p_addr_filter->feat_mask);
            return p_counter[cond_type];
        }
    }
    else
    {
        BTM_TRACE_ERROR("no matching filter counter found");
    }
    /* no matching filter located and updated */
    return BTM_BLE_INVALID_COUNTER;
}


/*******************************************************************************
**
** Function         btm_ble_update_addr_filter
**
** Description      this function update(add,delete or clear) the address filter of adv.
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_update_addr_filter(tBTM_BLE_SCAN_COND_OP action,
                                       tBTM_BLE_PF_FILT_INDEX filt_index,
                                       tBTM_BLE_PF_COND_PARAM *p_cond)
{
    UINT8       param[BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH],
                * p= param;
    tBTM_STATUS st = BTM_ILLEGAL_VALUE;
    tBLE_BD_ADDR *p_addr = (p_cond == NULL) ? NULL : &p_cond->target_addr;

    memset(param, 0, BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH);

    UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR);
    UINT8_TO_STREAM(p, action);

    /* Filter index */
    UINT8_TO_STREAM(p, filt_index);

    if (BTM_BLE_SCAN_COND_ADD == action ||
        BTM_BLE_SCAN_COND_DELETE == action)
    {
        if (NULL == p_addr)
            return st;

        BDADDR_TO_STREAM(p, p_addr->bda);
        UINT8_TO_STREAM(p, p_addr->type);
    }
    /* send address filter */
    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                              (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN),
                              param,
                              btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES)
    {
        memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
    }
    else
    {
        BTM_TRACE_ERROR("Broadcaster Address Filter Update failed");
    }
    return st;
}

/*******************************************************************************
**
** Function         btm_ble_update_uuid_filter
**
** Description      this function update(add,delete or clear) service UUID filter.
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_update_uuid_filter(tBTM_BLE_SCAN_COND_OP action,
                                       tBTM_BLE_PF_FILT_INDEX filt_index,
                                       tBTM_BLE_PF_COND_TYPE filter_type,
                                       tBTM_BLE_PF_COND_PARAM *p_cond)
{
    UINT8       param[BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH],
                * p= param,
                len = BTM_BLE_ADV_FILT_META_HDR_LENGTH;
    tBTM_STATUS st = BTM_ILLEGAL_VALUE;
    tBTM_BLE_PF_UUID_COND *p_uuid_cond;
    UINT8           evt_type;

    memset(param, 0, BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH);

    if (BTM_BLE_PF_SRVC_UUID == filter_type)
    {
        evt_type = BTM_BLE_META_PF_UUID;
        p_uuid_cond = p_cond ? &p_cond->srvc_uuid : NULL;
    }
    else
    {
        evt_type = BTM_BLE_META_PF_SOL_UUID;
        p_uuid_cond = p_cond ? &p_cond->solicitate_uuid : NULL;
    }

    if (NULL == p_uuid_cond && action != BTM_BLE_SCAN_COND_CLEAR)
    {
        BTM_TRACE_ERROR("Illegal param for add/delete UUID filter");
        return st;
    }

    /* need to add address fitler first, if adding per bda UUID filter without address filter */
    if (BTM_BLE_SCAN_COND_ADD == action &&
        p_uuid_cond->p_target_addr &&
        btm_ble_find_addr_filter_counter(p_uuid_cond->p_target_addr) == NULL)
    {
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR);
        UINT8_TO_STREAM(p, action);

        /* Filter index */
        UINT8_TO_STREAM(p, filt_index);

        BDADDR_TO_STREAM(p, p_uuid_cond->p_target_addr->bda);
        UINT8_TO_STREAM(p, p_uuid_cond->p_target_addr->type);

        /* send address filter */
        if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                                  (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN),
                                  param,
                                  btm_ble_scan_pf_cmpl_cback)) == BTM_NO_RESOURCES)
        {
            BTM_TRACE_ERROR("Update Address filter into controller failed.");
            return st;
        }
        BTM_TRACE_DEBUG("Updated Address filter");
    }

    p = param;
    UINT8_TO_STREAM(p, evt_type);
    UINT8_TO_STREAM(p, action);

    /* Filter index */
    UINT8_TO_STREAM(p, filt_index);

    if (BTM_BLE_SCAN_COND_ADD == action ||
        BTM_BLE_SCAN_COND_DELETE == action)
    {
        if (p_uuid_cond->uuid.len == LEN_UUID_16)
        {
            UINT16_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid16);
            len += LEN_UUID_16;
        }
        else if (p_uuid_cond->uuid.len == LEN_UUID_32)/*4 bytes */
        {
            UINT32_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid32);
            len += LEN_UUID_32;
        }
        else if (p_uuid_cond->uuid.len == LEN_UUID_128)
        {
            ARRAY_TO_STREAM (p, p_uuid_cond->uuid.uu.uuid128, LEN_UUID_128);
            len += LEN_UUID_128;
        }
        else
        {
            BTM_TRACE_ERROR("illegal UUID length: %d", p_uuid_cond->uuid.len);
            return BTM_ILLEGAL_VALUE;
        }

        if (NULL != p_uuid_cond->p_uuid_mask)
        {
            if (p_uuid_cond->uuid.len == LEN_UUID_16)
            {
                UINT16_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid16_mask);
                len += LEN_UUID_16;
            }
            else if (p_uuid_cond->uuid.len == LEN_UUID_32)/*4 bytes */
            {
                UINT32_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid32_mask);
                len += LEN_UUID_32;
            }
            else if (p_uuid_cond->uuid.len == LEN_UUID_128)
            {
                ARRAY_TO_STREAM (p, p_uuid_cond->p_uuid_mask->uuid128_mask, LEN_UUID_128);
                len += LEN_UUID_128;
            }
        }
        else
        {
            memset(p, 0xff, p_uuid_cond->uuid.len);
            len += p_uuid_cond->uuid.len;
        }
    }

    BTM_TRACE_DEBUG("btm_ble_update_uuid_filter : %d, %d, %d, %d", filter_type, evt_type,
                    p_uuid_cond->uuid.len, len);
    /* send UUID filter update */
    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                              len,
                              param,
                              btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES)
    {
        if (p_uuid_cond && p_uuid_cond->p_target_addr)
            memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_uuid_cond->p_target_addr,
                    sizeof(tBLE_BD_ADDR));
        else
            memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
    }
    else
    {
        BTM_TRACE_ERROR("UUID filter udpating failed");
    }

    return st;
}


/*******************************************************************************
**
** Function         btm_ble_clear_scan_pf_filter
**
** Description      clear all adv payload filter by de-select all the adv pf feature bits
**
**
** Returns          BTM_SUCCESS if sucessful,
**                  BTM_ILLEGAL_VALUE if paramter is not valid.
**
*******************************************************************************/
tBTM_STATUS btm_ble_clear_scan_pf_filter(tBTM_BLE_SCAN_COND_OP action,
                                       tBTM_BLE_PF_FILT_INDEX filt_index,
                                       tBTM_BLE_PF_COND_PARAM *p_cond,
                                       tBTM_BLE_REF_VALUE ref_value)
{
    tBLE_BD_ADDR *p_target = (p_cond == NULL)? NULL : &p_cond->target_addr;
    tBTM_BLE_PF_COUNT *p_bda_filter;
    tBTM_STATUS     st = BTM_WRONG_MODE;
    UINT8           param[20], *p;

    if (BTM_BLE_SCAN_COND_CLEAR != action)
    {
        BTM_TRACE_ERROR("unable to perform action:%d for generic adv filter type", action);
        return BTM_ILLEGAL_VALUE;
    }

    p = param;
    memset(param, 0, 20);

    p_bda_filter = btm_ble_find_addr_filter_counter(p_target);

    if (NULL == p_bda_filter ||
        /* not a generic filter, and feature selection is empty */
        (p_target != NULL && p_bda_filter && 0 == p_bda_filter->feat_mask))
    {
        BTM_TRACE_ERROR("Error: Can not clear filter, No PF filter has been configured!");
        return st;
    }

    /* clear the general filter entry */
    if (NULL == p_target)
    {
        /* clear manufactuer data filter */
        btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL,
                                    BTM_BLE_PF_MANU_DATA);
        /* clear local name filter */
        btm_ble_update_pf_local_name(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL);
        /* update the counter for service data */
        btm_ble_update_srvc_data_change(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL);
        /* clear UUID filter */
        btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index,
                                   BTM_BLE_PF_SRVC_UUID, NULL);
        btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index,
                                   BTM_BLE_PF_SRVC_SOL_UUID, NULL);
        /* clear service data filter */
        btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL,
                                    BTM_BLE_PF_SRVC_DATA_PATTERN);
    }

    /* select feature based on control block settings */
    UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL);
    UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR);

    /* Filter index */
    UINT8_TO_STREAM(p, filt_index);

    /* set PCF selection */
    UINT32_TO_STREAM(p, BTM_BLE_PF_SELECT_NONE);
    /* set logic condition as OR as default */
    UINT8_TO_STREAM(p, BTM_BLE_PF_LOGIC_OR);

    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                               (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_PF_FEAT_SEL_LEN),
                                param,
                                btm_ble_scan_pf_cmpl_cback))
            != BTM_NO_RESOURCES)
    {
        if (p_bda_filter)
            p_bda_filter->feat_mask = BTM_BLE_PF_SELECT_NONE;

        if (p_target)
            memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_target, sizeof(tBLE_BD_ADDR));
        else
            memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
    }

    btm_ble_advfilt_enq_op_q(action, BTM_BLE_PF_TYPE_MAX, ref_value);
    return st;
}

/*******************************************************************************
**
** Function         BTM_BleAdvFilterParamSetup
**
** Description      This function is called to setup the adv data payload filter
**                  condition.
**
** Parameters       action - Type of action to be performed
**                       filt_index - Filter index
**                       p_filt_params - Filter parameters
**                       p_target - Target device
**                       p_cmpl_back - Callback pointer
**                       ref_value - reference value
**
** Returns          void
**
*******************************************************************************/
tBTM_STATUS BTM_BleAdvFilterParamSetup(int action, tBTM_BLE_PF_FILT_INDEX filt_index,
                                tBTM_BLE_PF_FILT_PARAMS *p_filt_params,
                                tBLE_BD_ADDR *p_target, tBTM_BLE_PF_PARAM_CBACK *p_cmpl_cback,
                                tBTM_BLE_REF_VALUE ref_value)
{
    UINT8           param[20], *p;
    tBTM_STATUS     st = BTM_WRONG_MODE;
    tBTM_BLE_PF_COUNT *p_bda_filter = NULL;
    UINT8 len =0;

    p = param;
    memset(param, 0, 20);
    BTM_TRACE_EVENT (" BTM_BleAdvFilterParamSetup");

    BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb);
    if (0 == cmn_ble_vsc_cb.max_filter)
    {
        st = BTM_MODE_UNSUPPORTED;
        return st;
    }

    btm_ble_adv_filt_cb.p_filt_param_cback = p_cmpl_cback;

    if (BTM_BLE_SCAN_COND_ADD == action)
    {
        p_bda_filter = btm_ble_find_addr_filter_counter(p_target);
        if (NULL == p_bda_filter)
        {
           BTM_TRACE_ERROR("BD Address not found!");
           return st;
        }

        BTM_TRACE_DEBUG("BTM_BleAdvFilterParamSetup : Feat mask:%d", p_bda_filter->feat_mask);
        /* select feature based on control block settings */
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL);
        UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_ADD);

        /* Filter index */
        UINT8_TO_STREAM(p, filt_index);

        /* set PCF selection */
        UINT16_TO_STREAM(p, p_filt_params->feat_seln);
        /* set logic type */
        UINT16_TO_STREAM(p, p_filt_params->logic_type);
        /* set logic condition */
        UINT8_TO_STREAM(p, p_filt_params->filt_logic_type);
        /* set RSSI high threshold */
        UINT8_TO_STREAM(p, p_filt_params->rssi_high_thres);
        /* set delivery mode */
        UINT8_TO_STREAM(p, p_filt_params->dely_mode);

        if(0x01 == p_filt_params->dely_mode)
        {
            /* set onfound timeout */
            UINT16_TO_STREAM(p, p_filt_params->found_timeout);
            /* set onfound timeout count*/
            UINT8_TO_STREAM(p, p_filt_params->found_timeout_cnt);
            /* set RSSI low threshold */
            UINT8_TO_STREAM(p, p_filt_params->rssi_low_thres);
            /* set onlost timeout */
            UINT16_TO_STREAM(p, p_filt_params->lost_timeout);
        }

        len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN;

        if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                                (UINT8)len,
                                 param,
                                 btm_ble_scan_pf_cmpl_cback))
               == BTM_NO_RESOURCES)
        {
            return st;
        }
        btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, ref_value);
    }
    else
    if(BTM_BLE_SCAN_COND_DELETE == action)
    {
        /* select feature based on control block settings */
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL);
        UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_DELETE);
        /* Filter index */
        UINT8_TO_STREAM(p, filt_index);

        if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                                (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH),
                                 param,
                                 btm_ble_scan_pf_cmpl_cback))
               == BTM_NO_RESOURCES)
        {
            return st;
        }
        btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, ref_value);
    }
    else
    if(BTM_BLE_SCAN_COND_CLEAR == action)
    {
        /* Deallocate all filters here */
        btm_ble_dealloc_addr_filter_counter(NULL, BTM_BLE_PF_TYPE_ALL);

        /* select feature based on control block settings */
        UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL);
        UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR);

        if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                                (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH-1),
                                 param,
                                 btm_ble_scan_pf_cmpl_cback))
               == BTM_NO_RESOURCES)
        {
            return st;
        }
        btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, ref_value);
    }

    return st;
}

/*******************************************************************************
**
** Function         BTM_BleEnableDisableFilterFeature
**
** Description      This function is called to enable / disable the APCF feature
**
** Parameters  enable the generic scan condition.
**                  enable: enable or disable the filter condition
**                  p_stat_cback - Status callback pointer
**                  ref_value   - Ref value
** Returns          void
**
*******************************************************************************/
tBTM_STATUS BTM_BleEnableDisableFilterFeature(UINT8 enable,
                                     tBTM_BLE_PF_STATUS_CBACK *p_stat_cback,
                                     tBTM_BLE_REF_VALUE ref_value)
{
    UINT8           param[20], *p;
    tBTM_STATUS     st = BTM_WRONG_MODE;

    BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb);
    if (0 == cmn_ble_vsc_cb.max_filter)
    {
        st = BTM_MODE_UNSUPPORTED;
        return st;
    }

    p = param;
    memset(param, 0, 20);

    /* enable the content filter in controller */
    p = param;
    UINT8_TO_STREAM(p, BTM_BLE_META_PF_ENABLE);
    /* enable adv data payload filtering */
    UINT8_TO_STREAM(p, enable);

    if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF,
                              BTM_BLE_PCF_ENABLE_LEN, param,
                              btm_ble_scan_pf_cmpl_cback)) == BTM_CMD_STARTED)
    {
         btm_ble_adv_filt_cb.p_filt_stat_cback = p_stat_cback;
         btm_ble_advfilt_enq_op_q(enable, BTM_BLE_META_PF_ENABLE, ref_value);
    }
    return st;
}

/*******************************************************************************
**
** Function         BTM_BleCfgFilterCondition
**
** Description      This function is called to configure the adv data payload filter
**                  condition.
**
** Parameters       action: to read/write/clear
**                  cond_type: filter condition type.
**                  filt_index - Filter index
**                  p_cond: filter condition parameter
**                  p_cmpl_cback  - Config callback pointer
**                  ref_value - Reference value
**
** Returns          void
**
*******************************************************************************/
tBTM_STATUS BTM_BleCfgFilterCondition(tBTM_BLE_SCAN_COND_OP action,
                                      tBTM_BLE_PF_COND_TYPE cond_type,
                                      tBTM_BLE_PF_FILT_INDEX filt_index,
                                      tBTM_BLE_PF_COND_PARAM *p_cond,
                                      tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback,
                                      tBTM_BLE_REF_VALUE ref_value)
{
    tBTM_STATUS     st = BTM_ILLEGAL_VALUE;
    BTM_TRACE_EVENT (" BTM_BleCfgFilterCondition action:%d, cond_type:%d, index:%d", action,
                        cond_type, filt_index);

    BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb);
    if (0 == cmn_ble_vsc_cb.max_filter)
    {
        st = BTM_MODE_UNSUPPORTED;
        return st;
    }

    switch (cond_type)
    {
        /* write service data filter */
        case BTM_BLE_PF_SRVC_DATA_PATTERN:
        /* write manufacturer data filter */
        case BTM_BLE_PF_MANU_DATA:
            st = btm_ble_update_pf_manu_data(action, filt_index, p_cond, cond_type);
            break;

        /* write local name filter */
        case BTM_BLE_PF_LOCAL_NAME:
            st = btm_ble_update_pf_local_name(action, filt_index, p_cond);
            break;

        /* filter on advertiser address */
        case BTM_BLE_PF_ADDR_FILTER:
            st = btm_ble_update_addr_filter(action, filt_index, p_cond);
            break;

        /* filter on service/solicitated UUID */
        case BTM_BLE_PF_SRVC_UUID:
        case BTM_BLE_PF_SRVC_SOL_UUID:
            st = btm_ble_update_uuid_filter(action, filt_index, cond_type, p_cond);
            break;

        case BTM_BLE_PF_SRVC_DATA:
            st = btm_ble_update_srvc_data_change(action, filt_index, p_cond);
            break;

        case BTM_BLE_PF_TYPE_ALL: /* only used to clear filter */
            st = btm_ble_clear_scan_pf_filter(action, filt_index, p_cond, ref_value);
            break;

        default:
            BTM_TRACE_WARNING("condition type [%d] not supported currently.", cond_type);
            break;
    }

    if (BTM_CMD_STARTED == st)
        btm_ble_adv_filt_cb.p_scan_cfg_cback = p_cmpl_cback;
    if(BTM_CMD_STARTED == st && cond_type != BTM_BLE_PF_TYPE_ALL)
        btm_ble_advfilt_enq_op_q(action, cond_type, ref_value);

    return st;
}

/*******************************************************************************
**
** Function         btm_ble_adv_filter_init
**
** Description      This function initializes the adv filter control block
**
** Parameters
**
** Returns          status
**
*******************************************************************************/
void btm_ble_adv_filter_init(void)
{
    memset(&btm_ble_adv_filt_cb, 0, sizeof(tBTM_BLE_MULTI_ADV_CB));
    if (!HCI_LE_HOST_SUPPORTED(btm_cb.devcb.local_lmp_features[HCI_EXT_FEATURES_PAGE_1]))
        return;
}

#endif
#endif
