/*
 * Copyright (C) 2012-2014 NXP Semiconductors
 *
 * 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 <phNxpEseHal.h>
#include <phNxpEseProtocol.h>
#include <phTmlEse_spi.h>

#ifdef USE_QSEE
#include <qsee_timer.h>
#endif

#ifdef USE_BLOWFISH
#include <string.h>
#include <tee_internal_api.h>
#include <sec_apdu.h>
#include <time.h>
#endif

//extern void ese_stack_data_callback(ESESTATUS status, phNxpEseP61_data *eventData);
/*********************** Global Variables *************************************/

/* SPI HAL Control structure */
phNxpEseP61_Control_t nxpesehal_ctrl;

void phNxpEseP61_init(void)
{
	//LOGD("start phNxpEseP61_init");
    memset(&nxpesehal_ctrl, 0x00, sizeof(nxpesehal_ctrl));
    /* make sequence counter 1*/
    nxpesehal_ctrl.seq_counter = 0x01;
    /* STATUS_OPEN */
    nxpesehal_ctrl.halStatus = ESE_STATUS_OPEN;
	//nxpesehal_ctrl.p_ese_stack_data_cback = ese_stack_data_callback;
	LOGD("phNxpEseP61_init");
}

/******************************************************************************
 * Function         phNxpEseP61_Transceive
 *
 * Description      This function update the len and provided buffer
 *
 * Returns          On Success ESESTATUS_SUCCESS else proper error code
 *
 ******************************************************************************/
ESESTATUS phNxpEseP61_Transceive(uint16_t data_len, const uint8_t *p_data)
{
    ESESTATUS status = ESESTATUS_FAILED;

    if ((data_len == 0) || p_data == NULL )
    {
        LOGE(" phNxpEseP61_Transceive - Invalid Parameter");
        return ESESTATUS_INVALID_PARAMETER;
    }
    else if ((ESE_STATUS_CLOSE == nxpesehal_ctrl.halStatus))
    {
        LOGE(" %s P61 Not Initialized", __FUNCTION__);
        return ESESTATUS_NOT_INITIALISED;
    }
    else if ((ESE_STATUS_BUSY == nxpesehal_ctrl.halStatus))
    {
        LOGE(" %s P61 - BUSY", __FUNCTION__);
        return ESESTATUS_BUSY;
    }
    else
    {
        nxpesehal_ctrl.halStatus = ESE_STATUS_BUSY;
        status = phNxpEseP61_ProcessData(data_len, (uint8_t *)p_data);
        if (ESESTATUS_SUCCESS != status)
        {
            LOGE(" %s phNxpEseP61_ProcessData- Failed", __FUNCTION__);
            nxpesehal_ctrl.halStatus = ESE_STATUS_IDLE;
        }

        LOGD(" %s Exit status 0x%x", __FUNCTION__, status);
        return status;
    }
}


/******************************************************************************
 * Function         phNxpEseP61_read
 *
 * Description      This function write the data to ESE through physical
 *                  interface (e.g. I2C) using the P61 driver interface.
 *                  Before sending the data to ESE, phNxpEseP61_write_ext
 *                  is called to check if there is any extension processing
 *                  is required for the SPI packet being sent out.
 *
 * Returns          It returns number of bytes successfully written to ESE.
 *
 ******************************************************************************/
int phNxpEseP61_read(uint32_t data_len, const uint8_t *p_data)
{
    int ret = -1;
#ifdef USE_QSEE
    unsigned long long startTime;
#endif

#ifdef USE_MOBICORE
	timestamp_t startTime;
	timestamp_t currentTime;
#endif

#ifdef USE_BLOWFISH
    struct timespec req;
#endif

    LOGD("%s Enter", __FUNCTION__);
    if(p_data == NULL || data_len == 0)
    {
        LOGE("Invalid param %s", __FUNCTION__);
        return ret;
    }

    nxpesehal_ctrl.retry_cnt = 0;

    retry:
    ret = phTmlEse_spi_read((uint8_t *)p_data, data_len);
    if (ret < 0)
    {
        if((nxpesehal_ctrl.retry_cnt++ < MAX_RETRY_COUNT) && (ret != -ESESTATUS_RESPONSE_TIMEOUT))
        {
            LOGE("phTmlEse_spi_read - Retry");
            /* 1ms delay to give ESE wake up delay */
#ifdef USE_MOBICORE
			tlApiGetSecureTimestamp(&startTime);
			do{
				tlApiGetSecureTimestamp (&currentTime);
			}while((startTime != 0) &&((currentTime-startTime)<1000));
#endif
#ifdef USE_QSEE
            startTime = qsee_get_uptime();
            while (qsee_get_uptime() - startTime <= 1){}
#endif
#ifdef USE_BLOWFISH
            req.tv_sec = 0;
            req.tv_nsec = 1000000;
            nanosleep(&req,NULL);
#endif
            goto retry;
        }
        else
        {
            LOGE("phTmlEse_spi_read - (max count = 0x%x)", nxpesehal_ctrl.retry_cnt);
        }
    }

    LOGD("%s Exit", __FUNCTION__);
    return ret;
}


/******************************************************************************
 * Function         phNxpEseP61_WriteFrame
 *
 * Description      This is the actual function which is being called by
 *                  phNxpEseP61_write. This function writes the data to ESE.
 *                  It waits till write callback provide the result of write
 *                  process.
 *
 * Returns          It returns number of bytes successfully written to ESE.
 *
 ******************************************************************************/
ESESTATUS phNxpEseP61_WriteFrame(uint32_t data_len, const uint8_t *p_data)
{
	int ret = -1;
#ifdef USE_QSEE
    unsigned long long startTime;
#endif

#ifdef USE_MOBICORE
    timestamp_t startTime;
    timestamp_t currentTime;
#endif

#ifdef USE_BLOWFISH
    struct timespec req;
#endif

    ESESTATUS status = ESESTATUS_INVALID_PARAMETER;
    nxpesehal_ctrl.retry_cnt = 0;
    if(data_len > MAX_DATA_LEN || p_data == NULL)
    {
        LOGE("Invalid param %s", __FUNCTION__);
        return status;
    }
    /* Create local copy of cmd_data */
    memcpy(&nxpesehal_ctrl.p_cmd_data[0], p_data, data_len);
    nxpesehal_ctrl.cmd_len = data_len;

    retry:
    ret = phTmlEse_spi_write((uint8_t *) nxpesehal_ctrl.p_cmd_data,
            (uint16_t) nxpesehal_ctrl.cmd_len);
    if (ret < 0)
    {
        if(nxpesehal_ctrl.retry_cnt++ < MAX_RETRY_COUNT)
        {
            LOGE("phTmlEse_spi_write - Retry");
#ifdef USE_MOBICORE
			tlApiGetSecureTimestamp(&startTime);
			do{
				tlApiGetSecureTimestamp (&currentTime);
			}while((startTime != 0) &&((currentTime-startTime)<1000));
#endif
#ifdef USE_QSEE
        startTime = qsee_get_uptime();
        while (qsee_get_uptime() - startTime <= 1){}
#endif
#ifdef USE_BLOWFISH
            req.tv_sec = 0;
            req.tv_nsec = 1000000;
            nanosleep(&req,NULL);
#endif
            goto retry;
        }
        else
        {
            LOGE("phTmlEse_spi_write - (max count = 0x%x)", nxpesehal_ctrl.retry_cnt);
            status = ESESTATUS_FAILED;
        }
    }
    else
    {
        status  = ESESTATUS_SUCCESS;
    }

    LOGD("Exit %s status %x", __FUNCTION__, status);
    return status;
}
