/*
 *
 * 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_ISO15693Map.c
* \brief This component encapsulates read/write/check ndef/process functionalities,
*        for the ISO-15693 Card. 
*
* Project: NFC-FRI
*
* $Date: $
* $Author: ing02260 $
* $Revision: $
* $Aliases:  $
*
*/

#ifndef PH_FRINFC_MAP_ISO15693_DISABLED

#include <phNfcTypes.h>
#include <phNfcConfig.h>
#include <phNfcInterface.h>
#include <phNfcHalTypes.h>
#include <phFriNfc.h>
#include <phFriNfc_NdefMap.h>
#include <phFriNfc_OvrHal.h>
#include <phFriNfc_MapTools.h>
#include <phFriNfc_ISO15693Map.h>

/************************** START DATA STRUCTURE *********************/

typedef enum phFriNfc_eChkNdefSeq
{
    ISO15693_NDEF_TLV_T, 
    ISO15693_NDEF_TLV_L, 
    ISO15693_NDEF_TLV_V, 
    ISO15693_PROP_TLV_L, 
    ISO15693_PROP_TLV_V

}phFriNfc_eChkNdefSeq_t;

typedef enum phFriNfc_eWrNdefSeq
{
    ISO15693_RD_BEFORE_WR_NDEF_L_0,
    ISO15693_WRITE_DATA, 
    ISO15693_RD_BEFORE_WR_NDEF_L,
    ISO15693_WRITE_NDEF_TLV_L

}phFriNfc_eWrNdefSeq_t;

#ifdef FRINFC_READONLY_NDEF

typedef enum phFriNfc_eRONdefSeq
{
    ISO15693_RD_BEFORE_WR_CC,
    ISO15693_WRITE_CC, 
    ISO15693_LOCK_BLOCK

}phFriNfc_eRONdefSeq_t;

#endif /* #ifdef FRINFC_READONLY_NDEF */

/************************** END DATA STRUCTURE *********************/

/************************** START MACROS definition *********************/




/* UID bytes to differentiate ICODE cards */
#define ISO15693_UID_BYTE_4                 0x04U
#define ISO15693_UID_BYTE_5                 0x05U
#define ISO15693_UID_BYTE_6                 0x06U
#define ISO15693_UID_BYTE_7                 0x07U

/* UID 7th byte value shall be 0xE0 */
#define ISO15693_UIDBYTE_7_VALUE            0xE0U
/* UID 6th byte value shall be 0x04 - NXP manufacturer */
#define ISO15693_UIDBYTE_6_VALUE            0x04U


/* UID value for 
    SL2 ICS20 
    SL2S2002 
    */
#define ISO15693_UIDBYTE_5_VALUE_SLI_X      0x01U
/* Card size SL2 ICS20 / SL2S2002 */
#define ISO15693_SL2_S2002_ICS20            112U 

/* UID value for 
    SL2 ICS53,          
    SL2 ICS54  
    SL2S5302 
*/
#define ISO15693_UIDBYTE_5_VALUE_SLI_X_S    0x02U
#define ISO15693_UIDBYTE_4_VALUE_SLI_X_S    0x00U
#define ISO15693_UIDBYTE_4_VALUE_SLI_X_SHC  0x80U
#define ISO15693_UIDBYTE_4_VALUE_SLI_X_SY   0x40U
/* SL2 ICS53, SL2 ICS54 and SL2S5302 */
#define ISO15693_SL2_S5302_ICS53_ICS54      160U

/* UID value for 
    SL2 ICS50       
    SL2 ICS51
    SL2S5002 
*/
#define ISO15693_UIDBYTE_5_VALUE_SLI_X_L    0x03U
#define ISO15693_UIDBYTE_4_VALUE_SLI_X_L    0x00U
#define ISO15693_UIDBYTE_4_VALUE_SLI_X_LHC  0x80U
/* SL2 ICS50, SL2 ICS51 and SL2S5002 */
#define ISO15693_SL2_S5002_ICS50_ICS51      32U


/* State Machine declaration
CHECK NDEF state */
#define ISO15693_CHECK_NDEF                 0x01U
/* READ NDEF state */
#define ISO15693_READ_NDEF                  0x02U
/* WRITE NDEF state */
#define ISO15693_WRITE_NDEF                 0x03U
#ifdef FRINFC_READONLY_NDEF

    /* READ ONLY NDEF state */
    #define ISO15693_READ_ONLY_NDEF         0x04U

    /* READ ONLY MASK byte for CC */
    #define ISO15693_CC_READ_ONLY_MASK      0x03U

    /* CC READ WRITE index */
    #define ISO15693_RW_BTYE_INDEX          0x01U

    /* LOCK BLOCK command */
    #define ISO15693_LOCK_BLOCK_CMD         0x22U

#endif /* #ifdef FRINFC_READONLY_NDEF */

/* CC Bytes 
Magic number */
#define ISO15693_CC_MAGIC_BYTE              0xE1U
/* Expected mapping version */
#define ISO15693_MAPPING_VERSION            0x01U
/* Major version is in upper 2 bits */
#define ISO15693_MAJOR_VERSION_MASK         0xC0U

/* CC indicating tag is capable of multi-block read */
#define ISO15693_CC_USE_MBR                 0x01U
/* CC indicating tag is capable of inventory page read */
#define ISO15693_CC_USE_IPR                 0x02U
/* EXTRA byte in the response */
#define ISO15693_EXTRA_RESP_BYTE            0x01U

/* Maximum card size multiplication factor */
#define ISO15693_MULT_FACTOR                0x08U
/* NIBBLE mask for READ WRITE access */
#define ISO15693_LSB_NIBBLE_MASK            0x0FU
#define ISO15693_RD_WR_PERMISSION           0x00U
#define ISO15693_RD_ONLY_PERMISSION         0x03U

/* READ command identifier */
#define ISO15693_READ_COMMAND               0x20U

/* READ multiple command identifier */
#define ISO15693_READ_MULTIPLE_COMMAND      0x23U

/* INVENTORY pageread command identifier */
#define ICODE_INVENTORY_PAGEREAD_COMMAND    0xB0U
#define INVENTORY_PAGEREAD_FLAGS            0x24U
#define NXP_MANUFACTURING_CODE              0x04U

/* WRITE command identifier */
#define ISO15693_WRITE_COMMAND              0x21U
/* FLAG option */
#define ISO15693_FLAGS                      0x20U

/* RESPONSE length expected for single block READ */
#define ISO15693_SINGLE_BLK_RD_RESP_LEN     0x04U
/* NULL TLV identifier */
#define ISO15693_NULL_TLV_ID                0x00U
/* NDEF TLV, TYPE identifier  */
#define ISO15693_NDEF_TLV_TYPE_ID           0x03U

/* 8 BIT shift */
#define ISO15693_BTYE_SHIFT                 0x08U

/* Proprietary TLV TYPE identifier */
#define ISO15693_PROP_TLV_ID                0xFDU

/* CC SIZE in BYTES */
#define ISO15693_CC_SIZE                    0x04U

/* To get the remaining size in the card. 
Inputs are 
1. maximum data size 
2. block number 
3. index of the block number */
#define ISO15693_GET_REMAINING_SIZE(max_data_size, blk, index) \
    (max_data_size - ((blk * ISO15693_BYTES_PER_BLOCK) + index))

#define ISO15693_GET_LEN_FIELD_BLOCK_NO(blk, byte_addr, ndef_size) \
    (((byte_addr + ((ndef_size >= ISO15693_THREE_BYTE_LENGTH_ID) ? 3 : 1)) > \
    (ISO15693_BYTES_PER_BLOCK - 1)) ? (blk + 1) : blk)

#define ISO15693_GET_LEN_FIELD_BYTE_NO(blk, byte_addr, ndef_size) \
    (((byte_addr + ((ndef_size >= ISO15693_THREE_BYTE_LENGTH_ID) ? 3 : 1)) % \
    ISO15693_BYTES_PER_BLOCK))
    


/************************** END MACROS definition *********************/

/************************** START static functions declaration *********************/
static
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessReadOnly (
    phFriNfc_NdefMap_t      *psNdefMap);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessWriteNdef (
    phFriNfc_NdefMap_t      *psNdefMap);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessReadNdef (
    phFriNfc_NdefMap_t      *psNdefMap);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessCheckNdef (
    phFriNfc_NdefMap_t      *psNdefMap);

static 
void 
phFriNfc_ISO15693_H_Complete (
    phFriNfc_NdefMap_t      *psNdefMap,
    NFCSTATUS               Status);

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ReadWrite (
    phFriNfc_NdefMap_t      *psNdefMap, 
    uint8_t                 command, 
    uint8_t                 *p_data, 
    uint8_t                 data_length);

static
NFCSTATUS
phFriNfc_ReadRemainingInMultiple (
    phFriNfc_NdefMap_t  *psNdefMap,
    uint32_t            startBlock);

/************************** END static functions declaration *********************/

/************************** START static functions definition *********************/

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessWriteNdef (
    phFriNfc_NdefMap_t      *psNdefMap)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);
    phFriNfc_eWrNdefSeq_t       e_wr_ndef_seq = (phFriNfc_eWrNdefSeq_t)
                                psNdefMap->ISO15693Container.ndef_seq;
    uint8_t                     *p_recv_buf = NULL;
    uint8_t                     recv_length = 0;
    uint8_t                     write_flag = FALSE;
    uint8_t                     a_write_buf[ISO15693_BYTES_PER_BLOCK] = {0};
    uint8_t                     remaining_size = 0;

    switch (e_wr_ndef_seq)
    {
        case ISO15693_RD_BEFORE_WR_NDEF_L_0:
        {
            /* L byte is read  */
            p_recv_buf = (psNdefMap->SendRecvBuf + ISO15693_EXTRA_RESP_BYTE);
            recv_length = (uint8_t)
                        (*psNdefMap->SendRecvLength - ISO15693_EXTRA_RESP_BYTE);

            if (ISO15693_SINGLE_BLK_RD_RESP_LEN == recv_length)
            {
                /* Response length is correct */
                uint8_t     byte_index = 0;

                /* Copy the recevied buffer */
                (void)memcpy ((void *)a_write_buf, (void *)p_recv_buf, 
                                recv_length);

                byte_index = ISO15693_GET_LEN_FIELD_BYTE_NO(
                        ps_iso_15693_con->ndef_tlv_type_blk, 
                        ps_iso_15693_con->ndef_tlv_type_byte, 
                        psNdefMap->ApduBufferSize);

                /* Writing length field to 0, Update length field to 0 */
                *(a_write_buf + byte_index) = 0x00;
                
                if ((ISO15693_BYTES_PER_BLOCK - 1) != byte_index)
                {
                    /* User data is updated in the buffer */
                    byte_index = (uint8_t)(byte_index + 1);
                    /* Block number shall be udate */
                    remaining_size = (ISO15693_BYTES_PER_BLOCK - byte_index);

                    if ((psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex) 
                        < remaining_size)
                    {
                        remaining_size = (uint8_t)(psNdefMap->ApduBufferSize - 
                                                    psNdefMap->ApduBuffIndex);
                    }

                    /* Go to next byte to fill the write buffer */
                    (void)memcpy ((void *)(a_write_buf + byte_index), 
                                (void *)(psNdefMap->ApduBuffer + 
                                psNdefMap->ApduBuffIndex), remaining_size);

                    /* Write index updated */
                    psNdefMap->ApduBuffIndex = (uint8_t)(psNdefMap->ApduBuffIndex + 
                                                remaining_size);                    
                }
                
                /* After this write, user data can be written. 
                Update the sequence accordingly */
                e_wr_ndef_seq = ISO15693_WRITE_DATA;
                write_flag = TRUE;
            } /* if (ISO15693_SINGLE_BLK_RD_RESP_LEN == recv_length) */
            else
            {
                result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                    NFCSTATUS_INVALID_RECEIVE_LENGTH);
            }
            break;
        } /* case ISO15693_RD_BEFORE_WR_NDEF_L_0: */

        case ISO15693_RD_BEFORE_WR_NDEF_L:
        {
            p_recv_buf = (psNdefMap->SendRecvBuf + ISO15693_EXTRA_RESP_BYTE);
            recv_length = (uint8_t)(*psNdefMap->SendRecvLength - 
                            ISO15693_EXTRA_RESP_BYTE);

            if (ISO15693_SINGLE_BLK_RD_RESP_LEN == recv_length)
            {
                uint8_t     byte_index = 0;

                (void)memcpy ((void *)a_write_buf, (void *)p_recv_buf, 
                                recv_length);
                
                byte_index = ISO15693_GET_LEN_FIELD_BYTE_NO(
                                ps_iso_15693_con->ndef_tlv_type_blk, 
                                ps_iso_15693_con->ndef_tlv_type_byte, 
                                psNdefMap->ApduBuffIndex);

                *(a_write_buf + byte_index) = (uint8_t)psNdefMap->ApduBuffIndex;
                e_wr_ndef_seq = ISO15693_WRITE_NDEF_TLV_L;
                write_flag = TRUE;
            }
            else
            {
                result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                    NFCSTATUS_INVALID_RECEIVE_LENGTH);
            }
            break;
        }

        case ISO15693_WRITE_DATA:
        {
            if ((psNdefMap->ApduBufferSize == psNdefMap->ApduBuffIndex) 
                || (ps_iso_15693_con->current_block == 
                    (ps_iso_15693_con->max_data_size / ISO15693_BYTES_PER_BLOCK)))
            { 
                ps_iso_15693_con->current_block = 
                        ISO15693_GET_LEN_FIELD_BLOCK_NO(
                        ps_iso_15693_con->ndef_tlv_type_blk, 
                        ps_iso_15693_con->ndef_tlv_type_byte, 
                        psNdefMap->ApduBuffIndex);
                e_wr_ndef_seq = ISO15693_RD_BEFORE_WR_NDEF_L;
            }            
            else
            {
                remaining_size = ISO15693_BYTES_PER_BLOCK;

                ps_iso_15693_con->current_block = (uint16_t)
                                    (ps_iso_15693_con->current_block + 1);
                
                if ((psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex) 
                    < remaining_size)
                {
                    remaining_size = (uint8_t)(psNdefMap->ApduBufferSize - 
                                                psNdefMap->ApduBuffIndex);
                }

                (void)memcpy ((void *)a_write_buf, (void *)
                                (psNdefMap->ApduBuffer + 
                                psNdefMap->ApduBuffIndex), remaining_size);

                psNdefMap->ApduBuffIndex = (uint8_t)(psNdefMap->ApduBuffIndex + 
                                                remaining_size);
                write_flag = TRUE;
            } 
            break;
        } /* case ISO15693_WRITE_DATA: */

        case ISO15693_WRITE_NDEF_TLV_L:
        {
            *psNdefMap->WrNdefPacketLength = psNdefMap->ApduBuffIndex;
            ps_iso_15693_con->actual_ndef_size = psNdefMap->ApduBuffIndex;
            break;
        }

        default:
        {
            break;
        }
    } /* switch (e_wr_ndef_seq) */

    if (((0 == psNdefMap->ApduBuffIndex) 
        || (*psNdefMap->WrNdefPacketLength != psNdefMap->ApduBuffIndex))
        && (!result))
    {
        if (FALSE == write_flag)
        {
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                    ISO15693_READ_COMMAND, NULL, 0);
        }
        else
        {
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                        ISO15693_WRITE_COMMAND, 
                                        a_write_buf, sizeof (a_write_buf));
        }
    }

    psNdefMap->ISO15693Container.ndef_seq = (uint8_t)e_wr_ndef_seq;
    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ReadWrite (
    phFriNfc_NdefMap_t      *psNdefMap, 
    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*/
    psNdefMap->psDepAdditionalInfo.DepFlags.MetaChaining = 0;
    psNdefMap->psDepAdditionalInfo.DepFlags.NADPresent = 0;
    psNdefMap->psDepAdditionalInfo.NAD = 0;

    psNdefMap->MapCompletionInfo.CompletionRoutine = phFriNfc_ISO15693_Process;
    psNdefMap->MapCompletionInfo.Context = psNdefMap;

    *psNdefMap->SendRecvLength = psNdefMap->TempReceiveLength;

    psNdefMap->Cmd.Iso15693Cmd = phHal_eIso15693_Cmd;

    *(psNdefMap->SendRecvBuf + send_index) = (uint8_t)ISO15693_FLAGS;
    send_index = (uint8_t)(send_index + 1);

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

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

    *(psNdefMap->SendRecvBuf + send_index) = (uint8_t)
                                psNdefMap->ISO15693Container.current_block;
    send_index = (uint8_t)(send_index + 1);

    if ((ISO15693_WRITE_COMMAND == command) ||
        (ISO15693_READ_MULTIPLE_COMMAND == command))
    {
        (void)memcpy ((void *)(psNdefMap->SendRecvBuf + send_index), 
                    (void *)p_data, data_length);
        send_index = (uint8_t)(send_index + data_length);
    }

    psNdefMap->SendLength = send_index;
    result = phFriNfc_OvrHal_Transceive(psNdefMap->LowerDevice,
                                        &psNdefMap->MapCompletionInfo,
                                        psNdefMap->psRemoteDevInfo,
                                        psNdefMap->Cmd,
                                        &psNdefMap->psDepAdditionalInfo,
                                        psNdefMap->SendRecvBuf,
                                        psNdefMap->SendLength,
                                        psNdefMap->SendRecvBuf,
                                        psNdefMap->SendRecvLength);

    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_Inventory_Page_Read (
    phFriNfc_NdefMap_t      *psNdefMap, 
    uint8_t                 command, 
    uint8_t                 page,
    uint8_t                 numPages)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;
    uint8_t                     send_index = 0;

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

    psNdefMap->MapCompletionInfo.CompletionRoutine = phFriNfc_ISO15693_Process;
    psNdefMap->MapCompletionInfo.Context = psNdefMap;

    *psNdefMap->SendRecvLength = psNdefMap->TempReceiveLength;

    psNdefMap->Cmd.Iso15693Cmd = phHal_eIso15693_Cmd;

    *(psNdefMap->SendRecvBuf + send_index) = INVENTORY_PAGEREAD_FLAGS;
    send_index = (uint8_t)(send_index + 1);

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

    *(psNdefMap->SendRecvBuf + send_index) = NXP_MANUFACTURING_CODE;
    send_index = (uint8_t)(send_index + 1);

    *(psNdefMap->SendRecvBuf + send_index) = 0x40;
    send_index = (uint8_t)(send_index + 1);

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

    *(psNdefMap->SendRecvBuf + send_index) = (uint8_t)
                                page;
    send_index = (uint8_t)(send_index + 1);

    *(psNdefMap->SendRecvBuf + send_index) = (uint8_t)
                                numPages;
    send_index = (uint8_t)(send_index + 1);

    psNdefMap->SendLength = send_index;

    result = phFriNfc_OvrHal_Transceive(psNdefMap->LowerDevice,
                                        &psNdefMap->MapCompletionInfo,
                                        psNdefMap->psRemoteDevInfo,
                                        psNdefMap->Cmd,
                                        &psNdefMap->psDepAdditionalInfo,
                                        psNdefMap->SendRecvBuf,
                                        psNdefMap->SendLength,
                                        psNdefMap->SendRecvBuf,
                                        psNdefMap->SendRecvLength);

    return result;
}

static 
NFCSTATUS
phFriNfc_ISO15693_Reformat_Pageread_Buffer (
    uint8_t                 *p_recv_buf,
    uint8_t                 recv_length,
    uint8_t                 *p_dst_buf,
    uint8_t                 dst_length)
{
   // Inventory page reads return an extra security byte per page
   // So we need to reformat the returned buffer in memory
    uint32_t i = 0;
    uint32_t reformatted_index = 0;
    while (i < recv_length) {
        // Going for another page of 16 bytes, check for space in dst buffer
        if (reformatted_index + 16 > dst_length) {
            break;
        }
        if (p_recv_buf[i] == 0x0F) {
            // Security, insert 16 0 bytes
            memset(&(p_dst_buf[reformatted_index]), 0, 16);
            reformatted_index += 16;
            i++;
        } else {
            // Skip security byte
            i++;
            if (i + 16 <= recv_length) {
                memcpy(&(p_dst_buf[reformatted_index]), &(p_recv_buf[i]), 16);
                reformatted_index += 16;
            } else {
                break;
            }
            i+=16;
        }
    }
    return reformatted_index;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessReadNdef (
    phFriNfc_NdefMap_t      *psNdefMap)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);
    uint16_t                    remaining_data_size = 0;
    uint8_t                     *p_recv_buf = 
                                (psNdefMap->SendRecvBuf + ISO15693_EXTRA_RESP_BYTE);
    uint8_t                     recv_length = (uint8_t)
                                (*psNdefMap->SendRecvLength - ISO15693_EXTRA_RESP_BYTE);

    uint8_t *reformatted_buf = (uint8_t*) phOsalNfc_GetMemory(ps_iso_15693_con->max_data_size);

    if (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR)
    {
        uint8_t reformatted_size = phFriNfc_ISO15693_Reformat_Pageread_Buffer(p_recv_buf, recv_length,
                reformatted_buf, ps_iso_15693_con->max_data_size);
        p_recv_buf = reformatted_buf + (ps_iso_15693_con->current_block * ISO15693_BYTES_PER_BLOCK);
        recv_length = reformatted_size - (ps_iso_15693_con->current_block * ISO15693_BYTES_PER_BLOCK);
    }
    if (ps_iso_15693_con->store_length)
    {
        /* Continue Offset option selected 
            So stored data already existing, 
            copy the information to the user buffer 
        */
        if (ps_iso_15693_con->store_length 
            <= (psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex))
        {
            /* Stored data length is less than or equal 
                to the user expected size */
            (void)memcpy ((void *)(psNdefMap->ApduBuffer + 
                        psNdefMap->ApduBuffIndex), 
                            (void *)ps_iso_15693_con->store_read_data, 
                        ps_iso_15693_con->store_length);

            psNdefMap->ApduBuffIndex = (uint16_t)(psNdefMap->ApduBuffIndex + 
                                        ps_iso_15693_con->store_length);

            remaining_data_size = ps_iso_15693_con->store_length;
            
            ps_iso_15693_con->store_length = 0;                
        }
        else
        {
            /* stored length is more than the user expected size */
            remaining_data_size = (uint16_t)(ps_iso_15693_con->store_length -
                                (psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex));

            (void)memcpy ((void *)(psNdefMap->ApduBuffer + 
                        psNdefMap->ApduBuffIndex), 
                        (void *)ps_iso_15693_con->store_read_data, 
                        remaining_data_size);

                /* As stored data is more than the user expected data. So store 
                    the remaining bytes again into the data structure */
            (void)memcpy ((void *)ps_iso_15693_con->store_read_data, 
                        (void *)(ps_iso_15693_con->store_read_data + 
                        remaining_data_size), 
                        (ps_iso_15693_con->store_length - remaining_data_size));

            psNdefMap->ApduBuffIndex = (uint16_t)(psNdefMap->ApduBuffIndex + 
                                        remaining_data_size);

                ps_iso_15693_con->store_length = (uint8_t)
                            (ps_iso_15693_con->store_length - remaining_data_size);
        }
    } /* if (ps_iso_15693_con->store_length) */
    else
    {
            /* Data is read from the card. */
        uint8_t                 byte_index = 0;

        remaining_data_size = ps_iso_15693_con->remaining_size_to_read;

            /* Check if the block number is to read the first VALUE field */
        if (ISO15693_GET_VALUE_FIELD_BLOCK_NO(ps_iso_15693_con->ndef_tlv_type_blk, 
                                    ps_iso_15693_con->ndef_tlv_type_byte, 
                                    ps_iso_15693_con->actual_ndef_size) 
            == ps_iso_15693_con->current_block)
        {
            /* Read from the beginning option selected, 
                BYTE number may start from the middle */
            byte_index = (uint8_t)ISO15693_GET_VALUE_FIELD_BYTE_NO(
                            ps_iso_15693_con->ndef_tlv_type_blk, 
                            ps_iso_15693_con->ndef_tlv_type_byte, 
                            ps_iso_15693_con->actual_ndef_size);
        }

        if ((psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex)  
            < remaining_data_size)
        {
                remaining_data_size = (uint8_t)
                                    (recv_length - byte_index);
                /* user input is less than the remaining card size */
            if ((psNdefMap->ApduBufferSize - psNdefMap->ApduBuffIndex) 
                    < (uint16_t)remaining_data_size)
            {
                    /* user data required is less than the data read */
                remaining_data_size = (uint8_t)(psNdefMap->ApduBufferSize - 
                                                psNdefMap->ApduBuffIndex);

                    if (0 != (recv_length - (byte_index + 
                                    remaining_data_size)))
                    {
                /* Store the data for the continue read option */
                (void)memcpy ((void *)ps_iso_15693_con->store_read_data, 
                                (void *)(p_recv_buf + (byte_index + 
                                        remaining_data_size)), 
                                        (recv_length - (byte_index + 
                                        remaining_data_size)));

                ps_iso_15693_con->store_length = (uint8_t)
                                    (recv_length - (byte_index + 
                                        remaining_data_size));
            }
            }
        }
        else
        {
                /* user data required is equal or greater than the data read */
            if (remaining_data_size > (recv_length - byte_index))                
            {
                remaining_data_size = (uint8_t)
                                (recv_length - byte_index);
            }
        }

            /* Copy data in the user buffer */
        (void)memcpy ((void *)(psNdefMap->ApduBuffer + 
                        psNdefMap->ApduBuffIndex), 
                        (void *)(p_recv_buf + byte_index), 
                        remaining_data_size);

            /* Update the read index */
        psNdefMap->ApduBuffIndex = (uint16_t)(psNdefMap->ApduBuffIndex + 
                                    remaining_data_size);            

        } /* else part of if (ps_iso_15693_con->store_length) */

    /* Remaining size is decremented */
    ps_iso_15693_con->remaining_size_to_read = (uint8_t)
                            (ps_iso_15693_con->remaining_size_to_read - 
                            remaining_data_size);

    if ((psNdefMap->ApduBuffIndex != psNdefMap->ApduBufferSize)
        && (0 != ps_iso_15693_con->remaining_size_to_read))
    {            
        ps_iso_15693_con->current_block = (uint16_t)
                            (ps_iso_15693_con->current_block + 1);
        /* READ again */
        if ((ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_MBR) ||
            (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR)) {
            result = phFriNfc_ReadRemainingInMultiple(psNdefMap, ps_iso_15693_con->current_block);
        }
        else {
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, ISO15693_READ_COMMAND, 
                                                    NULL, 0);
        }
    }
    else
    {
            /* Read completed, EITHER index has reached to the user size 
            OR end of the card is reached
            update the user data structure with read data size */
        *psNdefMap->NumOfBytesRead = psNdefMap->ApduBuffIndex;
    }
    if (reformatted_buf != NULL) {
        phOsalNfc_FreeMemory(reformatted_buf);
    }
    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_CheckCCBytes (
    phFriNfc_NdefMap_t      *psNdefMap)
{
    NFCSTATUS               result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693Cont_t *ps_iso_15693_con = 
                            &(psNdefMap->ISO15693Container);
    uint8_t                 recv_index = 0;
    uint8_t                 *p_recv_buf = (psNdefMap->SendRecvBuf + 1);

    /* expected CC byte : E1 40 "MAX SIZE depends on tag" */
    if (ISO15693_CC_MAGIC_BYTE == *p_recv_buf)
    {
        /*  0xE1 magic byte found*/
        recv_index = (uint8_t)(recv_index + 1);
        uint8_t tag_major_version = (*(p_recv_buf + recv_index) & ISO15693_MAJOR_VERSION_MASK) >> 6;
        if (ISO15693_MAPPING_VERSION >= tag_major_version)
        {
            /* Correct mapping version found */
            switch (*(p_recv_buf + recv_index) & ISO15693_LSB_NIBBLE_MASK)
            {
                case ISO15693_RD_WR_PERMISSION:
                {
                    /* READ/WRITE possible */
                    psNdefMap->CardState = PH_NDEFMAP_CARD_STATE_READ_WRITE;
                    break;
                }

                case ISO15693_RD_ONLY_PERMISSION:
                {
                    /* ONLY READ possible, WRITE NOT possible */
                    psNdefMap->CardState = PH_NDEFMAP_CARD_STATE_READ_ONLY;
                    break;
                }

                default:
                {
                    result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                        NFCSTATUS_NO_NDEF_SUPPORT);
                    break;
                }
            }
            recv_index = (uint8_t)(recv_index + 1);
            
            if (!result)
            {
                /* Update MAX SIZE */
                ps_iso_15693_con->max_data_size = (uint16_t)
                    (*(p_recv_buf + recv_index) *
                    ISO15693_MULT_FACTOR);
                recv_index = (uint8_t)(recv_index + 1);
                ps_iso_15693_con->read_capabilities = (*(p_recv_buf + recv_index));


            }
        }
        else
        {
            result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_NO_NDEF_SUPPORT);
        }
    }
    else
    {
        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_NO_NDEF_SUPPORT);
    }
    return result;
}

static 
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessCheckNdef (
    phFriNfc_NdefMap_t      *psNdefMap)
{
    NFCSTATUS               result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693Cont_t *ps_iso_15693_con = 
                            &(psNdefMap->ISO15693Container);
    phFriNfc_eChkNdefSeq_t  e_chk_ndef_seq = (phFriNfc_eChkNdefSeq_t)
                            psNdefMap->ISO15693Container.ndef_seq;
    
    uint8_t                 *p_recv_buf = 
                            (psNdefMap->SendRecvBuf + ISO15693_EXTRA_RESP_BYTE);
    uint8_t                 recv_length = (uint8_t)
                            (*psNdefMap->SendRecvLength - ISO15693_EXTRA_RESP_BYTE);
    uint8_t                 parse_index = 0;
    static uint16_t         prop_ndef_index = 0;
    uint8_t *reformatted_buf = (uint8_t*) phOsalNfc_GetMemory(ps_iso_15693_con->max_data_size);

    if (0 == ps_iso_15693_con->current_block)
    {
        /* Check CC byte */
        result = phFriNfc_ISO15693_H_CheckCCBytes (psNdefMap);
        parse_index = (uint8_t)(parse_index + recv_length);
    }
    else if (1 == ps_iso_15693_con->current_block &&
            (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR))
    {
        
        uint8_t reformatted_size = phFriNfc_ISO15693_Reformat_Pageread_Buffer(p_recv_buf, recv_length,
                reformatted_buf, ps_iso_15693_con->max_data_size);
        // Skip initial CC bytes
        p_recv_buf = reformatted_buf + (ps_iso_15693_con->current_block * ISO15693_BYTES_PER_BLOCK);
        recv_length = reformatted_size - (ps_iso_15693_con->current_block * ISO15693_BYTES_PER_BLOCK);
    }
    else
    {
        /* Propreitary TLVs VALUE can end in between a block, 
            so when that block is read, update the parse_index 
            with byte address value */
        if (ISO15693_PROP_TLV_V == e_chk_ndef_seq)
        {
            parse_index = ps_iso_15693_con->ndef_tlv_type_byte;
            e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
        }
    }

    while ((parse_index < recv_length) 
            && (NFCSTATUS_SUCCESS == result) 
            && (ISO15693_NDEF_TLV_V != e_chk_ndef_seq))
    {
        /* Parse 
            1. till the received length of the block 
            2. till there is no error during parse 
            3. till LENGTH field of NDEF TLV is found  
        */
        switch (e_chk_ndef_seq)
        {
            case ISO15693_NDEF_TLV_T:
            {
                /* Expected value is 0x03 TYPE identifier 
                    of the NDEF TLV */
                prop_ndef_index = 0;
                switch (*(p_recv_buf + parse_index))
                {
                    case ISO15693_NDEF_TLV_TYPE_ID:
                    {
                        /* Update the data structure with the byte address and 
                        the block number */
                        ps_iso_15693_con->ndef_tlv_type_byte = parse_index;
                        ps_iso_15693_con->ndef_tlv_type_blk = 
                                            ps_iso_15693_con->current_block;
                        e_chk_ndef_seq = ISO15693_NDEF_TLV_L;

                        break;
                    }

                    case ISO15693_NULL_TLV_ID:
                    {
                        /* Dont do any thing, go to next byte */
                        break;
                    }
                    
                    case ISO15693_PROP_TLV_ID:
                    {
                        /* Move the sequence to find the length 
                            of the proprietary TLV */
                        e_chk_ndef_seq = ISO15693_PROP_TLV_L;
                        break;
                    }

                    default:
                    {
                        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                            NFCSTATUS_NO_NDEF_SUPPORT);
                        break;
                    }
                } /* switch (*(p_recv_buf + parse_index)) */
                break;
            }

            case ISO15693_PROP_TLV_L:
            {
                /* Length field of the proprietary TLV */
                switch (prop_ndef_index)
                {
                    /* Length field can have 1 or 3 bytes depending 
                        on the data size, so check for each index byte */
                    case 0:
                    {
                        /* 1st index of the length field of the TLV */
                        if (0 == *(p_recv_buf + parse_index))
                        {
                            /* LENGTH is 0, not possible, so error */
                            result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                                NFCSTATUS_NO_NDEF_SUPPORT);
                            e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
                        }
                        else 
                        {
                            if (ISO15693_THREE_BYTE_LENGTH_ID == 
                                *(p_recv_buf + parse_index))
                            {
                                /* 3 byte LENGTH field identified, so increment the 
                                index, so next time 2nd byte is parsed */
                                prop_ndef_index = (uint8_t)(prop_ndef_index + 1);
                            }
                            else
                            {
                                /* 1 byte LENGTH field identified, so "static" 
                                index is set to 0 and actual ndef size is 
                                copied to the data structure
                                */
                                ps_iso_15693_con->actual_ndef_size = 
                                                    *(p_recv_buf + parse_index);
                                e_chk_ndef_seq = ISO15693_PROP_TLV_V;
                                prop_ndef_index = 0;
                            }
                        }
                        break;
                    }

                    case 1:
                    {
                        /* 2nd index of the LENGTH field that is MSB of the length, 
                        so the length is left shifted by 8 */
                        ps_iso_15693_con->actual_ndef_size = (uint16_t)
                                        (*(p_recv_buf + parse_index) << 
                                        ISO15693_BTYE_SHIFT);
                        prop_ndef_index = (uint8_t)(prop_ndef_index + 1);
                        break;
                    }

                    case 2:
                    {
                        /* 3rd index of the LENGTH field that is LSB of the length, 
                        so the length ORed with the previously stored size */
                        ps_iso_15693_con->actual_ndef_size = (uint16_t)
                                        (ps_iso_15693_con->actual_ndef_size | 
                                        *(p_recv_buf + parse_index));

                        e_chk_ndef_seq = ISO15693_PROP_TLV_V;
                        prop_ndef_index = 0;
                        break;
                    }

                    default:
                    {
                        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                            NFCSTATUS_INVALID_DEVICE_REQUEST);
                        break;
                    }
                } /* switch (prop_ndef_index) */

                if ((ISO15693_PROP_TLV_V == e_chk_ndef_seq)
                    && (ISO15693_GET_REMAINING_SIZE(ps_iso_15693_con->max_data_size, 
                        ps_iso_15693_con->current_block, parse_index) 
                        <= ps_iso_15693_con->actual_ndef_size))
                {
                    /* Check for the length field value has not exceeded the card size, 
                    if size is exceeded or then return error */
                    e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
                    result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                        NFCSTATUS_NO_NDEF_SUPPORT);
                }
                else
                {
                    uint16_t            prop_byte_addr = 0;

                    /* skip the proprietary TLVs value field */
                    prop_byte_addr = (uint16_t)
                        ((ps_iso_15693_con->current_block * ISO15693_BYTES_PER_BLOCK) + 
                        parse_index + ps_iso_15693_con->actual_ndef_size);

                    ps_iso_15693_con->ndef_tlv_type_byte = (uint8_t)(prop_byte_addr % 
                                                        ISO15693_BYTES_PER_BLOCK);
                    ps_iso_15693_con->ndef_tlv_type_blk = (uint16_t)(prop_byte_addr / 
                                                        ISO15693_BYTES_PER_BLOCK);
                    if (parse_index + ps_iso_15693_con->actual_ndef_size >= 
                        recv_length)
                    {
                        parse_index = (uint8_t)recv_length;
                    }
                    else
                    {
                        parse_index = (uint8_t)(parse_index + 
                                        ps_iso_15693_con->actual_ndef_size);
                    }

                }
                break;
            } /* case ISO15693_PROP_TLV_L: */

            case ISO15693_PROP_TLV_V:
            {
                uint8_t         remaining_length = (uint8_t)(recv_length - 
                                                    parse_index);

                if ((ps_iso_15693_con->actual_ndef_size - prop_ndef_index) 
                    > remaining_length)
                {
                    parse_index = (uint8_t)(parse_index + remaining_length);
                    prop_ndef_index = (uint8_t)(prop_ndef_index + remaining_length);
                }
                else if ((ps_iso_15693_con->actual_ndef_size - prop_ndef_index) 
                    == remaining_length)
                {
                    parse_index = (uint8_t)(parse_index + remaining_length);
                    e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
                    prop_ndef_index = 0;
                }
                else
                {
                    parse_index = (uint8_t)(parse_index + 
                                            (ps_iso_15693_con->actual_ndef_size - 
                                            prop_ndef_index)); 
                    e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
                    prop_ndef_index = 0;
                }
                break;
            } /* case ISO15693_PROP_TLV_V: */

            case ISO15693_NDEF_TLV_L:
            {
                /* Length field of the NDEF TLV */
                switch (prop_ndef_index)
                {
                    /* Length field can have 1 or 3 bytes depending 
                        on the data size, so check for each index byte */
                    case 0:
                    {
                        /* 1st index of the length field of the TLV */
                        if (0 == *(p_recv_buf + parse_index))
                        {
                            /* LENGTH is 0, card is in INITILIASED STATE */
                            e_chk_ndef_seq = ISO15693_NDEF_TLV_V;
                            ps_iso_15693_con->actual_ndef_size = 0;
                        }
                        else 
                        {
                            prop_ndef_index = (uint8_t)(prop_ndef_index + 1);

                            if (ISO15693_THREE_BYTE_LENGTH_ID == 
                                *(p_recv_buf + parse_index))
                            {
                                /* At present no CARD supports more than 255 bytes, 
                                so error is returned */
                                prop_ndef_index = (uint8_t)(prop_ndef_index + 1);
                                result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                                    NFCSTATUS_NO_NDEF_SUPPORT);
                                prop_ndef_index = 0;
                            }
                            else
                            {
                                /* 1 byte LENGTH field identified, so "static" 
                                index is set to 0 and actual ndef size is 
                                copied to the data structure
                                */
                                ps_iso_15693_con->actual_ndef_size = 
                                                    *(p_recv_buf + parse_index);
                                /* next values are the DATA field of the NDEF TLV */
                                e_chk_ndef_seq = ISO15693_NDEF_TLV_V;
                                prop_ndef_index = 0;
                            }
                        }
                        break;
                    }

                    case 1:
                    {
                        /* 2nd index of the LENGTH field that is MSB of the length, 
                        so the length is left shifted by 8 */
                        ps_iso_15693_con->actual_ndef_size = (uint16_t)
                            (*(p_recv_buf + parse_index) << 
                            ISO15693_BTYE_SHIFT);
                        prop_ndef_index = (uint8_t)(prop_ndef_index + 1);
                        break;
                    }

                    case 2:
                    {
                        /* 3rd index of the LENGTH field that is LSB of the length, 
                        so the length ORed with the previously stored size */
                        ps_iso_15693_con->actual_ndef_size = (uint16_t)
                            (ps_iso_15693_con->actual_ndef_size | 
                            *(p_recv_buf + parse_index));

                        e_chk_ndef_seq = ISO15693_NDEF_TLV_V;
                        prop_ndef_index = 0;
                        break;
                    }

                    default:
                    {
                        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                    NFCSTATUS_INVALID_DEVICE_REQUEST);
                        break;
                    }
                } /* switch (prop_ndef_index) */
                
                if ((ISO15693_NDEF_TLV_V == e_chk_ndef_seq)
                    && (ISO15693_GET_REMAINING_SIZE(ps_iso_15693_con->max_data_size, 
                        /* parse_index + 1 is done because the data starts from the next index. 
                        "MOD" operation is used to know that parse_index > 
                        ISO15693_BYTES_PER_BLOCK, then block shall be incremented 
                        */
                        (((parse_index + 1) % ISO15693_BYTES_PER_BLOCK) ?  
                        ps_iso_15693_con->current_block : 
                        ps_iso_15693_con->current_block + 1), ((parse_index + 1) % 
                        ISO15693_BYTES_PER_BLOCK)) 
                        < ps_iso_15693_con->actual_ndef_size))
                {
                    /* Check for the length field value has not exceeded the card size */
                    e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
                    result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                        NFCSTATUS_NO_NDEF_SUPPORT);
                }
                else
                {
                    psNdefMap->CardState = (uint8_t)
                                    ((PH_NDEFMAP_CARD_STATE_READ_ONLY 
                                    == psNdefMap->CardState) ? 
                                    PH_NDEFMAP_CARD_STATE_READ_ONLY :
                                    ((ps_iso_15693_con->actual_ndef_size) ? 
                                    PH_NDEFMAP_CARD_STATE_READ_WRITE : 
                                    PH_NDEFMAP_CARD_STATE_INITIALIZED));
                }
                break;
            } /* case ISO15693_NDEF_TLV_L: */

            case ISO15693_NDEF_TLV_V:
            {
                break;
            }

            default:
            {
                result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                    NFCSTATUS_INVALID_DEVICE_REQUEST);
                break;
            }
        } /* switch (e_chk_ndef_seq) */
        parse_index = (uint8_t)(parse_index + 1);
    } /* while ((parse_index < recv_length) 
            && (NFCSTATUS_SUCCESS == result) 
            && (ISO15693_NDEF_TLV_V != e_chk_ndef_seq)) */
    
    if (result)
    {
        /* Error returned while parsing, so STOP read */
        e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
        prop_ndef_index = 0;
    }
    else if (ISO15693_NDEF_TLV_V != e_chk_ndef_seq)
    {
        /* READ again */
        if (ISO15693_PROP_TLV_V != e_chk_ndef_seq)
        {
            ps_iso_15693_con->current_block = (uint16_t)
                                (ps_iso_15693_con->current_block + 1);
        }
        else
        {
            /* Proprietary TLV detected, so skip the proprietary blocks */
            ps_iso_15693_con->current_block = ps_iso_15693_con->ndef_tlv_type_blk;
        }
   
        uint32_t remaining_size = ISO15693_GET_REMAINING_SIZE(ps_iso_15693_con->max_data_size,             
                                           ps_iso_15693_con->current_block, 0);
        if (remaining_size > 0)
        {
            if ((ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_MBR) ||
                (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR)) {
                result = phFriNfc_ReadRemainingInMultiple(psNdefMap, ps_iso_15693_con->current_block);
            } else  {
                result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, ISO15693_READ_COMMAND, 
                                                        NULL, 0);
            }
        }
        else
        {
            /* End of card reached, error no NDEF information found */
            e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
            prop_ndef_index = 0;
            /* Error, no size to parse */
            result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                    NFCSTATUS_NO_NDEF_SUPPORT);
        }

    }
    else
    {
        /* Successful read with proper NDEF information updated */
        prop_ndef_index = 0;
        e_chk_ndef_seq = ISO15693_NDEF_TLV_T;
        psNdefMap->CardType = (uint8_t)PH_FRINFC_NDEFMAP_ISO15693_CARD;
    }

    psNdefMap->ISO15693Container.ndef_seq = (uint8_t)e_chk_ndef_seq;

    if (reformatted_buf != NULL) {
        phOsalNfc_FreeMemory(reformatted_buf);
    }
    return result;
}

static 
void 
phFriNfc_ISO15693_H_Complete (
    phFriNfc_NdefMap_t      *psNdefMap,
    NFCSTATUS               Status)
{
    /* set the state back to the RESET_INIT state*/
    psNdefMap->State =  PH_FRINFC_NDEFMAP_STATE_RESET_INIT;

    /* set the completion routine*/
    psNdefMap->CompletionRoutine[psNdefMap->ISO15693Container.cr_index].
        CompletionRoutine (psNdefMap->CompletionRoutine->Context, Status);
}

#ifdef FRINFC_READONLY_NDEF

static
NFCSTATUS 
phFriNfc_ISO15693_H_ProcessReadOnly (
    phFriNfc_NdefMap_t      *psNdefMap)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;    
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);
    phFriNfc_eRONdefSeq_t       e_ro_ndef_seq = (phFriNfc_eRONdefSeq_t)
                                ps_iso_15693_con->ndef_seq;
    uint8_t                     *p_recv_buf = (psNdefMap->SendRecvBuf + 
                                ISO15693_EXTRA_RESP_BYTE);
    uint8_t                     recv_length = (uint8_t)(*psNdefMap->SendRecvLength - 
                                ISO15693_EXTRA_RESP_BYTE);
    uint8_t                     a_write_buf[ISO15693_BYTES_PER_BLOCK] = {0};

    switch (e_ro_ndef_seq)
    {
        case ISO15693_RD_BEFORE_WR_CC:
        {
            if (ISO15693_SINGLE_BLK_RD_RESP_LEN == recv_length)
            {
                result = phFriNfc_ISO15693_H_CheckCCBytes (psNdefMap);
                /* Check CC bytes and also the card state for READ ONLY, 
                if the card is already read only, then dont continue with 
                next operation */
                if ((PH_NDEFMAP_CARD_STATE_READ_ONLY != psNdefMap->CardState) 
                    && (!result))
                {
                    /* CC byte read successful */
                (void)memcpy ((void *)a_write_buf, (void *)p_recv_buf, 
                                sizeof (a_write_buf));

                    /* Change the read write access to read only */
                *(a_write_buf + ISO15693_RW_BTYE_INDEX) = (uint8_t)
                            (*(a_write_buf + ISO15693_RW_BTYE_INDEX) | 
                            ISO15693_CC_READ_ONLY_MASK);

                result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                    ISO15693_WRITE_COMMAND, a_write_buf, 
                                sizeof (a_write_buf));

                e_ro_ndef_seq = ISO15693_WRITE_CC;
            }
            }
            break;
        }

        case ISO15693_WRITE_CC:
        {
            /* Write to CC is successful. */
            e_ro_ndef_seq = ISO15693_LOCK_BLOCK;
            /* Start the lock block command to lock the blocks */
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                ISO15693_LOCK_BLOCK_CMD, NULL, 0);
            break;
        }

        case ISO15693_LOCK_BLOCK:
        {
            if (ps_iso_15693_con->current_block == 
                ((ps_iso_15693_con->max_data_size / ISO15693_BYTES_PER_BLOCK) - 
                1))
            {
                /* End of card reached, READ ONLY successful */
            }
            else
            {
                /* current block is incremented */
                ps_iso_15693_con->current_block = (uint16_t)
                    (ps_iso_15693_con->current_block + 1);
                /* Lock the current block */
                result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                ISO15693_LOCK_BLOCK_CMD, NULL, 0);
            }
            break;
        }

        default:
        {
            break;
        }
    }

    ps_iso_15693_con->ndef_seq = (uint8_t)e_ro_ndef_seq;
    return result;
}

#endif /* #ifdef FRINFC_READONLY_NDEF */
/************************** END static functions definition *********************/

/************************** START external functions *********************/

NFCSTATUS 
phFriNfc_ISO15693_ChkNdef (
    phFriNfc_NdefMap_t  *psNdefMap)
{
    NFCSTATUS                       result = NFCSTATUS_SUCCESS;
    phHal_sIso15693Info_t           *ps_iso_15693_info = 
                        &(psNdefMap->psRemoteDevInfo->RemoteDevInfo.Iso15693_Info);

    /* Update the previous operation with current operation. 
        This becomes the previous operation after this execution */
    psNdefMap->PrevOperation = PH_FRINFC_NDEFMAP_CHECK_OPE;
    /* Update the CR index to know from which operation completion 
        routine has to be called */
    psNdefMap->ISO15693Container.cr_index = PH_FRINFC_NDEFMAP_CR_CHK_NDEF;
    /* State update */
    psNdefMap->State = ISO15693_CHECK_NDEF;
    /* Reset the NDEF sequence */
    psNdefMap->ISO15693Container.ndef_seq = 0;
    psNdefMap->ISO15693Container.current_block = 0;
    psNdefMap->ISO15693Container.actual_ndef_size = 0;
    psNdefMap->ISO15693Container.ndef_tlv_type_blk = 0;
    psNdefMap->ISO15693Container.ndef_tlv_type_byte = 0;
    psNdefMap->ISO15693Container.store_length = 0;
    psNdefMap->ISO15693Container.remaining_size_to_read = 0;
    psNdefMap->ISO15693Container.read_capabilities = 0;

    if ((ISO15693_UIDBYTE_6_VALUE == 
        ps_iso_15693_info->Uid[ISO15693_UID_BYTE_6]) 
        && (ISO15693_UIDBYTE_7_VALUE == 
        ps_iso_15693_info->Uid[ISO15693_UID_BYTE_7]))
    {
        /* Check if the card is manufactured by NXP (6th byte 
            index of UID value = 0x04 and the 
            last byte i.e., 7th byte of UID is 0xE0, only then the card detected 
            is NDEF compliant */
    switch (ps_iso_15693_info->Uid[ISO15693_UID_BYTE_5])
    {
            /* Check for supported tags, by checking the 5th byte index of UID */
        case ISO15693_UIDBYTE_5_VALUE_SLI_X:
        {
                /* ISO 15693 card type is ICODE SLI 
                so maximum size is 112 */
            psNdefMap->ISO15693Container.max_data_size = 
                            ISO15693_SL2_S2002_ICS20;
            break;
        }

        case ISO15693_UIDBYTE_5_VALUE_SLI_X_S:
        {
                /* ISO 15693 card type is ICODE SLI/X S  
                so maximum size depends on the 4th UID byte index */
            switch (ps_iso_15693_info->Uid[ISO15693_UID_BYTE_4])
            {
                case ISO15693_UIDBYTE_4_VALUE_SLI_X_S:
                case ISO15693_UIDBYTE_4_VALUE_SLI_X_SHC:
                case ISO15693_UIDBYTE_4_VALUE_SLI_X_SY:
                {
                        /* Supported tags are with value (4th byte UID index)
                        of 0x00, 0x80 and 0x40 
                        For these cards max size is 160 bytes */
                    psNdefMap->ISO15693Container.max_data_size = 
                                    ISO15693_SL2_S5302_ICS53_ICS54;
                    break;
                }

                default:
                {
                        /* Tag not supported */
                    result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                        NFCSTATUS_INVALID_DEVICE_REQUEST);
                    break;
                }
            }
            break;
        }

        case ISO15693_UIDBYTE_5_VALUE_SLI_X_L:
        {
                /* ISO 15693 card type is ICODE SLI/X L  
                so maximum size depends on the 4th UID byte index */
            switch (ps_iso_15693_info->Uid[ISO15693_UID_BYTE_4])
            {
                case ISO15693_UIDBYTE_4_VALUE_SLI_X_L:
                case ISO15693_UIDBYTE_4_VALUE_SLI_X_LHC:
                {
                        /* Supported tags are with value (4th byte UID index)
                        of 0x00 and 0x80
                        For these cards max size is 32 bytes */
                    psNdefMap->ISO15693Container.max_data_size = 
                                    ISO15693_SL2_S5002_ICS50_ICS51;
                    break;
                }

                default:
                {
                        /* Tag not supported */
                    result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                        NFCSTATUS_INVALID_DEVICE_REQUEST);
                    break;
                }
            }
            break;
        }

        default:
        {
                /* Tag not supported */
            result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                                NFCSTATUS_INVALID_DEVICE_REQUEST);
            break;
        }
    }
    }
    else
    {
        /* Tag not supported */
        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_INVALID_DEVICE_REQUEST);
    }

    if (!result)
    {
        /* Start reading the data */
        result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, ISO15693_READ_COMMAND, 
                                                NULL, 0);
    }
    

    return result;
}

NFCSTATUS 
phFriNfc_ISO15693_RdNdef (
    phFriNfc_NdefMap_t  *psNdefMap,
    uint8_t             *pPacketData,
    uint32_t            *pPacketDataLength,
    uint8_t             Offset)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);

    /* Update the previous operation with current operation. 
        This becomes the previous operation after this execution */
    psNdefMap->PrevOperation = PH_FRINFC_NDEFMAP_READ_OPE;
    /* Update the CR index to know from which operation completion 
        routine has to be called */
    psNdefMap->ISO15693Container.cr_index = PH_FRINFC_NDEFMAP_CR_RD_NDEF;
    /* State update */
    psNdefMap->State = ISO15693_READ_NDEF;
    /* Copy user buffer to the context */
    psNdefMap->ApduBuffer = pPacketData;
    /* Copy user length to the context */
    psNdefMap->ApduBufferSize = *pPacketDataLength;
    /* Update the user memory size to a context variable */
    psNdefMap->NumOfBytesRead = pPacketDataLength;
    /* Number of bytes read from the card is zero. 
    This variable returns the number of bytes read 
    from the card. */
    *psNdefMap->NumOfBytesRead = 0;
    /* Index to know the length read */
    psNdefMap->ApduBuffIndex = 0;    
    /* Store the offset in the context */
    psNdefMap->Offset = Offset;

    if ((!ps_iso_15693_con->remaining_size_to_read) 
        && (!psNdefMap->Offset))
    {
        /* Entire data is already read from the card. 
        There is no data to give */
        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_EOF_NDEF_CONTAINER_REACHED); 
    }
    else if (0 == ps_iso_15693_con->actual_ndef_size)
    {
        /* Card is NDEF, but no data in the card. */
        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_READ_FAILED);
    }
    else if (PH_NDEFMAP_CARD_STATE_INITIALIZED == psNdefMap->CardState)
    {
        /* Card is NDEF, but no data in the card. */
        result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP, 
                            NFCSTATUS_READ_FAILED);
    }
    else if (psNdefMap->Offset)
    {
        /* BEGIN offset, so reset the remaining read size and 
        also the curretn block */
        ps_iso_15693_con->remaining_size_to_read = 
                        ps_iso_15693_con->actual_ndef_size;
        ps_iso_15693_con->current_block = 
                        ISO15693_GET_VALUE_FIELD_BLOCK_NO(
                        ps_iso_15693_con->ndef_tlv_type_blk, 
                        ps_iso_15693_con->ndef_tlv_type_byte, 
                        ps_iso_15693_con->actual_ndef_size);

        // Check capabilities
        if ((ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_MBR) ||
            (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR)) {
            result = phFriNfc_ReadRemainingInMultiple(psNdefMap, ps_iso_15693_con->current_block);
        } else  {
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, ISO15693_READ_COMMAND, 
                                                        NULL, 0);
        }
    }
    else
    {
        /* CONTINUE offset */
        if (ps_iso_15693_con->store_length > 0)
        {
            /* Previous read had extra bytes, so data is stored, so give that take 
            that data from store. If more data is required, then read remaining bytes */
            result = phFriNfc_ISO15693_H_ProcessReadNdef (psNdefMap);
        }
        else
        {
            ps_iso_15693_con->current_block = (uint16_t)
                                (ps_iso_15693_con->current_block + 1);
            result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                            ISO15693_READ_COMMAND, NULL, 0);
        }
    }

    return result;
}

static
NFCSTATUS
phFriNfc_ReadRemainingInMultiple (
    phFriNfc_NdefMap_t  *psNdefMap,
    uint32_t            startBlock) 
{
    NFCSTATUS result = NFCSTATUS_FAILED;
    phFriNfc_ISO15693Cont_t *ps_iso_15693_con = &(psNdefMap->ISO15693Container);

    uint32_t remaining_size = ISO15693_GET_REMAINING_SIZE(ps_iso_15693_con->max_data_size,             
                                           startBlock, 0);
    // Check capabilities
    if (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_MBR) {
        // Multi-page read command
        uint8_t mbread[1];
        mbread[0] = (remaining_size / ISO15693_BYTES_PER_BLOCK) - 1;
        result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, ISO15693_READ_MULTIPLE_COMMAND, 
                mbread, 1);
    } else if (ps_iso_15693_con->read_capabilities & ISO15693_CC_USE_IPR) {
        uint32_t page = 0;
        uint32_t pagesToRead = (remaining_size / ISO15693_BYTES_PER_BLOCK / 4) - 1;
        if ((remaining_size % (ISO15693_BYTES_PER_BLOCK * ISO15693_BLOCKS_PER_PAGE)) != 0) {
            pagesToRead++;
        }
        result = phFriNfc_ISO15693_H_Inventory_Page_Read (psNdefMap, ICODE_INVENTORY_PAGEREAD_COMMAND, 
                page, pagesToRead);
        // Inventory
    } else  {
        result = NFCSTATUS_FAILED;
    }
    return result;
}

NFCSTATUS 
phFriNfc_ISO15693_WrNdef (
    phFriNfc_NdefMap_t  *psNdefMap,
    uint8_t             *pPacketData,
    uint32_t            *pPacketDataLength,
    uint8_t             Offset)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;    
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);
    uint8_t                     a_write_buf[ISO15693_BYTES_PER_BLOCK] = {0};

    /* Update the previous operation with current operation. 
        This becomes the previous operation after this execution */
    psNdefMap->PrevOperation = PH_FRINFC_NDEFMAP_WRITE_OPE;
    /* Update the CR index to know from which operation completion 
        routine has to be called */
    psNdefMap->ISO15693Container.cr_index = PH_FRINFC_NDEFMAP_CR_WR_NDEF;
    /* State update */
    psNdefMap->State = ISO15693_WRITE_NDEF;
    /* Copy user buffer to the context */
    psNdefMap->ApduBuffer = pPacketData;
    /* Copy user length to the context */
    psNdefMap->ApduBufferSize = *pPacketDataLength;
    /* Update the user memory size to a context variable */
    psNdefMap->NumOfBytesRead = pPacketDataLength;
    /* Number of bytes written to the card is zero. 
    This variable returns the number of bytes written 
    to the card. */
    *psNdefMap->WrNdefPacketLength = 0;
    /* Index to know the length read */
    psNdefMap->ApduBuffIndex = 0;    
    /* Store the offset in the context */
    psNdefMap->Offset = Offset;

    /* Set the current block correctly to write the length field to 0 */
    ps_iso_15693_con->current_block = 
                        ISO15693_GET_LEN_FIELD_BLOCK_NO(
                        ps_iso_15693_con->ndef_tlv_type_blk, 
                        ps_iso_15693_con->ndef_tlv_type_byte, 
                        *pPacketDataLength);

    if (ISO15693_GET_LEN_FIELD_BYTE_NO(
                        ps_iso_15693_con->ndef_tlv_type_blk, 
                        ps_iso_15693_con->ndef_tlv_type_byte, 
                        *pPacketDataLength))
    {
        /* Check the byte address to write. If length byte address is in between or 
        is the last byte of the block, then READ before write 
        reason, write should not corrupt other data 
        */
        ps_iso_15693_con->ndef_seq = (uint8_t)ISO15693_RD_BEFORE_WR_NDEF_L_0;
        result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                ISO15693_READ_COMMAND, NULL, 0);
    }
    else
    {
        /* If length byte address is at the beginning of the block then WRITE 
        length field to 0 and as also write user DATA */
        ps_iso_15693_con->ndef_seq = (uint8_t)ISO15693_WRITE_DATA;

        /* Length is made 0x00 */
        *a_write_buf = 0x00;

        /* Write remaining data */
        (void)memcpy ((void *)(a_write_buf + 1), 
                        (void *)psNdefMap->ApduBuffer, 
                        (ISO15693_BYTES_PER_BLOCK - 1));

        /* Write data */
        result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                    ISO15693_WRITE_COMMAND, 
                                    a_write_buf, ISO15693_BYTES_PER_BLOCK);

        /* Increment the index to keep track of bytes sent for write */
        psNdefMap->ApduBuffIndex = (uint16_t)(psNdefMap->ApduBuffIndex
                                        + (ISO15693_BYTES_PER_BLOCK - 1));
    }

    return result;
}

#ifdef FRINFC_READONLY_NDEF

NFCSTATUS 
phFriNfc_ISO15693_ConvertToReadOnly (
    phFriNfc_NdefMap_t  *psNdefMap)
{
    NFCSTATUS                   result = NFCSTATUS_SUCCESS;    
    phFriNfc_ISO15693Cont_t     *ps_iso_15693_con = 
                                &(psNdefMap->ISO15693Container);

    psNdefMap->State = ISO15693_READ_ONLY_NDEF;
    /* READ CC bytes */
    ps_iso_15693_con->ndef_seq = (uint8_t)ISO15693_RD_BEFORE_WR_CC;
    ps_iso_15693_con->current_block = 0;

    result = phFriNfc_ISO15693_H_ReadWrite (psNdefMap, 
                                    ISO15693_READ_COMMAND, NULL, 0);

    return result;
}

#endif /* #ifdef FRINFC_READONLY_NDEF */


void 
phFriNfc_ISO15693_Process (
    void        *pContext,
    NFCSTATUS   Status)
{
    phFriNfc_NdefMap_t      *psNdefMap = 
                            (phFriNfc_NdefMap_t *)pContext;

    if ((NFCSTATUS_SUCCESS & PHNFCSTBLOWER) == (Status & PHNFCSTBLOWER))
    {
        switch (psNdefMap->State) 
        {
            case ISO15693_CHECK_NDEF:
            {
                /* State = CHECK NDEF in progress */
                Status = phFriNfc_ISO15693_H_ProcessCheckNdef (psNdefMap);
                break;
            }

            case ISO15693_READ_NDEF:
            {
                /* State = READ NDEF in progress */
                Status = phFriNfc_ISO15693_H_ProcessReadNdef (psNdefMap);
                break;
            }

            case ISO15693_WRITE_NDEF:
            {
                /* State = WRITE NDEF in progress */
                Status = phFriNfc_ISO15693_H_ProcessWriteNdef (psNdefMap);
                break;
            }

#ifdef FRINFC_READONLY_NDEF
            case ISO15693_READ_ONLY_NDEF:
            {
                /* State = RAD ONLY NDEF in progress */
                Status = phFriNfc_ISO15693_H_ProcessReadOnly (psNdefMap);
                break;
            }
#endif /* #ifdef FRINFC_READONLY_NDEF */

            default:
            {
                break;
            }
        }
    }

    /* Call for the Completion Routine*/
    if (NFCSTATUS_PENDING != Status)
    {
        phFriNfc_ISO15693_H_Complete(psNdefMap, Status);
    }
}

/************************** END external functions *********************/

#endif /* #ifndef PH_FRINFC_MAP_ISO15693_DISABLED */