/*****************************************************************************
 * Copyright Gemalto, unpublished work, created 2015. This computer program
 * includes Confidential, Proprietary Information and is a Trade Secret of
 * Gemalto. All use, disclosure, and/or reproduction is prohibited unless
 * authorised in writing by an officer of Gemalto. All Rights Reserved.
 *
 * Gemalto licenses this file to you under the secESE Gemalto License.
 * See NOTICE file for more information regarding copyright ownership.
 * A copy of secESE Gemalto License is available in LICENSE file included in
 * source code distribution of secESE Gemalto. You can ask a copy of the
 * License by contacting Gemalto (http://www.gemalto.com).
 ****************************************************************************/

/**
 * @file
 * $Author$
 * $Revision$
 * $Date$
 *
 * Samsung Secure eSE interface implementation.
 *
 */
 
#include <string.h>
#include <stdint.h>

#include "spi.h"
#include "iso7816_t1.h"
#include "secese.h"
#include "sec_spi_info.h"
#include "tz_debug.h"
#include "tz_utils.h"

#ifdef USE_QSEE
#include <qsee_spi.h>
#include <qsee_timer.h>
#include <qsee_heap.h>
#ifdef USE_ISPI_API
#include <qsee_env.h>
#include <CSPI.h>
#include <ISPI.h>
extern Object gSpiSingleton;
#endif
#endif

#ifdef USE_MOBICORE
#include "tlapi_secdrv.h"
#include "TlApi/TlApi.h"
#endif

#ifdef USE_BLOWFISH
#include <tee_spi.h>
#endif

#undef GPIO_CONTROL
#define GTO_CAPDU_MAX (4 + 3 + 3 + ((64 << 10) - 1))
#define GTO_RAPDU_MAX ((64 << 10) + 2)

/* At least a full size non-extended APDU must fit */
#if GTO_CAPDU_MAX < 262
# error GTO_CAPDU_MAX too small
#endif
#if GTO_RAPDU_MAX < 258
# error GTO_RAPDU_MAX too small
#endif
static struct {
    struct t1_state t1;

    uint8_t capdu[GTO_CAPDU_MAX];
    uint8_t rapdu[GTO_RAPDU_MAX];

    uint8_t check_alive;
    uint8_t isOpened;
} gto_ese;

ESESTATUS gtoSPI_open(void) {
    ESESTATUS status = ESE_OK;

    memset(&gto_ese, 0x00, sizeof(gto_ese));

    isot1_init(&gto_ese.t1);
    isot1_bind(&gto_ese.t1, 2, 1);

    gto_ese.isOpened = 1;

    return status;
}

ESESTATUS gtoSPI_transceive(p_secEse_7816_cpdu_t cpdu, p_secEse_7816_rpdu_t rpdu) {
    ESESTATUS status;

    size_t k = 0;
    int    r = 0;

    uint8_t *capdu_buf = gto_ese.capdu;
    uint8_t *rapdu_buf = gto_ese.rapdu;

    if (!gto_ese.isOpened) {
        LOGE("gtoSPI_transceive ESE_NOT_OPEN");
        return (ESESTATUS) ESE_NOT_OPEN;
    }

    /* Refuse Le greater than 65536 */
    if (cpdu->le_type && (cpdu->le > 65536)) {
        LOGE("gtoSPI_transceive ESE_INVALID_LE (gt 65536)");
        return (ESESTATUS) ESE_INVALID_LE;
    }

    /* Be sure that reception buffer is present */
    if (cpdu->cpdu_type && !rpdu->pdata) {
        return (ESESTATUS) ESE_INVALID_LE;
    }

    capdu_buf[k++] = cpdu->cla;
    capdu_buf[k++] = cpdu->ins;
    capdu_buf[k++] = cpdu->p1;
    capdu_buf[k++] = cpdu->p2;

    /* Lc present */
    if (cpdu->lc) {
        uint32_t lc = cpdu->lc;

        switch (cpdu->cpdu_type) {
            case 0:
                capdu_buf[k++] = (uint8_t)lc;
                break;

            case 1:
                capdu_buf[k++] = 0;
                capdu_buf[k++] = (uint8_t)(lc >> 8);
                capdu_buf[k++] = (uint8_t)(lc >> 0);
                break;

            default:
                LOGE("gtoSPI_transceive ESE_INVALID_LC");
                return (ESESTATUS) ESE_INVALID_LC;
        }
        if (k + lc > GTO_CAPDU_MAX) {
            LOGE("gtoSPI_transceive ESE_INVALID_LC (overflow)");
            return (ESESTATUS) ESE_INVALID_LC;
        }
        memcpy(capdu_buf + k, cpdu->pdata, lc);
        k += lc;
    }

    /* Le present */
    if (cpdu->le_type) {
        uint32_t le = cpdu->le;

        if (k + cpdu->le_type > GTO_CAPDU_MAX) {
            LOGE("gtoSPI_transceive ESE_INVALID_LE (overflow)");
            return (ESESTATUS) ESE_INVALID_LE;
        }
        switch (cpdu->le_type) {
            case 3: capdu_buf[k++] = 0;

            case 2: capdu_buf[k++] = (uint8_t)(le >> 8);

            case 1: capdu_buf[k++] = (uint8_t)(le >> 0);
                break;

            default:
                LOGE("gtoSPI_transceive ESE_INVALID_LE");
                return (ESESTATUS) ESE_INVALID_LE;
        }
    }

//    hex_print_tag("send", capdu_buf, k);
    r = isot1_transceive(&gto_ese.t1, capdu_buf, k,
                         rapdu_buf, GTO_RAPDU_MAX);

    if (r >= 2) {
        rpdu->len = r - 2;
        rpdu->sw1 = rapdu_buf[r - 2];
        rpdu->sw2 = rapdu_buf[r - 1];
        memcpy(rpdu->pdata, rapdu_buf, rpdu->len);
        status = ESE_OK;
    } else if (r >= 0) {
        status = (uint16_t) ESE_INVALID_DATA;
    } else {
        status = r; //errno_to_ese_status(r);
    }

    if (status != ESE_OK) {
        LOGE("gtoSPI_transceive status 0x%04x", status);
        gto_ese.check_alive = 1;
    }

    return status;
}

ESESTATUS gtoSPI_close(void) {
    if (!gto_ese.isOpened) {
        LOGE("gtoSPI_close ESE_NOT_OPEN");
        return (uint16_t) ESE_NOT_OPEN;
    }

    isot1_release(&gto_ese.t1);

    gto_ese.isOpened = 0;

    return ESE_OK;
}

#ifdef USE_QSEE
#ifdef USE_ISPI_API
void setISpiConfigurationGem(ISPI_Config_Ext* spi_config) {
#ifdef COMMON_VENDOR
    if (chip_vendor <= CHIP_VENDOR_THALES_UT51) {
        spi_config->spiFreq = SPI_MAX_FREQ_GEM;
    } else {
        spi_config->spiFreq = SPI_MAX_FREQ_GEM_UT60;
    }
#else
    spi_config->spiFreq = SPI_MAX_FREQ;
#endif
    spi_config->spiMode = ISPI_MODE_0;
    spi_config->endianness = ISPI_NATIVE;
    spi_config->csPolarity = ISPI_CS_POLARITY_ACTIVE_LOW;
    spi_config->csToggle = ISPI_CS_MODE_KEEP_ASSERTED;
    spi_config->bitsPerWord = 8;
    spi_config->loopBack = 0;
    spi_config->slaveIndex = 0;
    spi_config->csClkDelayCycles = 0x1F;
    spi_config->interWordDelayCycles = 0;
}
#endif
void setSpiConfigurationGem(qsee_spi_config_t* spi_config) {
    /**< SPI clock frequency */
#ifdef COMMON_VENDOR
    if (chip_vendor <= CHIP_VENDOR_THALES_UT51) {
        spi_config->max_freq = SPI_MAX_FREQ_GEM;
    } else {
        spi_config->max_freq = SPI_MAX_FREQ_GEM_UT60;
    }
#else
    spi_config->max_freq = SPI_MAX_FREQ;
#endif
    /**< Clock polarity  type to be used for the SPI core. */
    spi_config->spi_clk_polarity = QSEE_SPI_CLK_IDLE_LOW;
    /**< Shift mode(CPHA) type to be used for SPI core. */
    spi_config->spi_shift_mode = QSEE_SPI_INPUT_FIRST_MODE;
    /**< CS polarity type to be used for the SPI core. */
    spi_config->spi_cs_polarity = QSEE_SPI_CS_ACTIVE_LOW;
    /**< CS Mode to be used for the SPI core. */
    spi_config->spi_cs_mode = QSEE_SPI_CS_KEEP_ASSERTED;
    /**< Clock mode type to be used for the SPI core. */
    spi_config->spi_clk_always_on = QSEE_SPI_CLK_NORMAL;
    /**< SPI bits per word, any value from 3 to 31 */
    spi_config->spi_bits_per_word = 8;
#ifdef EXTENEDED_CONFIG
    /**< Put the Device in High Performance */
    spi_config->hs_mode = 0;
    /**< Put the Device in loop back test*/
    spi_config->loopback = 0;
#ifdef EXTENDED_NEW_CONFIG
    /**< Slave index from 0 to 3, if mulitple SPI devices are connected to the same master */
    spi_config->spi_slave_index = 0;
    /**< The minimum delay between two word(N-bit) transfers */
    spi_config->deassertion_time_ns = 0;
#endif
#endif
}

int spi_write_gem(const void *buf, int count) {
    qsee_spi_device_id_t deviceId;
#ifdef USE_ISPI_API
    ISPI_Config_Ext spi_config_ext;
    uint64_t bytesXferred;
    size_t bytesRead;
#else
    qsee_spi_config_t spi_config;
    qsee_spi_transaction_info_t spi_rx;
    qsee_spi_transaction_info_t spi_tx;
#endif
    int ret = 0;

    uint8_t rx_buffer[258] = {0,};
#ifdef GPIO_CONTROL
    int retval = 0;
    unsigned char data_cson[4] = "cson";
    unsigned char data_csoff[5] = "csoff";
    ese_oem_req_t cmd;
    unsigned int output;
    cmd.cmd_id = ESE_CS_ON;
    cmd.data = data_cson;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS ON qsee_oem_process_cmd retval = %d", retval);
#endif
#ifdef CS_CONTROL
    eSEGpioCSEnable();
#endif

    deviceId = ESE_SPI_DEVICE_ID;

#ifdef USE_ISPI_API
    setISpiConfigurationGem(&spi_config_ext);

    ret = ISPI_writeFullDuplexExt(gSpiSingleton, (int32_t)deviceId, &spi_config_ext, buf, count, &bytesXferred, rx_buffer, count, &bytesRead);
    if (ret != 0) {
        LOGE("gto spi_write error retcode = %d", ret);
        return -1;
    }
    ret = count;
#else
    setSpiConfigurationGem(&spi_config);

    spi_tx.buf_addr = (uint8_t*) buf;
    spi_tx.buf_len = count;
    spi_rx.buf_addr = rx_buffer;
    spi_rx.buf_len = count;

    ret = qsee_spi_full_duplex(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.buf_len;
#endif

#ifdef DEBUG_LOW
    hex_print_tag("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif

#ifdef GPIO_CONTROL
    cmd.cmd_id = ESE_CS_OFF;
    cmd.data = data_csoff;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS OFF qsee_oem_process_cmd retval = %d", retval);
#endif
#ifdef CS_CONTROL
    eSEGpioCSDisable();
#endif

    return ret;
}

int spi_read_gem(void *buf, int count) {
    qsee_spi_device_id_t deviceId;
#ifdef USE_ISPI_API
    ISPI_Config_Ext spi_config_ext;
    uint64_t bytesXferred;
    size_t bytesRead;
#else
    qsee_spi_config_t spi_config;
    qsee_spi_transaction_info_t spi_rx;
    qsee_spi_transaction_info_t spi_tx;
#endif
    int ret = 0;

    uint8_t tx_buffer[258] = {0,};

#ifdef GPIO_CONTROL
    int retval = 0;
    unsigned char data_cson[4] = "cson";
    unsigned char data_csoff[5] = "csoff";
    ese_oem_req_t cmd;
    unsigned int output;
    cmd.cmd_id = ESE_CS_ON;
    cmd.data = data_cson;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS ON qsee_oem_process_cmd retval = %d", retval);
#endif
#ifdef CS_CONTROL
    eSEGpioCSEnable();
#endif

    deviceId = ESE_SPI_DEVICE_ID;

#ifdef USE_ISPI_API
    setISpiConfigurationGem(&spi_config_ext);

    ret = ISPI_writeFullDuplexExt(gSpiSingleton, (int32_t)deviceId, &spi_config_ext, tx_buffer, count, &bytesXferred, buf, count, &bytesRead);
    if (ret != 0) {
        LOGE("gto spi_read error retcode = %d", ret);
        return -1;
    }
    ret = bytesRead;
#else
    setSpiConfigurationGem(&spi_config);

    spi_tx.buf_addr = tx_buffer;
    spi_tx.buf_len = count;
    spi_rx.buf_addr = (uint8_t*) buf;
    spi_rx.buf_len = count;

    ret = qsee_spi_full_duplex(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.buf_len;
#endif

#ifdef DEBUG_LOW
    hex_print_tag("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif


#ifdef GPIO_CONTROL
    cmd.cmd_id = ESE_CS_OFF;
    cmd.data = data_csoff;
    retval = qsee_oem_process_cmd((unsigned char *)&cmd, sizeof(cmd), (unsigned char *)&output, sizeof(unsigned int));
    if (retval != 0)
        LOGE("CS OFF qsee_oem_process_cmd retval = %d", retval);
#endif
#ifdef CS_CONTROL
    eSEGpioCSDisable();
#endif

    return ret;
}
#endif

#ifdef USE_MOBICORE
void setSpiConfigurationGem(spi_config_t* spi_config) {
    spi_config->delay_usecs = 100;
    spi_config->speed_hz = SPI_MAX_FREQ;
    spi_config->bits_per_word = 0;
    spi_config->dma_mode=0;
}

int spi_write_gem(const void *buf, int count) {
    uint32_t deviceId;
    spi_config_t spi_config;
    spi_transfer_t spi_rx;
    spi_transfer_t spi_tx;
    int ret = 0;

    uint8_t rx_buffer[258] = {0,};
    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfigurationGem(&spi_config);

    spi_tx.buf_addr = (uint8_t*) buf;
    spi_tx.buf_len = count;
    spi_rx.buf_addr = rx_buffer;
    spi_rx.buf_len = count;
    ret = tlApiSecSPIWriteRead(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.buf_len;
#ifdef DEBUG_LOW
    hex_print_tag("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

int spi_read_gem(void *buf, int count) {
    uint32_t deviceId;
    spi_config_t spi_config;
    spi_transfer_t spi_rx;
    spi_transfer_t spi_tx;
    int ret = 0;

    uint8_t tx_buffer[258] = {0,};
    deviceId = ESE_SPI_DEVICE_ID;
    setSpiConfigurationGem(&spi_config);

    spi_tx.buf_addr = tx_buffer;
    spi_tx.buf_len = count;
    spi_rx.buf_addr = (uint8_t*) buf;
    spi_rx.buf_len = count;
    ret = tlApiSecSPIWriteRead(deviceId, &spi_config, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.buf_len;
#ifdef DEBUG_LOW
    hex_print_tag("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

#endif

#ifdef USE_BLOWFISH

int spi_write_gem(const void *buf, int count) {
#if defined(GEM51) || defined(COMMON_VENDOR)
    int temp_tx_buffer_size;
    uint8_t temp_tx_buffer[258]= {0,};
#endif
    uint8_t rx_buffer[258] = {0,};
    TEE_Result ret = -1;

    TEES_SPIConfig cfg = {
        .speedHz = SPI_MAX_FREQ,
        .CPOL = 0,
        .CPHA = 0,
#if defined(GEM51)
        .bitsPerWord = 18,
#else
        .bitsPerWord = 0,
#endif
        .isDMAMode = false,
        .manualClockControl = true
    };

#if defined(GEM51)
    int padding_count = 4 - (count % 4);
    if (padding_count == 4) {
       padding_count = 0;
    }
    temp_tx_buffer_size = count+padding_count;
    memcpy(temp_tx_buffer,buf,count);

    TEES_SPITransfer spi_tx = {
        .bufAddr = temp_tx_buffer,
        .bufLen = temp_tx_buffer_size,
        .transferredLen = 0
    };

    TEES_SPITransfer spi_rx = {
        .bufAddr = rx_buffer,
        .bufLen = temp_tx_buffer_size,
        .transferredLen = 0
    };
#else
    TEES_SPITransfer spi_tx = {
        .bufAddr = (uint8_t*) buf,
        .bufLen = count,
        .transferredLen = 0
    };

    TEES_SPITransfer spi_rx = {
        .bufAddr = rx_buffer,
        .bufLen = count,
        .transferredLen = 0
    };
#endif

#ifdef COMMON_VENDOR
    if (chip_vendor == CHIP_VENDOR_THALES_UT20) {
        cfg.speedHz = SPI_MAX_FREQ_GEM;
    } else if (chip_vendor == CHIP_VENDOR_THALES_UT51) {
        cfg.speedHz = SPI_MAX_FREQ_GEM;
        cfg.bitsPerWord = 18;

        int padding_count = 4 - (count % 4);
        if (padding_count == 4) {
            padding_count = 0;
        }
        temp_tx_buffer_size = count+padding_count;
        memcpy(temp_tx_buffer,buf,count);

        spi_tx.bufAddr = temp_tx_buffer;
        spi_tx.bufLen = temp_tx_buffer_size;

        spi_rx.bufLen = temp_tx_buffer_size;
    } else {
        cfg.speedHz = SPI_MAX_FREQ_GEM_UT60;
    }
#endif
    LOGD("cfg.speedHz : %d", cfg.speedHz);

    ret = TEES_SPISetConfig(handler, &cfg);
    if (ret != TEE_SUCCESS) {
        LOGD("Failed to configure SPI, tee_err: %#x", ret);
        return ret;
    }

    ret = TEES_SPIWriteRead(handler, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_write error retcode = %d", ret);
        return -1;
    }
    ret = spi_tx.bufLen;
#ifdef DEBUG_LOW
    hex_print_tag("spi_write", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[SEND]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[SEND]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

int spi_read_gem(void *buf, int count) {
    uint8_t tx_buffer[258] = {0,};
    TEE_Result ret = -1;

    TEES_SPIConfig cfg = {
         .speedHz = SPI_MAX_FREQ,
         .CPOL = 0,
         .CPHA = 0,
         .bitsPerWord = 0,
         .isDMAMode = false,
         .manualClockControl = true
   };
   TEES_SPITransfer spi_tx = {
        .bufAddr = tx_buffer,
        .bufLen = count,
        .transferredLen = 0
    };

    TEES_SPITransfer spi_rx = {
        .bufAddr = (uint8_t*) buf,
        .bufLen = count,
        .transferredLen = 0
    };
    
#ifdef COMMON_VENDOR
    /**< SPI clock frequency */
    if (chip_vendor <= CHIP_VENDOR_THALES_UT51) {
        cfg.speedHz = SPI_MAX_FREQ_GEM;
    } else {
        cfg.speedHz = SPI_MAX_FREQ_GEM_UT60;
    }
#endif
    LOGD("cfg.speedHz : %d", cfg.speedHz);

    ret = TEES_SPISetConfig(handler, &cfg);
    if (ret != TEE_SUCCESS) {
        LOGD("Failed to configure SPI, tee_err: %#x", ret);
        return ret;
    }

    ret = TEES_SPIWriteRead(handler, &spi_tx, &spi_rx);
    if (ret != 0) {
        LOGE("gto spi_read error retcode = %d", ret);
        return -1;
    }
    ret = spi_rx.bufLen;
#ifdef DEBUG_LOW
    hex_print_tag("spi_read", (uint8_t*)buf, ret);
#else
    if (ret == 1) {
        // Do not print log
    } else if (ret < 10){
        hex_print_tag("[RECV]", (uint8_t*)buf, ret);
    } else {
        hex_print_tag("[RECV]", (uint8_t*)buf, 10);
    }
#endif
    return ret;
}

#endif

ESESTATUS gtoSPI_APDU_Transceive(uint16_t apduLen, uint8_t* pApdu, p_secEse_7816_rpdu_t rpdu) {
    int r = 0;
    uint8_t *rapdu_buf = gto_ese.rapdu;
    ESESTATUS status;

    if (rpdu == NULL || rpdu->pdata == NULL) {
        LOGE("invalid output buffer");
        return (uint16_t) ESE_INVALID_INPUT;
    }

    //hex_print_tag("pApdu", pApdu, apduLen);
    r = isot1_transceive(&gto_ese.t1, pApdu, apduLen, rapdu_buf, sizeof(gto_ese.rapdu));
    //hex_print_tag("rapdu_buf", rapdu_buf, r);

    if (r >= 2) {
        rpdu->len = r - 2;
        rpdu->sw1 = rapdu_buf[r - 2];
        rpdu->sw2 = rapdu_buf[r - 1];
        memcpy(rpdu->pdata, rapdu_buf, rpdu->len);
        status = ESE_OK;
    } else if (r >= 0) {
        status = (uint16_t) ESE_INVALID_DATA;
    } else {
        status = r; //errno_to_ese_status(r);
    }

    if (status != ESE_OK) {
        LOGE("gtoSPI_transceive status 0x%04x", status);
    }

    return status;
}

/* --------------------------------------------------------- */
