/*
 * Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/*============================================================================
  FILE:         vos_utils.c

  OVERVIEW:     This source file contains definitions for vOS crypto APIs
                The four APIs mentioned in this file are used for
                initializing, and de-initializing a crypto context, and
                obtaining truly random data (for keys), as well as
                SHA1 HMAC, and AES encrypt and decrypt routines.

                The routines include:
                vos_crypto_init() - Initializes Crypto module
                vos_crypto_deinit() - De-initializes Crypto module
                vos_rand_get_bytes() - Generates random byte
                vos_sha1_hmac_str() - Generate the HMAC-SHA1 of a string given a key
                vos_encrypt_AES() - Generate AES Encrypted byte stream
                vos_decrypt_AES() - Decrypts an AES Encrypted byte stream

  DEPENDENCIES:

============================================================================*/

/*============================================================================
  EDIT HISTORY FOR MODULE

============================================================================*/

/*----------------------------------------------------------------------------
 * Include Files
 * -------------------------------------------------------------------------*/

#include "vos_trace.h"
#include "vos_utils.h"
#include "vos_memory.h"
#include <linux/wcnss_wlan.h>

#include <linux/err.h>
#include <linux/random.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/completion.h>
#include <linux/vmalloc.h>
#include <crypto/hash.h>
#include "vos_diag_core_event.h"


/*----------------------------------------------------------------------------
 * Preprocessor Definitions and Constants
 * -------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * Type Declarations
 * -------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Global Data Definitions
 * -------------------------------------------------------------------------*/

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,19,0)) || \
	defined(WLAN_BTAMP_FEATURE)
extern struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
                                                          unsigned int type,
                                                          unsigned int mask);
extern int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req);
extern void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm);
extern int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
                                          unsigned int keylen);
extern struct crypto_ablkcipher *wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name,
                                                                    u32 type, u32 mask);
extern void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm);
extern void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req);

/*----------------------------------------------------------------------------
 * Static Variable Definitions
 * -------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
   Function Definitions and Documentation
 * -------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------

  \brief vos_crypto_init() - Initializes Crypto module

  The vos_crypto_init() function initializes Crypto module.

  \param phCryptProv - pointer to the Crypt handle

  \return VOS_STATUS_SUCCESS - Successfully generated random memory.

          VOS_STATUS_E_FAULT  - pbBuf is an invalid pointer.

          VOS_STATUS_E_FAILURE - default return value if it fails due to
          unknown reasons

       ***VOS_STATUS_E_RESOURCES - System resources (other than memory)
          are unavailable
  \sa

    ( *** return value not considered yet )
  --------------------------------------------------------------------------*/
VOS_STATUS vos_crypto_init( v_U32_t *phCryptProv )
{
    VOS_STATUS uResult = VOS_STATUS_E_FAILURE;

    // This implementation doesn't require a crypto context
    *phCryptProv  = 0;
    uResult = VOS_STATUS_SUCCESS;
    return ( uResult );
}

VOS_STATUS vos_crypto_deinit( v_U32_t hCryptProv )
{
    VOS_STATUS uResult = VOS_STATUS_E_FAILURE;

    // CryptReleaseContext succeeded
    uResult = VOS_STATUS_SUCCESS;

    return ( uResult );
}

#endif

/*--------------------------------------------------------------------------

  \brief vos_rand_get_bytes() - Generates random byte

  The vos_rand_get_bytes() function generate random bytes.

  Buffer should be allocated before calling vos_rand_get_bytes().

  Attempting to initialize an already initialized lock results in
  a failure.

  \param lock - pointer to the opaque lock object to initialize

  \return VOS_STATUS_SUCCESS - Successfully generated random memory.

          VOS_STATUS_E_FAULT  - pbBuf is an invalid pointer.

          VOS_STATUS_E_FAILURE - default return value if it fails due to
          unknown reasons

       ***VOS_STATUS_E_RESOURCES - System resources (other than memory)
          are unavailable
  \sa

    ( *** return value not considered yet )
  --------------------------------------------------------------------------*/
VOS_STATUS vos_rand_get_bytes( v_U32_t cryptHandle, v_U8_t *pbBuf, v_U32_t numBytes )
{
   VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
   //v_UINT_t uCode;
//   HCRYPTPROV hCryptProv = (HCRYPTPROV) cryptHandle;

   //check for invalid pointer
   if ( NULL == pbBuf )
   {
      uResult = VOS_STATUS_E_FAULT;
      return ( uResult );
   }

//#if 0
   // get_random_bytes() is a void procedure
   get_random_bytes( pbBuf, numBytes);
   // "Random sequence generated."
   uResult = VOS_STATUS_SUCCESS;
//#endif

   return ( uResult );
}


#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,19,0)) || \
	defined(WLAN_BTAMP_FEATURE)
/**
 * vos_sha1_hmac_str
 *
 * FUNCTION:
 * Generate the HMAC-SHA1 of a string given a key.
 *
 * LOGIC:
 * Standard HMAC processing from RFC 2104. The code is provided in the
 * appendix of the RFC.
 *
 * ASSUMPTIONS:
 * The RFC is correct.
 *
 * @param text text to be hashed
 * @param textLen length of text
 * @param key key to use for HMAC
 * @param keyLen length of key
 * @param digest holds resultant SHA1 HMAC (20B)
 *
 * @return VOS_STATUS_SUCCSS if the operation succeeds
 *
 */

struct hmac_sha1_result {
    struct completion completion;
    int err;
};

static void hmac_sha1_complete(struct crypto_async_request *req, int err)
{
    struct hmac_sha1_result *r = req->data;
    if (err == -EINPROGRESS)
        return;
    r->err = err;
    complete(&r->completion);
}

int hmac_sha1(v_U8_t *key, v_U8_t ksize, char *plaintext, v_U8_t psize,
              v_U8_t *output, v_U8_t outlen)
{
    int ret = 0;
    struct crypto_ahash *tfm;
    struct scatterlist sg;
    struct ahash_request *req;
    struct hmac_sha1_result tresult;
    void *hash_buff = NULL;

    unsigned char hash_result[64];
    int i;

    memset(output, 0, outlen);

    init_completion(&tresult.completion);

    tfm = wcnss_wlan_crypto_alloc_ahash("hmac(sha1)", CRYPTO_ALG_TYPE_AHASH,
                                        CRYPTO_ALG_TYPE_AHASH_MASK);
    if (IS_ERR(tfm)) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ahash failed");
        ret = PTR_ERR(tfm);
        goto err_tfm;
    }

    req = ahash_request_alloc(tfm, GFP_KERNEL);
    if (!req) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to allocate request for hmac(sha1)");
        ret = -ENOMEM;
        goto err_req;
    }

    ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                               hmac_sha1_complete, &tresult);

    hash_buff = kzalloc(psize, GFP_KERNEL);
    if (!hash_buff) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to kzalloc hash_buff");
        ret = -ENOMEM;
        goto err_hash_buf;
    }

    memset(hash_result, 0, 64);
    vos_mem_copy(hash_buff, plaintext, psize);
    sg_init_one(&sg, hash_buff, psize);

    if (ksize) {
        crypto_ahash_clear_flags(tfm, ~0);
        ret = wcnss_wlan_crypto_ahash_setkey(tfm, key, ksize);

        if (ret) {
            VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_ahash_setkey failed");
            goto err_setkey;
        }
    }

    ahash_request_set_crypt(req, &sg, hash_result, psize);
    ret = wcnss_wlan_crypto_ahash_digest(req);

    VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "ret 0x%x", ret);

    switch (ret) {
    case 0:
        for (i=0; i< outlen; i++)
            output[i] = hash_result[i];
        break;
    case -EINPROGRESS:
    case -EBUSY:
        ret = wait_for_completion_interruptible(&tresult.completion);
        if (!ret && !tresult.err) {
            for (i=0; i< outlen; i++)
                output[i] = hash_result[i];
            INIT_COMPLETION(tresult.completion);
            break;
        } else {
            VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "wait_for_completion_interruptible failed");
            if (!ret)
                ret = tresult.err;
            goto out;
        }
    default:
        goto out;
    }

out:
err_setkey:
    kfree(hash_buff);
err_hash_buf:
    ahash_request_free(req);
err_req:
    wcnss_wlan_crypto_free_ahash(tfm);
err_tfm:
    return ret;
}

VOS_STATUS vos_sha1_hmac_str(v_U32_t cryptHandle, /* Handle */
           v_U8_t *pText, /* pointer to data stream */
           v_U32_t textLen, /* length of data stream */
           v_U8_t *pKey, /* pointer to authentication key */
           v_U32_t keyLen, /* length of authentication key */
           v_U8_t digest[VOS_DIGEST_SHA1_SIZE])/* caller digest to be filled in */
{
    int ret = 0;

    ret = hmac_sha1(
            pKey,                   //v_U8_t *key,
            (v_U8_t) keyLen,        //v_U8_t ksize,
            (char *)pText,          //char *plaintext,
            (v_U8_t) textLen,       //v_U8_t psize,
            digest,                 //v_U8_t *output,
            VOS_DIGEST_SHA1_SIZE    //v_U8_t outlen
            );

    if (ret != 0) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"hmac_sha1() call failed");
        return VOS_STATUS_E_FAULT;
    }

    return VOS_STATUS_SUCCESS;
}

/**
 * vos_md5_hmac_str
 *
 * FUNCTION:
 * Generate the HMAC-MD5 of a string given a key.
 *
 * LOGIC:
 * Standard HMAC processing from RFC 2104. The code is provided in the
 * appendix of the RFC.
 *
 * ASSUMPTIONS:
 * The RFC is correct.
 *
 * @param text text to be hashed
 * @param textLen length of text
 * @param key key to use for HMAC
 * @param keyLen length of key
 * @param digest holds resultant MD5 HMAC (20B)
 *
 * @return VOS_STATUS_SUCCSS if the operation succeeds
 *
 */
struct hmac_md5_result {
    struct completion completion;
    int err;
};

static void hmac_md5_complete(struct crypto_async_request *req, int err)
{
    struct hmac_md5_result *r = req->data;
    if (err == -EINPROGRESS)
            return;
    r->err = err;
    complete(&r->completion);
}

int hmac_md5(v_U8_t *key, v_U8_t ksize, char *plaintext, v_U8_t psize,
                v_U8_t *output, v_U8_t outlen)
{
    int ret = 0;
    struct crypto_ahash *tfm;
    struct scatterlist sg;
    struct ahash_request *req;
    struct hmac_md5_result tresult = {.err = 0};
    void *hash_buff = NULL;

    unsigned char hash_result[64];
    int i;

    memset(output, 0, outlen);

    init_completion(&tresult.completion);

    tfm = wcnss_wlan_crypto_alloc_ahash("hmac(md5)", CRYPTO_ALG_TYPE_AHASH,
                                        CRYPTO_ALG_TYPE_AHASH_MASK);
    if (IS_ERR(tfm)) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ahash failed");
                ret = PTR_ERR(tfm);
                goto err_tfm;
    }

    req = ahash_request_alloc(tfm, GFP_KERNEL);
    if (!req) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to allocate request for hmac(md5)");
        ret = -ENOMEM;
        goto err_req;
    }

    ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                        hmac_md5_complete, &tresult);

    hash_buff = kzalloc(psize, GFP_KERNEL);
    if (!hash_buff) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to kzalloc hash_buff");
        ret = -ENOMEM;
        goto err_hash_buf;
    }

    memset(hash_result, 0, 64);
    vos_mem_copy(hash_buff, plaintext, psize);
    sg_init_one(&sg, hash_buff, psize);

    if (ksize) {
        crypto_ahash_clear_flags(tfm, ~0);
        ret = wcnss_wlan_crypto_ahash_setkey(tfm, key, ksize);

        if (ret) {
            VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_ahash_setkey failed");
            goto err_setkey;
        }
    }

    ahash_request_set_crypt(req, &sg, hash_result, psize);
    ret = wcnss_wlan_crypto_ahash_digest(req);

    VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "ret 0x%x", ret);

    switch (ret) {
        case 0:
            for (i=0; i< outlen; i++)
                    output[i] = hash_result[i];
            break;
        case -EINPROGRESS:
        case -EBUSY:
             ret = wait_for_completion_interruptible(&tresult.completion);
             if (!ret && !tresult.err) {
                 for (i=0; i< outlen; i++)
                     output[i] = hash_result[i];
                 INIT_COMPLETION(tresult.completion);
                 break;
             } else {
                 VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "wait_for_completion_interruptible failed");
                 if (!ret)
                     ret = tresult.err;
                 goto out;
             }
        default:
              goto out;
        }

out:
err_setkey:
        kfree(hash_buff);
err_hash_buf:
        ahash_request_free(req);
err_req:
        wcnss_wlan_crypto_free_ahash(tfm);
err_tfm:
        return ret;
}

VOS_STATUS vos_md5_hmac_str(v_U32_t cryptHandle, /* Handle */
           v_U8_t *pText, /* pointer to data stream */
           v_U32_t textLen, /* length of data stream */
           v_U8_t *pKey, /* pointer to authentication key */
           v_U32_t keyLen, /* length of authentication key */
           v_U8_t digest[VOS_DIGEST_MD5_SIZE])/* caller digest to be filled in */
{
    int ret = 0;

    ret = hmac_md5(
            pKey,                   //v_U8_t *key,
            (v_U8_t) keyLen,        //v_U8_t ksize,
            (char *)pText,          //char *plaintext,
            (v_U8_t) textLen,       //v_U8_t psize,
            digest,                 //v_U8_t *output,
            VOS_DIGEST_MD5_SIZE     //v_U8_t outlen
            );

    if (ret != 0) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"hmac_md5() call failed");
        return VOS_STATUS_E_FAULT;
    }

    return VOS_STATUS_SUCCESS;
}


struct ecb_aes_result {
    struct completion completion;
    int err;
};

static void ecb_aes_complete(struct crypto_async_request *req, int err)
{
    struct ecb_aes_result *r = req->data;
    if (err == -EINPROGRESS)
        return;
    r->err = err;
    complete(&r->completion);
}


/*--------------------------------------------------------------------------

  \brief vos_encrypt_AES() - Generate AES Encrypted byte stream

  The vos_encrypt_AES() function generates the encrypted byte stream for given text.

  Buffer should be allocated before calling vos_rand_get_bytes().

  Attempting to initialize an already initialized lock results in
  a failure.

  \param lock - pointer to the opaque lock object to initialize

  \return VOS_STATUS_SUCCESS - Successfully generated random memory.

          VOS_STATUS_E_FAULT  - pbBuf is an invalid pointer.

          VOS_STATUS_E_FAILURE - default return value if it fails due to
          unknown reasons

       ***VOS_STATUS_E_RESOURCES - System resources (other than memory)
          are unavailable
  \sa

    ( *** return value not considered yet )
  --------------------------------------------------------------------------*/

#define IV_SIZE_AES_128 16
#define KEY_SIZE_AES_128 16
#define AES_BLOCK_SIZE 16

VOS_STATUS vos_encrypt_AES(v_U32_t cryptHandle, /* Handle */
                           v_U8_t *pPlainText, /* pointer to data stream */
                           v_U8_t *pCiphertext,
                           v_U8_t *pKey) /* pointer to authentication key */
{
//    VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
    struct ecb_aes_result result;
    struct ablkcipher_request *req;
    struct crypto_ablkcipher *tfm;
    int ret = 0;
    char iv[IV_SIZE_AES_128];
    struct scatterlist sg_in;
    struct scatterlist sg_out;

    init_completion(&result.completion);

    tfm =  wcnss_wlan_crypto_alloc_ablkcipher( "cbc(aes)", 0, 0);
    if (IS_ERR(tfm)) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ablkcipher failed");
        ret = PTR_ERR(tfm);
        goto err_tfm;
    }

    req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
    if (!req) {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, "Failed to allocate request for cbc(aes)");
        ret = -ENOMEM;
        goto err_req;
    }

    ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                    ecb_aes_complete, &result);


    crypto_ablkcipher_clear_flags(tfm, ~0);

    ret = crypto_ablkcipher_setkey(tfm, pKey, KEY_SIZE_AES_128);
    if (ret) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_cipher_setkey failed");
        goto err_setkey;
    }

    memset(iv, 0, IV_SIZE_AES_128);

    sg_init_one(&sg_in, pPlainText, AES_BLOCK_SIZE);

    sg_init_one(&sg_out, pCiphertext, AES_BLOCK_SIZE);

    ablkcipher_request_set_crypt(req, &sg_in, &sg_out, AES_BLOCK_SIZE, iv);

    crypto_ablkcipher_encrypt(req);



// -------------------------------------
err_setkey:
    wcnss_wlan_ablkcipher_request_free(req);
err_req:
    wcnss_wlan_crypto_free_ablkcipher(tfm);
err_tfm:
    //return ret;
    if (ret != 0) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"%s() call failed", __func__);
        return VOS_STATUS_E_FAULT;
   }

    return VOS_STATUS_SUCCESS;
}

/*--------------------------------------------------------------------------

  \brief vos_decrypt_AES() - Decrypts an AES Encrypted byte stream

  The vos_decrypt_AES() function decrypts the encrypted byte stream.

  Buffer should be allocated before calling vos_rand_get_bytes().

  Attempting to initialize an already initialized lock results in
  a failure.

  \param lock - pointer to the opaque lock object to initialize

  \return VOS_STATUS_SUCCESS - Successfully generated random memory.

          VOS_STATUS_E_FAULT  - pbBuf is an invalid pointer.

          VOS_STATUS_E_FAILURE - default return value if it fails due to
          unknown reasons

       ***VOS_STATUS_E_RESOURCES - System resources (other than memory)
          are unavailable
  \sa

    ( *** return value not considered yet )
  --------------------------------------------------------------------------*/

VOS_STATUS vos_decrypt_AES(v_U32_t cryptHandle, /* Handle */
                           v_U8_t *pText, /* pointer to data stream */
                           v_U8_t *pDecrypted,
                           v_U8_t *pKey) /* pointer to authentication key */
{
//    VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
    struct ecb_aes_result result;
    struct ablkcipher_request *req;
    struct crypto_ablkcipher *tfm;
    int ret = 0;
    char iv[IV_SIZE_AES_128];
    struct scatterlist sg_in;
    struct scatterlist sg_out;

    init_completion(&result.completion);

    tfm =  wcnss_wlan_crypto_alloc_ablkcipher( "cbc(aes)", 0, 0);
    if (IS_ERR(tfm)) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ablkcipher failed");
        ret = PTR_ERR(tfm);
        goto err_tfm;
    }

    req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
    if (!req) {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, "Failed to allocate request for cbc(aes)");
        ret = -ENOMEM;
        goto err_req;
    }

    ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                    ecb_aes_complete, &result);


    crypto_ablkcipher_clear_flags(tfm, ~0);

    ret = crypto_ablkcipher_setkey(tfm, pKey, KEY_SIZE_AES_128);
    if (ret) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_cipher_setkey failed");
        goto err_setkey;
       }

    memset(iv, 0, IV_SIZE_AES_128);

    sg_init_one(&sg_in, pText, AES_BLOCK_SIZE);

    sg_init_one(&sg_out, pDecrypted, AES_BLOCK_SIZE);

    ablkcipher_request_set_crypt(req, &sg_in, &sg_out, AES_BLOCK_SIZE, iv);

    crypto_ablkcipher_decrypt(req);



// -------------------------------------
err_setkey:
    wcnss_wlan_ablkcipher_request_free(req);
err_req:
    wcnss_wlan_crypto_free_ablkcipher(tfm);
err_tfm:
    //return ret;
    if (ret != 0) {
        VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"%s() call failed", __func__);
        return VOS_STATUS_E_FAULT;
      }

    return VOS_STATUS_SUCCESS;
}

#endif

v_U8_t vos_chan_to_band(v_U32_t chan)
{
    if (chan <= VOS_24_GHZ_CHANNEL_14)
        return VOS_BAND_2GHZ;

    return VOS_BAND_5GHZ;
}

void vos_get_wlan_unsafe_channel(v_U16_t *unsafeChannelList,
                           v_U16_t buffer_size, v_U16_t *unsafeChannelCount)
{
    /* Get unsafe channel list from cached location */
    wcnss_get_wlan_unsafe_channel(unsafeChannelList, buffer_size,
                                  unsafeChannelCount);
}


#include <linux/skbuff.h>
#include "vos_timer.h"
#include "i_vos_packet.h"

#define MAC_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
#define DXE_DATA_MAGIC_NO 0x010
#define DXE_MGMT_MAGIC_NO 0x011

//Define gRoamDelayMetaInfo
tRoamDelayMetaInfo gRoamDelayMetaInfo = {0};
tRoamDelayMetaInfo *gpRoamDelayTable = NULL;
v_BOOL_t gRoamDelayCurrentIndex = 0;

#define VOS_ETHERTYPE_802_1_X                           ( 0x888E )
#define VOS_ETHERTYPE_802_1_X_SIZE                      ( 2 )
//802.3 frame header have SRC MAC (6), DST(6),next will PROTOCOL type
#define VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_3_PKT ( 12 )

//802.11 header wil have 26 byte (Inculding QoS Info)
//8Byte LLC / SNAP header in which last two byte will be PROTOCOL type
//So offset will 32 if it is QoS data pkt else it will be 30
#define VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_11_PKT ( 32 )
#define VOS_QOS_DATA_VALUE                              ( 0x88 )
#define VOS_NON_QOS_DATA_VALUE                          ( 0x80 )

//802.11 header wil have 24 byte excluding qos
#define VOS_802_11_HEADER_SIZE ( 24 )
#define VOS_QOS_SIZE ( 2 )
#define VOS_LLC_HEADER_SIZE   (8)
#define VOS_IP_HEADER_SIZE    (20)
#define VOS_TCP_MIN_HEADER_SIZE   (20)
#define VOS_DEF_PKT_STATS_LEN_TO_COPY \
       (VOS_802_11_HEADER_SIZE + VOS_LLC_HEADER_SIZE \
       + VOS_IP_HEADER_SIZE + VOS_TCP_MIN_HEADER_SIZE)
// DHCP Port number
#define VOS_DHCP_SOURCE_PORT 0x4400
#define VOS_DHCP_DESTINATION_PORT 0x4300



// Frame Type definitions
#define VOS_MAC_MGMT_FRAME    0x0
#define VOS_MAC_CTRL_FRAME    0x1
#define VOS_MAC_DATA_FRAME    0x2

#define MONITOR_STOP            0x0
#define MONITOR_START           0x1
#define MONITOR_EAPOL_DONE      0x2
#define MONITOR_FIRST_DATA_DONE 0x4

v_BOOL_t vos_skb_is_eapol(struct sk_buff *skb,
                          v_SIZE_t pktOffset,
                          v_SIZE_t numBytes)
{
    void       *pBuffer   = NULL;
    v_BOOL_t   fEAPOL     = VOS_FALSE;

    // Validate the skb
    if (unlikely(NULL == skb))
    {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                    "vos_skb_is_eapol [%d]: NULL skb", __LINE__);
        return VOS_FALSE;
    }
    // check for overflow
    if (unlikely((pktOffset + numBytes) > skb->len))
    {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                  "vos_skb_is_eapol [%d]: Packet overflow, offset %d size %d len %d",
                  __LINE__, pktOffset, numBytes, skb->len);
        return VOS_FALSE;
    }
    //check for the Qos Data, if Offset length is more 12.
    //it means it will 802.11 header skb
    if((pktOffset > VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_3_PKT)
       && (skb->data[0] == VOS_NON_QOS_DATA_VALUE))
    {
        // reduced 2 byte of Qos ctrl field in DOT11 header
        pktOffset = pktOffset - 2;
    }
    pBuffer = &skb->data[pktOffset];
    if (pBuffer && vos_be16_to_cpu( *(unsigned short*)pBuffer ) == VOS_ETHERTYPE_802_1_X )
    {
      fEAPOL = VOS_TRUE;
    }
    return fEAPOL;
}

v_BOOL_t vos_roam_delay_stats_init(void)
{
    if (gpRoamDelayTable == NULL)
    {
        gpRoamDelayTable = vmalloc(sizeof(tRoamDelayMetaInfo) * ROAM_DELAY_TABLE_SIZE);
        if (gpRoamDelayTable == NULL)
        {
            VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, "Memory allocation failed");
            return VOS_FALSE;
        }
    }
    else
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO, "Memory is already allocated");
        return VOS_FALSE;
    }

    return VOS_TRUE;
}


v_BOOL_t vos_roam_delay_stats_deinit(void)
{
    if (gpRoamDelayTable != NULL)
    {
        vfree(gpRoamDelayTable);
        gpRoamDelayTable = NULL;
    }
    else
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO, "Memory is already freed");
        return VOS_FALSE;
    }

    return VOS_TRUE;
}

void vos_record_roam_event(enum e_roaming_event roam_event, void *pBuff, v_ULONG_t buff_len)
{
    if (gpRoamDelayTable == NULL)
    {
        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                  "Roam delay table is not initialized\n");
        return;
    }
    switch(roam_event)
    {
        case e_HDD_DISABLE_TX_QUEUE:
             gRoamDelayMetaInfo.hdd_monitor_tx = MONITOR_STOP;
             gRoamDelayMetaInfo.disable_tx_queues_time = vos_timer_get_system_time();
             break;
        case e_SME_PREAUTH_REASSOC_START:
             gRoamDelayMetaInfo.preauth_reassoc_start_time = vos_timer_get_system_time();
             break;
        case e_SME_PREAUTH_CALLBACK_HIT:
             gRoamDelayMetaInfo.preauth_cb_time = vos_timer_get_system_time();
             break;
        case e_SME_ISSUE_REASSOC_REQ:
             gRoamDelayMetaInfo.issue_reassoc_req_time = vos_timer_get_system_time();
             //HACK buff len will carry the AuthType
             gRoamDelayMetaInfo.hdd_auth_type = buff_len;
             break;
        case e_LIM_SEND_REASSOC_REQ:
             gRoamDelayMetaInfo.send_reassoc_req_time = vos_timer_get_system_time();
             //we can enable the rx eapol monitoring ASAP we send the REASSOC REQ Because
             //there is very less delay in between REASSOC RSP and M1 Sent by the AP
             gRoamDelayMetaInfo.hdd_monitor_rx = MONITOR_START;
             gRoamDelayMetaInfo.dxe_monitor_rx = MONITOR_START;
             break;
        case e_CACHE_ROAM_PEER_MAC:
             vos_mem_copy(&gRoamDelayMetaInfo.peer_mac_addr, pBuff, buff_len);
             break;
        case e_HDD_SEND_REASSOC_RSP:
             gRoamDelayMetaInfo.hdd_sendassoc_rsp_time = vos_timer_get_system_time();
             break;
        case e_SME_DISASSOC_ISSUE:
             gRoamDelayMetaInfo.disassoc_issue_time = vos_timer_get_system_time();
             break;
        case e_SME_DISASSOC_COMPLETE:
             gRoamDelayMetaInfo.disassoc_comp_time = vos_timer_get_system_time();
             break;
        case e_LIM_ADD_BS_REQ:
             gRoamDelayMetaInfo.lim_add_bss_req_time = vos_timer_get_system_time();
             break;
        case e_LIM_ADD_BS_RSP:
             gRoamDelayMetaInfo.lim_add_bss_rsp_time = vos_timer_get_system_time();
             break;
        case e_HDD_ENABLE_TX_QUEUE:
             gRoamDelayMetaInfo.hdd_monitor_tx = MONITOR_START;
             gRoamDelayMetaInfo.enable_tx_queues_reassoc_time = vos_timer_get_system_time();
             break;
        case e_HDD_SET_PTK_REQ:
             gRoamDelayMetaInfo.set_ptk_roam_key_time = vos_timer_get_system_time();
             break;
        case e_HDD_SET_GTK_REQ:
             gRoamDelayMetaInfo.set_gtk_roam_key_time = vos_timer_get_system_time();
             break;
        case e_HDD_SET_PTK_RSP:
             gRoamDelayMetaInfo.complete_ptk_roam_key_time = vos_timer_get_system_time();
             //vos_mem_copy(&gRoamDelayMetaInfo.peer_mac_addr, pBuff, buff_len);
             break;
        case e_HDD_SET_GTK_RSP:
             gRoamDelayMetaInfo.complete_gtk_roam_key_time = vos_timer_get_system_time();
             break;
        case e_TL_FIRST_XMIT_TIME:
             if(gRoamDelayMetaInfo.log_tl)
             {
                 gRoamDelayMetaInfo.tl_fetch_pkt_time = vos_timer_get_system_time();
                 gRoamDelayMetaInfo.log_tl = VOS_FALSE;
             }
             break;
        case e_HDD_FIRST_XMIT_TIME:
             if(gRoamDelayMetaInfo.hdd_monitor_tx != MONITOR_STOP)
             {
                 struct sk_buff *skb = (struct sk_buff *)pBuff;
                 if(!skb)
                 {
                     VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                                "event e_HDD_FIRST_XMIT_TIME skb is null");
                     return;
                 }
                 if((gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_RSN_PSK) ||
                    (gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_WPA_PSK))
                 {
                     //Hdd xmit will have only 802.3 pkt so offset will pass as accordingly
                     if(vos_skb_is_eapol(skb, VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_3_PKT,
                            VOS_ETHERTYPE_802_1_X_SIZE) == VOS_TRUE)
                     {
                          if(gRoamDelayMetaInfo.hdd_eapol_m2 == 0)
                          {
                              VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"HDD XMIT m2");
                              gRoamDelayMetaInfo.hdd_eapol_m2 = vos_timer_get_system_time();
                              gRoamDelayMetaInfo.dxe_monitor_tx = MONITOR_START;
                          }
                          else if((gRoamDelayMetaInfo.hdd_eapol_m2) && (gRoamDelayMetaInfo.hdd_eapol_m4 == 0))
                         {
                              gRoamDelayMetaInfo.hdd_eapol_m4 = vos_timer_get_system_time();
                              VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"HDD XMIT m4");
                              gRoamDelayMetaInfo.hdd_monitor_tx = MONITOR_EAPOL_DONE;
                              //We should return from here so can cache the time for first data pkt
                              return;
                         }
                     }
                 }
                 else
                 {
                    gRoamDelayMetaInfo.hdd_monitor_tx = MONITOR_EAPOL_DONE;
                    gRoamDelayMetaInfo.dxe_monitor_tx = MONITOR_START;
                 }
                 //Eapol is done it must be first data frame capture it
                 if(gRoamDelayMetaInfo.hdd_monitor_tx == MONITOR_EAPOL_DONE)
                 {
                     gRoamDelayMetaInfo.hdd_first_pkt_len = 50;
                     gRoamDelayMetaInfo.hdd_first_xmit_time = vos_timer_get_system_time();
                     gRoamDelayMetaInfo.log_tl = VOS_TRUE;
                     gRoamDelayMetaInfo.hdd_monitor_tx = MONITOR_STOP;
                     VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                                "HDD %s XMIT first data frame after roaming", __func__);
                     if(skb->len < gRoamDelayMetaInfo.hdd_first_pkt_len)
                         gRoamDelayMetaInfo.hdd_first_pkt_len = skb->len;
                     vos_mem_copy(&gRoamDelayMetaInfo.hdd_first_pkt_data,
                                  skb->data,gRoamDelayMetaInfo.hdd_first_pkt_len);
                 }
             }
             break;
        case e_HDD_RX_PKT_CBK_TIME:
             if(gRoamDelayMetaInfo.hdd_monitor_rx != MONITOR_STOP)
             {
                 struct sk_buff *skb = (struct sk_buff *)pBuff;
                 if(!skb)
                 {
                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                               "event e_HDD_RX_PKT_CBK_TIME skb is null");
                    return;
                 }
                 if((gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_RSN_PSK) ||
                    (gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_WPA_PSK))
                 {
                     if(vos_skb_is_eapol(skb, VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_3_PKT,
                            VOS_ETHERTYPE_802_1_X_SIZE) == VOS_TRUE)
                     {
                         if(gRoamDelayMetaInfo.hdd_eapol_m1 == 0)
                         {
                             VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"HDD recv m1");
                             gRoamDelayMetaInfo.hdd_eapol_m1 = vos_timer_get_system_time();
                         }
                         else if((gRoamDelayMetaInfo.hdd_eapol_m1) && (gRoamDelayMetaInfo.hdd_eapol_m3 == 0))
                         {
                             gRoamDelayMetaInfo.hdd_eapol_m3 = vos_timer_get_system_time();
                             VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"HDD recv m3");
                             gRoamDelayMetaInfo.hdd_monitor_rx = MONITOR_EAPOL_DONE;
                         }
                     }
                 }
                 else
                 {
                     gRoamDelayMetaInfo.hdd_monitor_rx = MONITOR_EAPOL_DONE;
                 }
                 if(gRoamDelayMetaInfo.hdd_monitor_rx == MONITOR_EAPOL_DONE)
                 {
                     gRoamDelayMetaInfo.hdd_monitor_rx = MONITOR_STOP;
                 }
             }
             break;
        case e_DXE_RX_PKT_TIME:
             if(gRoamDelayMetaInfo.dxe_monitor_rx != MONITOR_STOP)
             {
                 vos_pkt_t *vos_pkt = NULL;
                 struct sk_buff *skb = NULL;
                 vos_pkt = (vos_pkt_t *)pBuff;
                 if(!vos_pkt)
                 {
                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                               "event e_DXE_RX_PKT_TIME vos_pkt is null");
                    return;
                 }
                 skb = vos_pkt->pSkb;
                 if(!skb)
                 {
                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                               "event e_DXE_RX_PKT_TIME skb is null");
                    return;
                 }
                 //DXE can RECV MGMT and DATA frame, we are interetsed in only DATA frame
                 if(buff_len & VOS_MAC_DATA_FRAME)
                 {
                     if((gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_RSN_PSK) ||
                        (gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_WPA_PSK))
                     {
                         if(vos_skb_is_eapol(skb, VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_11_PKT,
                                VOS_ETHERTYPE_802_1_X_SIZE) == VOS_TRUE)
                         {
                             if(gRoamDelayMetaInfo.dxe_eapol_m1 == 0)
                             {
                                 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"DXE recv m1");
                                 gRoamDelayMetaInfo.dxe_eapol_m1 = vos_timer_get_system_time();
                             }
                             else if((gRoamDelayMetaInfo.dxe_eapol_m1) && (gRoamDelayMetaInfo.dxe_eapol_m3 == 0))
                             {
                                 gRoamDelayMetaInfo.dxe_eapol_m3 = vos_timer_get_system_time();
                                 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"DXE recv m3");
                                 gRoamDelayMetaInfo.dxe_monitor_rx = MONITOR_EAPOL_DONE;
                             }
                         }
                     }
                     else
                     {
                         gRoamDelayMetaInfo.dxe_monitor_rx = MONITOR_EAPOL_DONE;
                     }
                     if(gRoamDelayMetaInfo.dxe_monitor_rx == MONITOR_EAPOL_DONE)
                     {
                         gRoamDelayMetaInfo.dxe_monitor_rx = MONITOR_STOP;
                     }
                 }
                 /*
                 else
                 {
                     printk("e_DXE_RX_PKT_TIME dump mgmt frames");
                     vos_trace_hex_dump( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, &skb->data[0], skb->len);
                 }
                 */
             }
             break;
        case e_DXE_FIRST_XMIT_TIME:
             if(gRoamDelayMetaInfo.dxe_monitor_tx != MONITOR_STOP)
             {
                 vos_pkt_t *vos_pkt = NULL;
                 struct sk_buff *skb = NULL;
                 vos_pkt = (vos_pkt_t *)pBuff;
                 if(!vos_pkt)
                 {
                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                               "event e_DXE_FIRST_XMIT_TIME vos_pkt is null");
                    return;
                 }
                 skb = vos_pkt->pSkb;
                 if(!skb)
                 {
                    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                               "event e_DXE_FIRST_XMIT_TIME skb is null");
                    return;
                 }
                 //DXE can Txmit MGMT and DATA frame, we are interetsed in only DATA frame
                 if(buff_len & VOS_MAC_DATA_FRAME)
                 {
                    if((gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_RSN_PSK) ||
                       (gRoamDelayMetaInfo.hdd_auth_type == eVOS_AUTH_TYPE_WPA_PSK))
                    {
                        if(vos_skb_is_eapol(skb, VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_11_PKT,
                               VOS_ETHERTYPE_802_1_X_SIZE) == VOS_TRUE)
                        {
                             if(gRoamDelayMetaInfo.dxe_eapol_m2 == 0)
                             {
                                 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"DXE XMIT m2");
                                 gRoamDelayMetaInfo.dxe_eapol_m2 = vos_timer_get_system_time();
                             }
                             else if((gRoamDelayMetaInfo.dxe_eapol_m2) && (gRoamDelayMetaInfo.dxe_eapol_m4 == 0))
                            {
                                 gRoamDelayMetaInfo.dxe_eapol_m4 = vos_timer_get_system_time();
                                 VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"DXE XMIT m4");
                                 gRoamDelayMetaInfo.dxe_monitor_tx = MONITOR_EAPOL_DONE;
                                 //We should return from here so can cache the time for first data pkt
                                 return;
                            }
                        }
                    }
                    else
                    {
                        gRoamDelayMetaInfo.dxe_monitor_tx = MONITOR_EAPOL_DONE;
                    }
                    //HACK buff len is getting used as FRAME TYPE
                    if(gRoamDelayMetaInfo.dxe_monitor_tx == MONITOR_EAPOL_DONE)
                    {
                        gRoamDelayMetaInfo.dxe_first_tx_time = vos_timer_get_system_time();
                        gRoamDelayMetaInfo.dxe_monitor_tx = MONITOR_STOP;
                        gRoamDelayMetaInfo.dxe_first_pkt_len = 75;
                        if(skb->len < gRoamDelayMetaInfo.dxe_first_pkt_len)
                            gRoamDelayMetaInfo.dxe_first_pkt_len = skb->len;
                        vos_mem_copy(&gRoamDelayMetaInfo.dxe_first_pkt_data, skb->data,
                                     gRoamDelayMetaInfo.dxe_first_pkt_len);
                        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                                   "DXE %s XMIT first data frame after roaming", __func__);
                    }
                 }
             }
             break;
        case e_SME_VO_ADDTS_REQ:
             gRoamDelayMetaInfo.hdd_addts_vo_req_time = vos_timer_get_system_time();
             break;
        case e_SME_VO_ADDTS_RSP:
             gRoamDelayMetaInfo.hdd_addts_vo_rsp_time = vos_timer_get_system_time();
             break;
        case e_SME_VI_ADDTS_REQ:
             gRoamDelayMetaInfo.hdd_addts_vi_req_time = vos_timer_get_system_time();
             break;
        case e_SME_VI_ADDTS_RSP:
             gRoamDelayMetaInfo.hdd_addts_vi_rsp_time = vos_timer_get_system_time();
             break;
        case e_CACHE_ROAM_DELAY_DATA:
             //Let us copy roam meta info
             if(gRoamDelayCurrentIndex > ROAM_DELAY_TABLE_SIZE)
                 gRoamDelayCurrentIndex = 0;
             vos_mem_copy(&gpRoamDelayTable[gRoamDelayCurrentIndex++],
                          &gRoamDelayMetaInfo, sizeof(gRoamDelayMetaInfo));
             vos_mem_set(&gRoamDelayMetaInfo, sizeof(gRoamDelayMetaInfo), 0);
             break;
        default:
             VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
                       "%s Invalid roam_event = %d received ", __func__, roam_event);
             break;
    }
}

void vos_reset_roam_timer_log(void)
{
    if (gpRoamDelayTable != NULL)
    {
       //Set zero to whole gpRoamDelayTable
       vos_mem_set(gpRoamDelayTable, (sizeof(tRoamDelayMetaInfo) * ROAM_DELAY_TABLE_SIZE), 0);
    }
}

void vos_dump_roam_time_log_service(void)
{
    v_SLONG_t slA, slB, slC, slD, slE, slF, slG, slH, slI, slJ, slK, slL, slM, slRoamDelay;
    tRoamDelayMetaInfo currentRoamDelayInfo;
    v_ULONG_t index = 0,i=0;

    if (gpRoamDelayTable == NULL)
    {
        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                  "Roam delay table is not initialized\n");
        return;
    }
    //Let us first copy the current gRoamDelayMetaInfo into gpRoamDelayTable
    if(gRoamDelayCurrentIndex > ROAM_DELAY_TABLE_SIZE)
        gRoamDelayCurrentIndex = 0;
    vos_mem_copy(&gpRoamDelayTable[gRoamDelayCurrentIndex++], &gRoamDelayMetaInfo, sizeof(gRoamDelayMetaInfo));

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "** RoamDelay = ( dxe_first_tx_time - disable_tx_queues_time)\n");

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||========================"
         "===============|====== A ======|====== B ======|====== C ======|"
         "====== D ======|====== E ======|====== F ======|====== G ======|"
         "====== H ======|====== I ======|====== J ======|====== K ======|"
         "====== L ======|====== M ======||\n");

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||Sl | Peer MAC address  |"
         " **RoamDelay** | PreAuth Timer | Disassoc Issue| Add BSS Req   |"
         " AddBssRsp to  | ReassocReq to | ReassocRsp to | Disable to    |"
         " M1-M2 DXE SW  | M1-M2 HDD SW  | M3-M4 DXE SW  | M3-M4 HDD SW  |"
         " ReassocRsp to | HDD to DXE    ||\n");

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||No.|                   |"
         " ************* | to Roam Start | to Complete   | to Rsp time   |"
         " Reassoc Req   | ReassocRsp    | Enable Tx Que | Enable Tx Que |"
         "               |               |               |               |"
         " Set GTK       | 1st data frame||\n");

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||========================"
         "================================================================"
         "================================================================"
         "================================================================"
         "===============================||\n");

    for (index = 0; index < gRoamDelayCurrentIndex; index++)
    {
        currentRoamDelayInfo = gpRoamDelayTable[index];
        /* PreAuth Timer to Roam Start */
        slA = (currentRoamDelayInfo.preauth_cb_time -
               currentRoamDelayInfo.preauth_reassoc_start_time);

        /* Disassoc Issue to Complete */
        slB = (currentRoamDelayInfo.disassoc_comp_time -
               currentRoamDelayInfo.disassoc_issue_time);

        /* Add BSS Req to Rsp time */
        slC = (currentRoamDelayInfo.lim_add_bss_rsp_time -
               currentRoamDelayInfo.lim_add_bss_req_time);

        /* AddBssRsp to Reassoc Req */
        slD = (currentRoamDelayInfo.send_reassoc_req_time -
               currentRoamDelayInfo.lim_add_bss_rsp_time);

        /* ReassocReq to ReassocRsp */
        slE = (currentRoamDelayInfo.hdd_sendassoc_rsp_time -
               currentRoamDelayInfo.send_reassoc_req_time);

        /* ReassocRsp to Enable Tx Que */
        slF = (currentRoamDelayInfo.enable_tx_queues_reassoc_time -
               currentRoamDelayInfo.hdd_sendassoc_rsp_time);

        /* Disable to Enable Tx Que */
        slG = (currentRoamDelayInfo.enable_tx_queues_reassoc_time -
               currentRoamDelayInfo.disable_tx_queues_time);

        /* M1-M2 DXE SW */
        slH = (currentRoamDelayInfo.dxe_eapol_m2 -
               currentRoamDelayInfo.dxe_eapol_m1);

        /* M1-M2 HDD SW */
        slI = (currentRoamDelayInfo.hdd_eapol_m2 -
               currentRoamDelayInfo.hdd_eapol_m1);

        /* M3-M4 DXE SW */
        slJ = (currentRoamDelayInfo.dxe_eapol_m4 -
               currentRoamDelayInfo.dxe_eapol_m3);

        /* M3-M4 HDD SW */
        slK = (currentRoamDelayInfo.hdd_eapol_m4 -
               currentRoamDelayInfo.hdd_eapol_m3);

        /* ReassocRsp to Set GTK */
        slL = (currentRoamDelayInfo.set_gtk_roam_key_time -
               currentRoamDelayInfo.hdd_sendassoc_rsp_time);

        /* HDD to DXE 1st data frame */
        slM = (currentRoamDelayInfo.dxe_first_tx_time -
               currentRoamDelayInfo.hdd_first_xmit_time);

        /* Calculate roam delay */
        slRoamDelay = (currentRoamDelayInfo.dxe_first_tx_time -
                       currentRoamDelayInfo.disable_tx_queues_time);

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||%2ld:|<"MAC_ADDRESS_STR">|"
                "%14ld |%14ld |%14ld |%14ld |"
                "%14ld |%14ld |%14ld |%14ld |"
                "%14ld |%14ld |%14ld |%14ld |"
                "%14ld |%14ld ||\n",
                (index+1), MAC_ADDR_ARRAY(currentRoamDelayInfo.peer_mac_addr),
                slRoamDelay, slA, slB, slC,
                slD, slE, slF, slG,
                slH, slI, slJ, slK,
                slL, slM );
    }

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||========================"
         "================================================================"
         "================================================================"
         "================================================================"
         "===============================||\n");

    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||== More Details ====================="
         "===============================||\n");

    for (index = 0; index < gRoamDelayCurrentIndex; index++)
    {

        currentRoamDelayInfo = gpRoamDelayTable[index];
        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||%2ld: Peer Mac: <"MAC_ADDRESS_STR">\n",
                (index+1), MAC_ADDR_ARRAY(currentRoamDelayInfo.peer_mac_addr)
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||preauth_reassoc_start_time : %14ld\n",
                currentRoamDelayInfo.preauth_reassoc_start_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||preauth_cb_time            : %14ld\n",
                currentRoamDelayInfo.preauth_cb_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||disable_tx_queues_time     : %14ld\n",
                currentRoamDelayInfo.disable_tx_queues_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||disassoc_issue_time        : %14ld\n",
                currentRoamDelayInfo.disassoc_issue_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||lim_add_bss_req_time       : %14ld\n",
                currentRoamDelayInfo.lim_add_bss_req_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||lim_add_bss_rsp_time       : %14ld\n",
                currentRoamDelayInfo.lim_add_bss_rsp_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||disassoc_comp_time         : %14ld\n",
                currentRoamDelayInfo.disassoc_comp_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||send_reassoc_req_time      : %14ld\n",
                currentRoamDelayInfo.send_reassoc_req_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_sendassoc_rsp_time     : %14ld\n",
                currentRoamDelayInfo.hdd_sendassoc_rsp_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||enable_tx_queues_time      : %14ld\n",
                currentRoamDelayInfo.enable_tx_queues_reassoc_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_eapol_m1               : %14ld\n",
                currentRoamDelayInfo.hdd_eapol_m1
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_eapol_m2               : %14ld\n",
                currentRoamDelayInfo.hdd_eapol_m2
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_eapol_m3               : %14ld\n",
                currentRoamDelayInfo.hdd_eapol_m3
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_eapol_m4               : %14ld\n",
                currentRoamDelayInfo.hdd_eapol_m4
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_eapol_m1               : %14ld\n",
                currentRoamDelayInfo.dxe_eapol_m1
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_eapol_m2               : %14ld\n",
                currentRoamDelayInfo.dxe_eapol_m2
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_eapol_m3               : %14ld\n",
                currentRoamDelayInfo.dxe_eapol_m3
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_eapol_m4               : %14ld\n",
                currentRoamDelayInfo.dxe_eapol_m4
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||set_ptk_roam_key_time      : %14ld\n",
                currentRoamDelayInfo.set_ptk_roam_key_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||set_gtk_roam_key_time      : %14ld\n",
                currentRoamDelayInfo.set_gtk_roam_key_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||tl_fetch_pkt_time          : %14ld\n",
                currentRoamDelayInfo.tl_fetch_pkt_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_first_xmit_time        : %14ld\n",
                currentRoamDelayInfo.hdd_first_xmit_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_first_tx_time          : %14ld\n",
                currentRoamDelayInfo.dxe_first_tx_time
                );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||hdd_first_pkt_data         : \n"
                );


        for (i=0; i<currentRoamDelayInfo.hdd_first_pkt_len && i< (50-8); i+=8)
            VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                    "||%2X, %2X, %2X, %2X, %2X, %2X, %2X, %2X, ",
                    currentRoamDelayInfo.hdd_first_pkt_data[i],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+1],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+2],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+3],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+4],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+5],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+6],
                    currentRoamDelayInfo.hdd_first_pkt_data[i+7]
                    );

        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                "||dxe_first_pkt_data         : \n"
                );

        for (i=0; i<currentRoamDelayInfo.dxe_first_pkt_len && i < (75-8); i+=8)
            VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
                    "||%2X, %2X, %2X, %2X, %2X, %2X, %2X, %2X, ",
                    currentRoamDelayInfo.dxe_first_pkt_data[i],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+1],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+2],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+3],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+4],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+5],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+6],
                    currentRoamDelayInfo.dxe_first_pkt_data[i+7]
                    );
    }
    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
         "||== END ====================="
         "===============================||\n");
}

v_U32_t vos_copy_80211_data(void *pBuff, v_U8_t *dst, v_U8_t frametype)
{
    vos_pkt_t *vos_pkt = NULL;
    struct sk_buff *skb = NULL;
    v_U32_t length_to_copy;

    vos_pkt = (vos_pkt_t *)pBuff;

    if(!vos_pkt || !dst)
    {
       VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                 "vos_pkt/dst is null");
       return 0;
    }
    skb = vos_pkt->pSkb;
    if(!skb)
    {
       VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                   " skb is null");
       return 0;
    }
    if (VOS_MAC_MGMT_FRAME == frametype)
    {
      length_to_copy = skb->len;
    }
    else
    {
        length_to_copy = VOS_DEF_PKT_STATS_LEN_TO_COPY;
           if(skb->data[0] == VOS_QOS_DATA_VALUE)
              length_to_copy += VOS_QOS_SIZE;

        /* Copy whole skb data if DHCP or EAPOL pkt.Here length_to_copy
         * will give the pointer to IP header and adding VOS_IP_HEADER_SIZE
         * to it will give the DHCP port number.
         */
        if (((skb->len > (length_to_copy + VOS_IP_HEADER_SIZE)) &&
             ((*((u16*)((u8*)skb->data + length_to_copy + VOS_IP_HEADER_SIZE))
              == VOS_DHCP_SOURCE_PORT) ||
             (*((u16*)((u8*)skb->data + length_to_copy + VOS_IP_HEADER_SIZE))
              == VOS_DHCP_DESTINATION_PORT)))  ||
             vos_skb_is_eapol(skb,
             VOS_ETHERTYPE_802_1_X_FRAME_OFFSET_IN_802_11_PKT,
             VOS_ETHERTYPE_802_1_X_SIZE))
        {
           length_to_copy = skb->len;
        }
    }

    if (length_to_copy > skb->len)
    {
        length_to_copy = skb->len;
    }
    if (length_to_copy > MAX_PKT_STAT_DATA_LEN)
    {
        length_to_copy = MAX_PKT_STAT_DATA_LEN;
    }

    vos_mem_copy(dst, skb->data, length_to_copy);
    return length_to_copy;
}

#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
 * vos_tdls_tx_rx_mgmt_event()- send tdls mgmt rx tx event
 *
 * @event_id: event id
 * @tx_rx: tx or rx
 * @type: type of frame
 * @action_sub_type: action frame type
 * @peer_mac: peer mac
 *
 * This Function sendsend tdls mgmt rx tx diag event
 *
 * Return: void.
 */
void vos_tdls_tx_rx_mgmt_event(uint8_t event_id, uint8_t tx_rx,
              uint8_t type, uint8_t action_sub_type, uint8_t *peer_mac)
{
    WLAN_VOS_DIAG_EVENT_DEF(tdls_tx_rx_mgmt,
                    struct vos_event_tx_rx_mgmt);
    vos_mem_zero(&tdls_tx_rx_mgmt, sizeof(tdls_tx_rx_mgmt));

    tdls_tx_rx_mgmt.event_id = event_id;
    tdls_tx_rx_mgmt.tx_rx = tx_rx;
    tdls_tx_rx_mgmt.type = type;
    tdls_tx_rx_mgmt.action_sub_type = action_sub_type;
    vos_mem_copy(tdls_tx_rx_mgmt.peer_mac,
                     peer_mac, VOS_MAC_ADDR_SIZE);
    WLAN_VOS_DIAG_EVENT_REPORT(&tdls_tx_rx_mgmt,
                             EVENT_WLAN_TX_RX_MGMT);
}
#endif
