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

/*!
* \file  phFriNfc_ISO15693Format.c
* \brief This component encapsulates different format functinalities ,
*        for the ISO-15693 card. 
*
* Project: NFC-FRI
*
* $Date:  $
* $Author: ing02260 $
* $Revision: 1.0 $
* $Aliases:  $
*
*/

#ifndef PH_FRINFC_FMT_ISO15693_DISABLED

#include <phNfcTypes.h>
#include <phFriNfc_OvrHal.h>
#include <phFriNfc_SmtCrdFmt.h>
#include <phFriNfc_ISO15693Format.h>


/****************************** Macro definitions start ********************************/
/* State for the format */
#define ISO15693_FORMAT                                 0x01U

/* Bytes per block in the ISO-15693 */
#define ISO15693_BYTES_PER_BLOCK                        0x04U

/* ISO-15693 Commands 
GET SYSTEM INFORMATION COMMAND
*/
#define ISO15693_GET_SYSTEM_INFO_CMD                    0x2BU
/* READ SINGLE BLOCK COMMAND */
#define ISO15693_RD_SINGLE_BLK_CMD                      0x20U
/* WRITE SINGLE BLOCK COMMAND */
#define ISO15693_WR_SINGLE_BLK_CMD                      0x21U
/* READ MULTIPLE BLOCK COMMAND */
#define ISO15693_RD_MULTIPLE_BLKS_CMD                   0x23U

/* CC bytes 
CC BYTE 0 - Magic Number - 0xE1
*/
#define ISO15693_CC_MAGIC_NUM                           0xE1U
/* CC BYTE 1 - Mapping version and READ WRITE settings 0x40
*/
#define ISO15693_CC_VER_RW                              0x40U
/* CC BYTE 2 - max size is calaculated using the byte 3 multiplied by 8 */
#define ISO15693_CC_MULTIPLE_FACTOR                     0x08U

/* Inventory command support mask for the CC byte 4 */
#define ISO15693_INVENTORY_CMD_MASK                     0x02U
/* Read MULTIPLE blocks support mask for CC byte 4 */
#define ISO15693_RDMULBLKS_CMD_MASK                     0x01U
/* Flags for the command */
#define ISO15693_FMT_FLAGS                              0x20U

/* Read two blocks */
#define ISO15693_RD_2_BLOCKS                            0x02U

/* TYPE identifier of the NDEF TLV */
#define ISO15693_NDEF_TLV_TYPE_ID                       0x03U
/* Terminator TLV identifier  */
#define ISO15693_TERMINATOR_TLV_ID                      0xFEU

/* UID 7th byte value shall be 0xE0 */
#define ISO15693_7TH_BYTE_UID_VALUE                     0xE0U
#define ISO15693_BYTE_7_INDEX                           0x07U

/* UID 6th byte value shall be 0x04 - NXP manufacturer */
#define ISO15693_6TH_BYTE_UID_VALUE                     0x04U
#define ISO15693_BYTE_6_INDEX                           0x06U

#define ISO15693_EXTRA_RESPONSE_FLAG                    0x01U

#define ISO15693_GET_SYS_INFO_RESP_LEN                  0x0EU
#define ISO15693_DSFID_MASK                             0x01U
#define ISO15693_AFI_MASK                               0x02U
#define ISO15693_MAX_SIZE_MASK                          0x04U
#define ISO15693_ICREF_MASK                             0x08U
#define ISO15693_SKIP_DFSID                             0x01U
#define ISO15693_SKIP_AFI                               0x01U
#define ISO15693_BLOCK_SIZE_IN_BYTES_MASK               0x1FU


/* MAXimum size of ICODE SLI/X */
#define ISO15693_SLI_X_MAX_SIZE                         112U
/* MAXimum size of ICODE SLI/X - S */
#define ISO15693_SLI_X_S_MAX_SIZE                       160U
/* MAXimum size of ICODE SLI/X - L */
#define ISO15693_SLI_X_L_MAX_SIZE                       32U
/****************************** Macro definitions end ********************************/

/****************************** Data structures start ********************************/
typedef enum phFriNfc_ISO15693_FormatSeq
{
    ISO15693_GET_SYS_INFO, 
    ISO15693_RD_SINGLE_BLK_CHECK,
    ISO15693_WRITE_CC_FMT, 
    ISO15693_WRITE_NDEF_TLV
}phFriNfc_ISO15693_FormatSeq_t;
/****************************** Data structures end ********************************/

/*********************** Static function declarations start ***********************/
static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProFormat (
    phFriNfc_sNdefSmtCrdFmt_t   *psNdefSmtCrdFmt);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_GetMaxDataSize (
    phFriNfc_sNdefSmtCrdFmt_t   *psNdefSmtCrdFmt, 
    uint8_t                     *p_recv_buf, 
    uint8_t                     recv_length);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_FmtReadWrite (
    phFriNfc_sNdefSmtCrdFmt_t   *psNdefSmtCrdFmt, 
    uint8_t                     command, 
    uint8_t                     *p_data, 
    uint8_t                     data_length);
/*********************** Static function declarations end ***********************/

/*********************** Static function definitions start ***********************/

static 
NFCSTATUS 
phFriNfc_ISO15693_H_FmtReadWrite (
    phFriNfc_sNdefSmtCrdFmt_t   *psNdefSmtCrdFmt, 
    uint8_t                     command, 
    uint8_t                     *p_data, 
    uint8_t                     data_length)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;
    uint8_t                     send_index = 0;

    /* set the data for additional data exchange*/
    psNdefSmtCrdFmt->psDepAdditionalInfo.DepFlags.MetaChaining = 0;
    psNdefSmtCrdFmt->psDepAdditionalInfo.DepFlags.NADPresent = 0;
    psNdefSmtCrdFmt->psDepAdditionalInfo.NAD = 0;

    psNdefSmtCrdFmt->SmtCrdFmtCompletionInfo.CompletionRoutine = 
                                            phFriNfc_ISO15693_FmtProcess;
    psNdefSmtCrdFmt->SmtCrdFmtCompletionInfo.Context = psNdefSmtCrdFmt;

    *psNdefSmtCrdFmt->SendRecvLength = PH_FRINFC_SMTCRDFMT_MAX_SEND_RECV_BUF_SIZE;

    psNdefSmtCrdFmt->Cmd.Iso15693Cmd = phHal_eIso15693_Cmd;

    *(psNdefSmtCrdFmt->SendRecvBuf + send_index) = (uint8_t)ISO15693_FMT_FLAGS;
    send_index = (uint8_t)(send_index + 1);

    *(psNdefSmtCrdFmt->SendRecvBuf + send_index) = (uint8_t)command;
    send_index = (uint8_t)(send_index + 1);

    (void)memcpy ((void *)(psNdefSmtCrdFmt->SendRecvBuf + send_index), 
        (void *)psNdefSmtCrdFmt->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info.Uid, 
        psNdefSmtCrdFmt->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info.UidLength);
    send_index = (uint8_t)(send_index + 
            psNdefSmtCrdFmt->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info.UidLength);

    switch (command)
    {        
        case ISO15693_WR_SINGLE_BLK_CMD:
        case ISO15693_RD_MULTIPLE_BLKS_CMD:
        {
            *(psNdefSmtCrdFmt->SendRecvBuf + send_index) = (uint8_t)
                        psNdefSmtCrdFmt->AddInfo.s_iso15693_info.current_block;
            send_index = (uint8_t)(send_index + 1);

            if (data_length)
            {
                (void)memcpy ((void *)(psNdefSmtCrdFmt->SendRecvBuf + send_index), 
                            (void *)p_data, data_length);
                send_index = (uint8_t)(send_index + data_length);
            }
            else
            {
                result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                    NFCSTATUS_INVALID_DEVICE_REQUEST);
            }
            break;
        }

        case ISO15693_RD_SINGLE_BLK_CMD:
        {
            *(psNdefSmtCrdFmt->SendRecvBuf + send_index) = (uint8_t)
                        psNdefSmtCrdFmt->AddInfo.s_iso15693_info.current_block;
            send_index = (uint8_t)(send_index + 1);
            break;
        }

        case ISO15693_GET_SYSTEM_INFO_CMD:
        {
            /* Dont do anything */
            break;
        }

        default:
        {
            result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                NFCSTATUS_INVALID_DEVICE_REQUEST);
            break;
        }
    }

    psNdefSmtCrdFmt->SendLength = send_index;

    if (!result)
    {
        result = phFriNfc_OvrHal_Transceive(psNdefSmtCrdFmt->LowerDevice,
                                            &psNdefSmtCrdFmt->SmtCrdFmtCompletionInfo,
                                            psNdefSmtCrdFmt->psRemoteDevInfo,
                                            psNdefSmtCrdFmt->Cmd,
                                            &psNdefSmtCrdFmt->psDepAdditionalInfo,
                                            psNdefSmtCrdFmt->SendRecvBuf,
                                            psNdefSmtCrdFmt->SendLength,
                                            psNdefSmtCrdFmt->SendRecvBuf,
                                            psNdefSmtCrdFmt->SendRecvLength);
    }

    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_GetMaxDataSize (
    phFriNfc_sNdefSmtCrdFmt_t   *psNdefSmtCrdFmt, 
    uint8_t                     *p_recv_buf, 
    uint8_t                     recv_length)
{
    NFCSTATUS                       result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693_AddInfo_t     *ps_iso15693_info = 
                                    &(psNdefSmtCrdFmt->AddInfo.s_iso15693_info);
    phHal_sIso15693Info_t           *ps_rem_iso_15693_info = 
                        &(psNdefSmtCrdFmt->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info);
    uint8_t                         recv_index = 0;

    if ((ISO15693_GET_SYS_INFO_RESP_LEN == recv_length)
        && (ISO15693_MAX_SIZE_MASK == (*p_recv_buf & ISO15693_MAX_SIZE_MASK)))
    {
        uint8_t information_flag = *p_recv_buf;
        /* MAX size is present in the system information and 
        also response length is correct */
        recv_index = (uint8_t)(recv_index + 1);

        if (!phOsalNfc_MemCompare ((void *)ps_rem_iso_15693_info->Uid, 
                                (void *)(p_recv_buf + recv_index), 
                                ps_rem_iso_15693_info->UidLength))
        {
            /* UID comaparision successful */
            uint8_t                 no_of_blocks = 0;
            uint8_t                 blk_size_in_bytes = 0;
            uint8_t                 ic_reference = 0;

            /* So skip the UID size compared in the received buffer */
            recv_index = (uint8_t)(recv_index + 
                                    ps_rem_iso_15693_info->UidLength);

            if (information_flag & ISO15693_DSFID_MASK) {
                /* Skip DFSID  */
                recv_index = (uint8_t)(recv_index + ISO15693_SKIP_DFSID);
            }
            if (information_flag & ISO15693_AFI_MASK) {
                /* Skip AFI  */
                recv_index = (uint8_t)(recv_index + ISO15693_SKIP_AFI);
            }

            /* To get the number of blocks in the card */
            no_of_blocks = (uint8_t)(*(p_recv_buf + recv_index) + 1);
            recv_index = (uint8_t)(recv_index + 1);

            /* To get the each block size in bytes */
            blk_size_in_bytes = (uint8_t)((*(p_recv_buf + recv_index) 
                                & ISO15693_BLOCK_SIZE_IN_BYTES_MASK) + 1);
            recv_index = (uint8_t)(recv_index + 1);

            if (information_flag & ISO15693_ICREF_MASK) {
                /* Get the IC reference */
                ic_reference = (uint8_t)(*(p_recv_buf + recv_index));
                if (ic_reference == 0x03) {
                    no_of_blocks = 8;
                }
            }

            /* calculate maximum data size in the card */
            ps_iso15693_info->max_data_size = (uint16_t)
                                        (no_of_blocks * blk_size_in_bytes);

        }
        else
        {
            result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                NFCSTATUS_INVALID_DEVICE_REQUEST);
        }                     
    }
    else
    {
        result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                            NFCSTATUS_INVALID_DEVICE_REQUEST);
    }


    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProFormat (
    phFriNfc_sNdefSmtCrdFmt_t *psNdefSmtCrdFmt)
{
    NFCSTATUS                       result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693_AddInfo_t     *ps_iso15693_info = 
                                    &(psNdefSmtCrdFmt->AddInfo.s_iso15693_info);
    phFriNfc_ISO15693_FormatSeq_t   e_format_seq = 
                                    (phFriNfc_ISO15693_FormatSeq_t)
                                    ps_iso15693_info->format_seq;
    uint8_t                         command_type = 0;
    uint8_t                         a_send_byte[ISO15693_BYTES_PER_BLOCK] = {0};
    uint8_t                         send_length = 0;
    uint8_t                         send_index = 0;
    uint8_t                         format_complete = FALSE;
    
    switch (e_format_seq)
    {
        case ISO15693_GET_SYS_INFO:
        {
            /* RESPONSE received for GET SYSTEM INFO  */

            if (!phFriNfc_ISO15693_H_GetMaxDataSize (psNdefSmtCrdFmt, 
                (psNdefSmtCrdFmt->SendRecvBuf + ISO15693_EXTRA_RESPONSE_FLAG), 
                (uint8_t)(*psNdefSmtCrdFmt->SendRecvLength - 
                ISO15693_EXTRA_RESPONSE_FLAG)))
            {
                /* Send the READ SINGLE BLOCK COMMAND */
                command_type = ISO15693_RD_SINGLE_BLK_CMD;
                e_format_seq = ISO15693_RD_SINGLE_BLK_CHECK;

                /* Block number 0 to read */
                psNdefSmtCrdFmt->AddInfo.s_iso15693_info.current_block = 0x00;
            }
            else
            {
                result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                    NFCSTATUS_INVALID_RECEIVE_LENGTH);
            }
            break;
        }

        case ISO15693_RD_SINGLE_BLK_CHECK:
        {
            /* RESPONSE received for READ SINGLE BLOCK
            received*/

            /* Check if Card is really fresh
               First 4 bytes must be 0 for fresh card */

            if ((psNdefSmtCrdFmt->AddInfo.s_iso15693_info.current_block == 0x00) &&
                (psNdefSmtCrdFmt->SendRecvBuf[1] != 0x00 ||
                 psNdefSmtCrdFmt->SendRecvBuf[2] != 0x00 ||
                 psNdefSmtCrdFmt->SendRecvBuf[3] != 0x00 ||
                 psNdefSmtCrdFmt->SendRecvBuf[4] != 0x00))
            {
                result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, NFCSTATUS_INVALID_FORMAT);
            }
            else
            {
                /* prepare data for writing CC bytes */

                command_type = ISO15693_WR_SINGLE_BLK_CMD;
                e_format_seq = ISO15693_WRITE_CC_FMT;

                /* CC magic number */
                *a_send_byte = (uint8_t)ISO15693_CC_MAGIC_NUM;
                send_index = (uint8_t)(send_index + 1);

                /* CC Version and read/write access */
                *(a_send_byte + send_index) = (uint8_t) ISO15693_CC_VER_RW;
                send_index = (uint8_t)(send_index + 1);

                /* CC MAX data size, calculated during GET system information */
                *(a_send_byte + send_index) = (uint8_t) (ps_iso15693_info->max_data_size / ISO15693_CC_MULTIPLE_FACTOR);
                send_index = (uint8_t)(send_index + 1);

                switch (ps_iso15693_info->max_data_size)
                {
                    case ISO15693_SLI_X_MAX_SIZE:
                    {
                        /* For SLI tags : Inventory Page read not supported */
                        *(a_send_byte + send_index) = (uint8_t) ISO15693_RDMULBLKS_CMD_MASK;
                        break;
                    }

                    case ISO15693_SLI_X_S_MAX_SIZE:
                    {
                        /* For SLI - S tags : Read multiple blocks not supported */
                        *(a_send_byte + send_index) = (uint8_t) ISO15693_INVENTORY_CMD_MASK;
                        break;
                    }

                    case ISO15693_SLI_X_L_MAX_SIZE:
                    {
                        /* For SLI - L tags : Read multiple blocks not supported */
                        *(a_send_byte + send_index) = (uint8_t) ISO15693_INVENTORY_CMD_MASK;
                        break;
                    }

                    default:
                    {
                        result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, NFCSTATUS_INVALID_DEVICE_REQUEST);
                        break;
                    }
                }

                send_index = (uint8_t)(send_index + 1);

                send_length = sizeof (a_send_byte);
            }

            break;
        }

        case ISO15693_WRITE_CC_FMT:
        {
            /* CC byte write succcessful. 
            Prepare data for NDEF TLV writing */
            command_type = ISO15693_WR_SINGLE_BLK_CMD;
            e_format_seq = ISO15693_WRITE_NDEF_TLV;

            ps_iso15693_info->current_block = (uint16_t)
                        (ps_iso15693_info->current_block + 1);
            
            /* NDEF TLV - Type byte updated to 0x03 */
            *a_send_byte = (uint8_t)ISO15693_NDEF_TLV_TYPE_ID;
            send_index = (uint8_t)(send_index + 1);

            /* NDEF TLV - Length byte updated to 0 */
            *(a_send_byte + send_index) = 0;
            send_index = (uint8_t)(send_index + 1);

            /* Terminator TLV - value updated to 0xFEU */
            *(a_send_byte + send_index) = (uint8_t)
                            ISO15693_TERMINATOR_TLV_ID;
            send_index = (uint8_t)(send_index + 1);

            send_length = sizeof (a_send_byte);
            break;
        }

        case ISO15693_WRITE_NDEF_TLV:
        {
            /* SUCCESSFUL formatting complete */
            format_complete = TRUE;
            break;
        }

        default:
        {
            result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                NFCSTATUS_INVALID_DEVICE_REQUEST);
            break;
        }
    }

    if ((!format_complete) && (!result))
    {
        result = phFriNfc_ISO15693_H_FmtReadWrite (psNdefSmtCrdFmt, 
                            command_type, a_send_byte, send_length);
    }

    ps_iso15693_info->format_seq = (uint8_t)e_format_seq; 
    return result;
}

/*********************** Static function definitions end ***********************/

/*********************** External function definitions start ***********************/
void 
phFriNfc_ISO15693_FmtReset (
    phFriNfc_sNdefSmtCrdFmt_t *psNdefSmtCrdFmt)
{
    /* reset to ISO15693 data structure */
    (void)memset((void *)&(psNdefSmtCrdFmt->AddInfo.s_iso15693_info), 
                0x00, sizeof (phFriNfc_ISO15693_AddInfo_t));
    psNdefSmtCrdFmt->FmtProcStatus = 0;
}

NFCSTATUS 
phFriNfc_ISO15693_Format (
    phFriNfc_sNdefSmtCrdFmt_t *psNdefSmtCrdFmt)
{
    NFCSTATUS                       result = NFCSTATUS_SUCCESS;
    phHal_sIso15693Info_t           *ps_rem_iso_15693_info = 
                        &(psNdefSmtCrdFmt->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info);
    
    
    if ((ISO15693_7TH_BYTE_UID_VALUE == 
        ps_rem_iso_15693_info->Uid[ISO15693_BYTE_7_INDEX]) 
        && (ISO15693_6TH_BYTE_UID_VALUE == 
        ps_rem_iso_15693_info->Uid[ISO15693_BYTE_6_INDEX]))
    {
        /* Check if the card is manufactured by NXP (6th byte 
        index of UID value = 0x04 and the 
        last byte of UID is 0xE0, only then the card detected 
        is NDEF compliant */
        psNdefSmtCrdFmt->State = ISO15693_FORMAT;

        /* GET system information command to get the card size */
        result = phFriNfc_ISO15693_H_FmtReadWrite (psNdefSmtCrdFmt, 
                            ISO15693_GET_SYSTEM_INFO_CMD, NULL, 0);
    }
    else
    {
        result = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                            NFCSTATUS_INVALID_DEVICE_REQUEST);
    }

    return result;
}

void 
phFriNfc_ISO15693_FmtProcess (
    void        *pContext,
    NFCSTATUS   Status)
{
    phFriNfc_sNdefSmtCrdFmt_t      *psNdefSmtCrdFmt = 
                                    (phFriNfc_sNdefSmtCrdFmt_t *)pContext;
    phFriNfc_ISO15693_AddInfo_t     *ps_iso15693_info = 
                                    &(psNdefSmtCrdFmt->AddInfo.s_iso15693_info);

    if((NFCSTATUS_SUCCESS & PHNFCSTBLOWER) == (Status & PHNFCSTBLOWER))
    {
        if (ISO15693_FORMAT == psNdefSmtCrdFmt->State)
        {
            /* Check for further formatting */
            Status = phFriNfc_ISO15693_H_ProFormat (psNdefSmtCrdFmt);
        }
        else
        {
            Status = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT, 
                                NFCSTATUS_INVALID_DEVICE_REQUEST);
        }
    }
    else
    {   
        Status = PHNFCSTVAL (CID_FRI_NFC_NDEF_SMTCRDFMT,
                            NFCSTATUS_FORMAT_ERROR);
    }

    /* Handle the all the error cases */
    if ((NFCSTATUS_PENDING & PHNFCSTBLOWER) != (Status & PHNFCSTBLOWER))
    {
        /* call respective CR */
        phFriNfc_SmtCrdFmt_HCrHandler (psNdefSmtCrdFmt, Status);
    }
}
/*********************** External function definitions end ***********************/


#endif /* #ifndef PH_FRINFC_FMT_ISO15693_DISABLED */