/*
 * Copyright (C)
 *
 * Handle SPI communication.
 * DO OPEN / CLOSE / SEND / RECEIVE / SET CONFIGURATION
 *
 * NOTICE : the ERROR which returned by below function is critical error.
 *          caller should finished the application when ERROR returned.
 *
 */


#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "lrc.h"
#include "spi_common.h"
#include "spi_communication.h"
#include "tz_debug.h"
#include "tz_utils.h"
#include "spi_protocol.h"

#define DELAY_AFTER_SENDING_IN_US 1000  // 1ms
#define DELAY_AFTER_SENDING_IN_MS 1     // 1ms


/**
 *
 * Compose I-Frame and send it to eSE
 * Input param seqnum(0x00 or 0x01), moredata(0x00 or 0x01),
 *             p_data(data to send), data_len(len of data, < MAX_INF_FIELD_LEN)
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_INVALID_INPUT_PARAM for invalid input parameter
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *
*/
ESESTATUS send_IFrame(uint8_t seqnum, uint8_t moredata, uint8_t *p_data, uint32_t data_len) {
    uint8_t frame_buf[MAX_FRAME_BUF_LEN] = {0x0, };
    /* Prologue(3byte) + data(n byte) + Epilogue(1byte) */
    uint32_t frame_buf_len = 3 + data_len + 1;
    int ret;

    // hex_print_tag_debug("[enter] send_IFrame ", p_data, data_len);
    // LOGD("[enter] send_IFrame : data_len 0x%x\n", data_len);

    /* check seqnum : bit7 encodes the sequence number */
    if((seqnum != 0x00) && (seqnum != 0x01))
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* check moredata: bit6 is more-data bit */
    if((moredata != 0x00) && (moredata != 0x01))
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* check p_data and data_len */
    if((NULL == p_data) || (data_len > MAX_INF_FIELD_LEN))
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* set prologue field */
    frame_buf[0] = NODE_ADDR_PEARL;
    frame_buf[1] = ((seqnum << 6) | (moredata << 5));
    frame_buf[2] = data_len;
    memcpy(frame_buf + 3, p_data, data_len);
    /* set epilogue field */
    frame_buf[frame_buf_len - 1] = computeLRC(frame_buf, 0x0, frame_buf_len - 1);
    hex_print_tag_debug("[SPI SEND]", frame_buf, frame_buf_len);
    ret = spi_write(frame_buf, frame_buf_len);
    if( ret < 0) {
        LOGE("[CHECK RETURN] %s, return value of write : %d\n",__func__, ret);
        return ESESTATUS_DEVICE_ERROR;
    }
    sleepProcess(DELAY_AFTER_SENDING_IN_MS); /* Polling interval */

    return ESESTATUS_SUCCESS;
}


/**
 *
 * Compose S-Frame and send it to eSE
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_INVALID_SBLOCK_CNTL_INFO for invalid control byte
 *        ESESTATUS_INVALID_INPUT_PARAM for invalid input parameter
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *
*/
ESESTATUS send_SFrame(uint8_t control, uint8_t *p_data, uint32_t data_len) {
    uint8_t frame_buf[MAX_FRAME_BUF_LEN] = {0x0, };
    /* Prologue(3byte) + data(n byte) + Epilogue(1byte) */
    uint32_t frame_buf_len = 3 + data_len + 1;
    int ret;
    LOGD("[enter] send_SFrame : control 0x%x\n", control);

    /* check S-FRAME control information */
    switch(control) {
    case S_RESYNCH_REQ:
    case S_ABORT_REQ:
    case S_WTX_RSP:
    case S_WARM_RESET_REQ:
    case S_SLEEP_ENABLE_REQ:
    case S_SET_CONF_REQ:
        /* it is valid control information */
        break;

    case S_RESYNCH_RSP:
    case S_ABORT_RSP:
    case S_WTX_REQ:
    case S_WARM_RESET_RSP:
    case S_SLEEP_ENABLE_RSP:
    case S_SET_CONF_RSP:
    case S_RECOVERY_RSP:
    default:
        /* it is invalid control information. eSE will not handle this one. */
        return ESESTATUS_INVALID_SBLOCK_CNTL_INFO;
//        break;
    }

    /* check data_len and p_data*/
    if ((data_len <= MAX_INF_FIELD_LEN)) {
        if(NULL == p_data)
            return ESESTATUS_INVALID_INPUT_PARAM;

        memcpy(frame_buf + 3, p_data, data_len);
    }else {
        return ESESTATUS_INVALID_INPUT_PARAM;
    }

    /* set prologue field */
    frame_buf[0] = NODE_ADDR_PEARL;
    frame_buf[1] = control;
    frame_buf[2] = data_len;
    /* set epilogue field */
    frame_buf[frame_buf_len - 1] = computeLRC(frame_buf, 0x0, frame_buf_len - 1);
    hex_print_tag_debug("[SPI SEND]", frame_buf, frame_buf_len);
    ret = spi_write(frame_buf, frame_buf_len);
    if( ret < 0) {
        LOGE("[CHECK RETURN] %s, return value of write : %d", __func__, ret);
        return ESESTATUS_DEVICE_ERROR;
    }
    sleepProcess(DELAY_AFTER_SENDING_IN_MS); /* Polling interval */

    return ESESTATUS_SUCCESS;
}

/**
 *
 * Compose R-Frame and send it to eSE
 * Input param seqnum(0x00 or 0x01), r_ack(0x80, 0x81, 0x82)
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_INVALID_INPUT_PARAM for invalid input parameter
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *
*/
ESESTATUS send_RFrame(uint8_t seqnum, uint8_t r_ack) {
    /* Prologue(3byte) + Epilogue(1byte) */
    uint8_t frame_buf[4] = {0x0, };
    uint32_t frame_buf_len = 4;
    int ret;
    LOGD("%s : seqnum(0x%x), rack(0x%x)", __func__, seqnum, r_ack);

    /* check seqnum : bit5 encodes the sequence number */
    if((seqnum != 0x00) && (seqnum != 0x01))
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* check r_ack : receive ready. it convey acknowledgment */
    if((r_ack < R_ERROR_FREE) && (r_ack > R_ERROR_OTHER))
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* set prologue field */
    frame_buf[0] = NODE_ADDR_PEARL;
    frame_buf[1] = ((seqnum << 4) | r_ack);
    frame_buf[2] = 0x0;
    /* set epilogue field */
    frame_buf[frame_buf_len - 1] = computeLRC(frame_buf, 0x0, frame_buf_len - 1);
    hex_print_tag_debug("[SPI SEND]", frame_buf, frame_buf_len);
    ret = spi_write(frame_buf, frame_buf_len);
    if( ret < 0) {
        LOGE("[CHECK RETURN] %s, return value of write : %d", __func__, ret);
        return ESESTATUS_DEVICE_ERROR;
    }
    sleepProcess(DELAY_AFTER_SENDING_IN_MS); /* Polling interval */

    return ESESTATUS_SUCCESS;
}

/**
 *
 * Receive Frame from eSE
 * This function receive NAD byte.
 * If it is non-zero then reveive PCB/LEN byte.
 * After that receive data and LRC at once.
 * This function doesn't check the frame contents.
 * Input param p_buffer(buffer to save received data)
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_INVALID_INPUT_PARAM for invalid input parameter
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *        ESESTATUS_POLLING_TIME_OUT for time out
 * NOTICE : buffer length should be bigger then MAX_FRAME_BUF_LEN.
*/
ESESTATUS receive_Frame(uint8_t * p_buffer) {
    int ret = -1;
    uint32_t polling_cnt = 0;
    uint8_t nad;

    if(NULL == p_buffer)
        return ESESTATUS_INVALID_INPUT_PARAM;

    /* polling at every POLLING_INTERVAL_IN_US */
    while(1) {
        ret = spi_read(&nad, 0x1);
        if( ret < 0) {
            LOGE("[CHECK RETURN] %s, return value of read : %d", __func__, ret);
            return ESESTATUS_DEVICE_ERROR;
        }

        if(nad != 0x00) {
            // LOGD("[%s], received nad 0x%x", __func__, nad);
            p_buffer[0] = nad;
            /* receive PCB and LEN */
            ret = spi_read(&p_buffer[1], 0x2);
            if( ret < 0) {
                LOGE("[CHECK RETURN] %s, return value of read : %d", __func__, ret);
                return ESESTATUS_DEVICE_ERROR;
            }

            /* Abnormal case. LEN byte is invalid. */
            if(p_buffer[2] == 0xFF) {
                /* TODO : print the log */
                LOGD("[%s] received length is 0xFF", __func__);
                p_buffer[2] = 0xFE;
            }

            /* receive DATA and LRC */
            ret = spi_read(&p_buffer[3], p_buffer[2] + 1);
            if( ret < 0) {
                LOGE("[CHECK RETURN] %s, return value of read : %d", __func__, ret);
                return ESESTATUS_DEVICE_ERROR;
            }

            hex_print_tag_debug("[SPI RECEIVE]", p_buffer, p_buffer[2] +4);
            return ESESTATUS_SUCCESS;
        } else {
            /* increase the polling counter.
               If reach MAX_POLLING_COUNTER then return error */
            polling_cnt++;

            if(MAX_POLLING_CNT <= polling_cnt)
                return ESESTATUS_POLLING_TIME_OUT;

            sleepProcess(POLLING_INTERVAL_IN_MS); /* Polling interval */
        }
    }
    /* Never reach */
    //return ESESTATUS_SUCCESS;
}

/**
 *
 * Send Null Byte
 * Input param none
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *
*/
ESESTATUS send_NullByte() {
    uint8_t nullByte = 0x0;
    int ret;

    ret = spi_write(&nullByte, 1);
    if( ret < 0) {
        LOGE("[CHECK RETURN] %s, return value of write : %d", __func__, ret);
        return ESESTATUS_DEVICE_ERROR;
    }

    LOGD("%s, null byte sent to eSE", __func__ );
    sleepProcess(DELAY_AFTER_SENDING_IN_MS); /* Polling interval */
    return ESESTATUS_SUCCESS;
}


/**
 *
 * Send Raw Data, This function doesn't care frame format.
 * Input param none
 * Return ESESTATUS_SUCCESS for success
 *        ESESTATUS_DEVICE_ERROR for device driver error
 *
*/
ESESTATUS send_RawData(uint8_t *p_data, uint32_t data_len) {
    int ret;
    /* TODO : Check Input Value */
    hex_print_tag_debug("[enter : send_RawData]", p_data, data_len);
    ret = spi_write(p_data, data_len);
    if (ret < 0) {
        LOGE("[CHECK RETURN] %s, return value of write : %d", __func__, ret);
        return ESESTATUS_DEVICE_ERROR;
    }
    sleepProcess(DELAY_AFTER_SENDING_IN_MS); /* Polling interval */

    return ESESTATUS_SUCCESS;
}


