/*
*         Copyright (c), NXP Semiconductors Gratkorn / Austria
*
*                     (C)NXP Semiconductors
*       All rights are reserved. Reproduction in whole or in part is
*      prohibited without the written consent of the copyright owner.
*  NXP reserves the right to make changes without notice at any time.
* NXP makes no warranty, expressed, implied or statutory, including but
* not limited to any implied warranty of merchantability or fitness for any
*particular purpose, or that the use will not infringe any third party patent,
* copyright or trademark. NXP must not be liable for any loss or damage
*                          arising from its use.
*/

/** \file
* Software ICode EPC/UID Component of Reader Library Framework.
* $Author$
* $Revision$ (v05.19.00)
* $Date$
*
* History:
*  CHu: Generated 8. September 2009
*
*/

#include <ph_Status.h>
#include <phTools.h>
#include <phhalHw.h>
#include <phpalEpcUid.h>
#include <ph_RefDefs.h>

#ifdef NXPBUILD__PHPAL_EPCUID_SW

#include "phpalEpcUid_Sw.h"
#include "phpalEpcUid_Sw_Int.h"

phStatus_t phpalEpcUid_Sw_Init(
                               phpalEpcUid_Sw_DataParams_t * pDataParams,
                               uint16_t wSizeOfDataParams,
                               void * pHalDataParams
                               )
{
    /* Dataparam check */
    if (sizeof(phpalEpcUid_Sw_DataParams_t) != wSizeOfDataParams)
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_DATA_PARAMS, PH_COMP_PAL_EPCUID);
    }

    /* Pointer check */
    PH_ASSERT_NULL (pDataParams);
    PH_ASSERT_NULL (pHalDataParams);

    /* init private data */
    pDataParams->wId            = PH_COMP_PAL_EPCUID | PHPAL_EPCUID_SW_ID;
    pDataParams->pHalDataParams = pHalDataParams;
    pDataParams->bUidValid      = PH_OFF;

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_ActivateCard(
                                       phpalEpcUid_Sw_DataParams_t * pDataParams,
                                       uint8_t bTagType,
                                       uint8_t bNumSlots,
                                       uint8_t * pMask,
                                       uint8_t bMaskBitLength,
                                       uint8_t bHash,
                                       uint8_t * pRxBuffer,
                                       uint8_t * pRxLength,
                                       uint8_t * pMoreCardsAvailable
                                       )
{
    phStatus_t  PH_MEMLOC_REM status;
    phStatus_t  PH_MEMLOC_REM statusTmp;
    phStatus_t  PH_MEMLOC_REM statusReturned;
    uint8_t     PH_MEMLOC_REM bNumSlotsTmp;
    uint16_t    PH_MEMLOC_REM wNumSlotsDec;
    uint8_t     PH_MEMLOC_REM bCardsPresent;

    /* reset pMoreCardsAvailable flag */
    *pMoreCardsAvailable = PH_OFF;

    /* init. return status */
    statusReturned = PH_ERR_SUCCESS;

    do
    {
        /* Reset card present state */
        bCardsPresent = PH_OFF;

        /* Start a new Response Round and scan slot 0 */
        status = phpalEpcUid_Sw_BeginRound(
            pDataParams,
            bTagType,
            bNumSlots,
            pMask,
            bMaskBitLength,
            bHash,
            pRxBuffer,
            pRxLength);

        /* Return on parameter error */
        if ((status & PH_ERR_MASK) == PH_ERR_INVALID_PARAMETER)
        {
            return status;
        }

        /* Calculate number of slots in decimal */
        wNumSlotsDec = 0x01;
        if (bNumSlots > PHPAL_EPCUID_NUMSLOTS_1)
        {
            bNumSlotsTmp = bNumSlots;
            do
            {
                bNumSlotsTmp >>= 1U;
                wNumSlotsDec <<= 1U;
            } while (0U != bNumSlotsTmp);

            wNumSlotsDec <<= 1U;
        }
        bNumSlotsTmp = bNumSlots;

        /* Do for all further requested slots */
        while (0U != (wNumSlotsDec--))
        {
            /* Multiple labels found in current slot */
            if (((status & PH_ERR_MASK) == PH_ERR_COLLISION_ERROR) ||
                ((status & PH_ERR_MASK) == PH_ERR_INTEGRITY_ERROR))
            {
                bCardsPresent = PH_ON;
                *pMoreCardsAvailable = PH_ON;

                /* Store collision error in case it can't be resolved */
                if ((statusReturned & PH_ERR_MASK) == PH_ERR_SUCCESS)
                {
                    statusReturned = status;
                }
            }
            else
            {
                /* Store timeout error if integrity / collision error has not happened */
                if ((status & PH_ERR_MASK) == PH_ERR_IO_TIMEOUT)
                {
                    if ((statusReturned & PH_ERR_MASK) == PH_ERR_SUCCESS)
                    {
                        statusReturned = status;
                    }
                }
                /* Return on success or other errors */
                else
                {
                    return status;
                }
            }

            /* Last slot -> send CloseSlotLast */
            if (wNumSlotsDec == 0U)
            {
                /* Send CloseSlot command */
                PH_CHECK_SUCCESS_FCT(statusTmp, phpalEpcUid_Sw_CloseSlot(
                    pDataParams,
                    PHPAL_EPCUID_CLOSESLOT_LAST,
                    pRxBuffer,
                    pRxLength));
            }
            else
            {
                /* Send CloseSlot command */
                status = phpalEpcUid_Sw_CloseSlot(
                    pDataParams,
                    PHPAL_EPCUID_CLOSESLOT_NONLAST,
                    pRxBuffer,
                    pRxLength);
            }
        }

        /* Increment number of slots */
        bNumSlots = (bNumSlots << 1U) | 0x01U;
    }
    while ((bCardsPresent != PH_OFF) && (bNumSlotsTmp < PHPAL_EPCUID_NUMSLOTS_512));

    /* return error */
    return statusReturned;
}

phStatus_t phpalEpcUid_Sw_BeginRound(
                                     phpalEpcUid_Sw_DataParams_t * pDataParams,
                                     uint8_t bTagType,
                                     uint8_t bNumSlots,
                                     uint8_t * pMask,
                                     uint8_t bMaskBitLength,
                                     uint8_t bHash,
                                     uint8_t * pRxBuffer,
                                     uint8_t * pRxLength
                                     )
{
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t     PH_MEMLOC_REM bCrc8;
    uint8_t     PH_MEMLOC_REM bBitCount;
    uint8_t     PH_MEMLOC_REM aCommand[PHPAL_EPCUID_IDD_LENGTH + 4U];
    uint16_t    PH_MEMLOC_REM wCommandLen = 0;
    uint16_t    PH_MEMLOC_REM wCommandBitLen = 0;
    uint8_t *   PH_MEMLOC_REM pResponse = NULL;
    uint16_t    PH_MEMLOC_REM wResponseLen = 0;

    /* Parameter check */
    switch (bNumSlots)
    {
    case PHPAL_EPCUID_NUMSLOTS_1:
    case PHPAL_EPCUID_NUMSLOTS_4:
    case PHPAL_EPCUID_NUMSLOTS_8:
    case PHPAL_EPCUID_NUMSLOTS_16:
    case PHPAL_EPCUID_NUMSLOTS_32:
    case PHPAL_EPCUID_NUMSLOTS_64:
    case PHPAL_EPCUID_NUMSLOTS_128:
    case PHPAL_EPCUID_NUMSLOTS_256:
    case PHPAL_EPCUID_NUMSLOTS_512:
        break;
    default:
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* checks for EPC */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        if (bMaskBitLength > (PHPAL_EPCUID_EPC_LENGTH << 3U))
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }

        /* Copy EPC mask */
        (void)memcpy(pDataParams->LabelInfo.bEpc, pMask, (size_t)(((uint32_t)bMaskBitLength) >> 3U));
    }
    /* checks for UID */
    else if (bTagType == PHPAL_EPCUID_LABEL_UID)
    {
        if (bMaskBitLength > (PHPAL_EPCUID_IDD_LENGTH << 3U))
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }

        /* Copy IDD mask */
        (void)memcpy(pDataParams->LabelInfo.bIdd, pMask, (((uint32_t)bMaskBitLength) >> 3U));
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* reset label data */
    pDataParams->bLabelType = bTagType;
    pDataParams->bMaskBitLength = bMaskBitLength;
    pDataParams->bUidValid = PH_OFF;

    /* Build first part of frame */
    aCommand[0] = PHPAL_EPCUID_SW_CMD_BEGINROUND;
    aCommand[1] = bMaskBitLength;

    /* Evaluate command length and copy mask */
    bBitCount = bMaskBitLength & 0x07U;
    wCommandLen = (bBitCount == 0U) ? (((uint16_t)bMaskBitLength) >> 3U) : ((((uint16_t)bMaskBitLength) >> 3U) + 1U);
    (void)memcpy(&aCommand[2], pMask, wCommandLen);
    wCommandLen = wCommandLen + 2U;

    /* append number of slots */
    if (0U != (bBitCount))
    {
        /* clear the last invalid bits of the mask */
        aCommand[wCommandLen - 1u] &= (uint8_t) (0xFFU << (8U - bBitCount));

        /* append number of slots */
        aCommand[wCommandLen - 1u] |= (uint8_t) (bNumSlots >> bBitCount);
        aCommand[wCommandLen++] = (uint8_t) (bNumSlots << (8U - bBitCount));
    }
    else
    {
        aCommand[wCommandLen++] = bNumSlots;
    }

    /* Calculate full bit-length */
    wCommandBitLen = (bBitCount == 0U) ? (wCommandLen << 3U) : (((uint16_t)(wCommandLen - 1u) << 3U) + bBitCount);

    /* Calculate CRC for Epc */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST | PH_TOOLS_CRC_OPTION_BITWISE,
            PH_TOOLS_CRC8_PRESET_EPC,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            wCommandBitLen,
            &bCrc8));
    }
    /* calculate CRC for Uid */
    else
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST | PH_TOOLS_CRC_OPTION_BITWISE,
            PH_TOOLS_CRC8_PRESET_UID,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            wCommandBitLen,
            &bCrc8));
    }

    /* Insert CRC & Hashvalue if applicable */
    if (0U != (bBitCount))
    {
        /* Insert the crc */
        aCommand[wCommandLen - 1u] |= bCrc8 >> bBitCount;
        aCommand[wCommandLen++] = bCrc8 << (8U - bBitCount);

        if (bTagType == PHPAL_EPCUID_LABEL_EPC)
        {
            /* Insert the hash value */
            aCommand[wCommandLen - 1u] |= bHash >> bBitCount;
            aCommand[wCommandLen++] = bHash << (8U - bBitCount);
        }
    }
    else
    {
        /* Insert the crc */
        aCommand[wCommandLen++] = bCrc8;

        if (bTagType == PHPAL_EPCUID_LABEL_EPC)
        {
            /* Insert the hash value */
            aCommand[wCommandLen++] = bHash;
        }
    }

    /* Set TxLastBits to bBitCount */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TXLASTBITS,
        bBitCount));

    /* set SSOF as start symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_START,
        PHHAL_HW_SYMBOL_ICODEEPCUID_SSOF)
        );

    /* set CEOF as end symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_END,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CEOF)
        );

    /* Set RxWait */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_RXWAIT_US,
        PHPAL_EPCUID_SW_BEGINROUND_DEAFTIME));

    /* Set the RxTimeout */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
        PHPAL_EPCUID_SW_TIMEOUT_ANTICOLL_US));

    /* Exchange command */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_DEFAULT,
        aCommand,
        wCommandLen,
        &pResponse,
        &wResponseLen));

    /* Parse response */
    PH_CHECK_SUCCESS_FCT(statusTmp, phpalEpcUid_Sw_ParseResponse(pDataParams, pResponse, wResponseLen));

    /* Return response */
    if (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC)
    {
        *pRxLength = PHPAL_EPCUID_EPC_LENGTH;
        (void)memcpy(pRxBuffer, pDataParams->LabelInfo.bEpc, *pRxLength);
    }
    else
    {
        *pRxLength = PHPAL_EPCUID_IDD_LENGTH;
        (void)memcpy(pRxBuffer, pDataParams->LabelInfo.bIdd, *pRxLength);
    }

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_CloseSlot(
                                    phpalEpcUid_Sw_DataParams_t * pDataParams,
                                    uint8_t bOption,
                                    uint8_t * pRxBuffer,
                                    uint8_t * pRxLength
                                    )
{
    phStatus_t  PH_MEMLOC_REM status;
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t     PH_MEMLOC_REM aCommand[1];
    uint8_t *   PH_MEMLOC_REM pResponse = NULL;
    uint16_t    PH_MEMLOC_REM wResponseLen = 0;

    /* Set closeslot timeout*/
    if (bOption == PHPAL_EPCUID_CLOSESLOT_NONLAST)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
            pDataParams->pHalDataParams,
            PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
            PHPAL_EPCUID_SW_TIMEOUT_ANTICOLL_US));
    }
    /* Set minimum timeout */
    else if (bOption == PHPAL_EPCUID_CLOSESLOT_LAST)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
            pDataParams->pHalDataParams,
            PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
            1));
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* set CS as start symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_START,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CS));

    /* no stop symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_END,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CEOF));

    /* Set RxWait */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_RXWAIT_US,
        PHPAL_EPCUID_SW_DEAFTIME_DEFAULT));

    /* Exchange command */
    status = phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_DEFAULT,
        aCommand,
        0,
        &pResponse,
        &wResponseLen);

    if (bOption == PHPAL_EPCUID_CLOSESLOT_NONLAST)
    {
        /* Return on error */
        PH_CHECK_SUCCESS(status);

        /* Parse response */
        PH_CHECK_SUCCESS_FCT(statusTmp, phpalEpcUid_Sw_ParseResponse(pDataParams, pResponse, wResponseLen));

        /* Return response */
        if (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC)
        {
            *pRxLength = PHPAL_EPCUID_EPC_LENGTH;
            (void)memcpy(pRxBuffer, pDataParams->LabelInfo.bEpc, *pRxLength);
        }
        else
        {
            *pRxLength = PHPAL_EPCUID_IDD_LENGTH;
            (void)memcpy(pRxBuffer, pDataParams->LabelInfo.bIdd, *pRxLength);
        }
    }
    else
    {
        /* Reset received length */
        if (NULL != (pRxLength))
        {
            *pRxLength = 0;
        }

        /* Timeout is the correct behaviour */
        if ((status & PH_ERR_MASK) != PH_ERR_IO_TIMEOUT)
        {
            if ((status & PH_ERR_MASK) == PH_ERR_SUCCESS)
            {
                /* if data was received return an error */
                return PH_ADD_COMPCODE_FIXED(PH_ERR_PROTOCOL_ERROR, PH_COMP_PAL_EPCUID);
            }
            else
            {
                /* Return the received error */
                return status;
            }
        }
    }

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_FixSlot(
                                  phpalEpcUid_Sw_DataParams_t * pDataParams,
                                  uint8_t bTagType,
                                  uint8_t * pMask,
                                  uint8_t bMaskBitLength
                                  )
{
    phStatus_t  PH_MEMLOC_REM status;
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t     PH_MEMLOC_REM aCommand[2];
    uint8_t *   PH_MEMLOC_REM pResponse = NULL;
    uint16_t    PH_MEMLOC_REM wResponseLen = 0;
    uint8_t     PH_MEMLOC_REM bResponseLen;
    uint16_t    PH_MEMLOC_REM wCrc16;

    /* checks for EPC */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        if (bMaskBitLength != (PHPAL_EPCUID_EPC_LENGTH << 3U))
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    /* checks for UID */
    else if (bTagType == PHPAL_EPCUID_LABEL_UID)
    {
        if (bMaskBitLength != (PHPAL_EPCUID_IDD_LENGTH << 3U))
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* Set TxWait */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TXWAIT_US,
        PHPAL_EPCUID_SW_T1_MIN_US));

    /* Execute Begin-Round with only 1 slot */
    PH_CHECK_SUCCESS_FCT(statusTmp, phpalEpcUid_Sw_BeginRound(
        pDataParams,
        bTagType,
        PHPAL_EPCUID_NUMSLOTS_1,
        pMask,
        bMaskBitLength,
        0x00,
        pDataParams->LabelInfo.bIdd,
        &bResponseLen));

    /* Calculate CRC */
    if (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc16(
            PH_TOOLS_CRC_OPTION_MSB_FIRST | PH_TOOLS_CRC_OPTION_OUPUT_INVERTED,
            PH_TOOLS_CRC16_PRESET_EPCUID,
            PH_TOOLS_CRC16_POLY_EPCUID,
            pDataParams->LabelInfo.bEpc,
            PHPAL_EPCUID_EPC_LENGTH,
            &wCrc16));
    }
    else
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc16(
            PH_TOOLS_CRC_OPTION_MSB_FIRST | PH_TOOLS_CRC_OPTION_OUPUT_INVERTED,
            PH_TOOLS_CRC16_PRESET_EPCUID,
            PH_TOOLS_CRC16_POLY_EPCUID,
            &pDataParams->LabelInfo.bIdd[PHPAL_EPCUID_EPC_LENGTH + 2U],
            PHPAL_EPCUID_IDD_LENGTH - (PHPAL_EPCUID_EPC_LENGTH + 2U),
            &wCrc16));
    }

    /* Build fix-slot command (2 crc bytes) */
    aCommand[0] = (uint8_t)(wCrc16 >> 8U);
    aCommand[1] = (uint8_t)wCrc16;

    /* set LSOF as start symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_START,
        PHHAL_HW_SYMBOL_ICODEEPCUID_LSOF));

    /* set CEOF as end symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_END,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CEOF));

    /* Set RxWait */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_RXWAIT_US,
        PHPAL_EPCUID_SW_DEAFTIME_DEFAULT));

    /* Set fix-slot timeout */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
        1));

    /* Send the command */
    status = phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_DEFAULT,
        aCommand,
        2,
        &pResponse,
        &wResponseLen);

    /* Timeout is the correct behaviour */
    if ((status & PH_ERR_MASK) != PH_ERR_IO_TIMEOUT)
    {
        if ((status & PH_ERR_MASK) == PH_ERR_SUCCESS)
        {
            /* if data was received return an error */
            return PH_ADD_COMPCODE_FIXED(PH_ERR_PROTOCOL_ERROR, PH_COMP_PAL_EPCUID);
        }
        else
        {
            /* Return the received error */
            return status;
        }
    }

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_Write(
                                phpalEpcUid_Sw_DataParams_t * pDataParams,
                                uint8_t bTagType,
                                uint8_t bBlockNo,
                                uint8_t bData
                                )
{
    phStatus_t  PH_MEMLOC_REM status;
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t     PH_MEMLOC_REM aCommand[4];
    uint8_t *   PH_MEMLOC_REM pResponse = NULL;
    uint16_t    PH_MEMLOC_REM wResponseLen = 0;

    /* checks for EPC */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        if (bBlockNo > PHPAL_EPCUID_EPC_MAXBLOCKNUMBER)
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    /* checks for UID */
    else if (bTagType == PHPAL_EPCUID_LABEL_UID)
    {
        if (bBlockNo > PHPAL_EPCUID_UID_MAXBLOCKNUMBER)
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* set SSOF as start symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_START,
        PHHAL_HW_SYMBOL_ICODEEPCUID_SSOF)
        );

    /* set CEOF as end symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_END,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CEOF)
        );

    /* Set timeout to 6.4ms */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
        PHPAL_EPCUID_SW_TIMEOUT_DEFAULT_US));

    /* Build command frame */
    aCommand[0] = PHPAL_EPCUID_SW_CMD_WRITE;
    aCommand[1] = bBlockNo;
    aCommand[2] = bData;

    /* Calculate CRC */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST,
            PH_TOOLS_CRC8_PRESET_EPC,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            3,
            &aCommand[3]));
    }
    else
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST,
            PH_TOOLS_CRC8_PRESET_UID,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            3,
            &aCommand[3]));
    }

    /* Send the command */
    status = phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_DEFAULT,
        aCommand,
        4,
        &pResponse,
        &wResponseLen);

    /* Timeout is the correct behaviour */
    if ((status & PH_ERR_MASK) != PH_ERR_IO_TIMEOUT)
    {
        if ((status & PH_ERR_MASK) == PH_ERR_SUCCESS)
        {
            /* if data was received return an error */
            return PH_ADD_COMPCODE_FIXED(PH_ERR_PROTOCOL_ERROR, PH_COMP_PAL_EPCUID);
        }
        else
        {
            /* Return the received error */
            return status;
        }
    }

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_Destroy(
                                  phpalEpcUid_Sw_DataParams_t * pDataParams,
                                  uint8_t bTagType,
                                  uint8_t * pData,
                                  uint8_t bDataLength,
                                  uint8_t * pDestroyCode
                                  )
{
    phStatus_t  PH_MEMLOC_REM status;
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t     PH_MEMLOC_REM bCrc8;
    uint8_t     PH_MEMLOC_REM aCommand[1];
    uint8_t *   PH_MEMLOC_REM pResponse = NULL;
    uint16_t    PH_MEMLOC_REM wResponseLen = 0;

    /* checks for EPC */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        if (bDataLength != PHPAL_EPCUID_EPC_LENGTH)
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    /* checks for UID */
    else if (bTagType == PHPAL_EPCUID_LABEL_UID)
    {
        if (bDataLength != PHPAL_EPCUID_IDD_LENGTH)
        {
            return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
        }
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_EPCUID);
    }

    /* set SSOF as start symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_START,
        PHHAL_HW_SYMBOL_ICODEEPCUID_SSOF)
        );

    /* set CEOF as end symbol */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_SYMBOL_END,
        PHHAL_HW_SYMBOL_ICODEEPCUID_CEOF)
        );

    /* Set timeout to 6.4ms */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_SetConfig(
        pDataParams->pHalDataParams,
        PHHAL_HW_CONFIG_TIMEOUT_VALUE_US,
        PHPAL_EPCUID_SW_TIMEOUT_DEFAULT_US));

    /* Set command code */
    aCommand[0] = PHPAL_EPCUID_SW_CMD_DESTROY;
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_BUFFER_FIRST,
        aCommand,
        1,
        NULL,
        NULL));

    /* Append EPC/IDD */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_BUFFER_CONT,
        pData,
        (uint16_t)bDataLength,
        NULL,
        NULL));

    /* Append Destroy Code */
    PH_CHECK_SUCCESS_FCT(statusTmp, phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_BUFFER_CONT,
        pDestroyCode,
        PHPAL_EPCUID_DESTROYCODE_LENGTH,
        NULL,
        NULL));

    /* Calculate CRC over command */
    if (bTagType == PHPAL_EPCUID_LABEL_EPC)
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST,
            PH_TOOLS_CRC8_PRESET_EPC,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            1,
            &bCrc8));
    }
    else
    {
        PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
            PH_TOOLS_CRC_OPTION_MSB_FIRST,
            PH_TOOLS_CRC8_PRESET_UID,
            PH_TOOLS_CRC8_POLY_EPCUID,
            aCommand,
            1,
            &bCrc8));
    }

    /* Calculate CRC over data */
    PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
        PH_TOOLS_CRC_OPTION_MSB_FIRST,
        bCrc8,
        PH_TOOLS_CRC8_POLY_EPCUID,
        pData,
        (uint16_t)bDataLength,
        &bCrc8));

    /* Calculate CRC over destroy-code */
    PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc8(
        PH_TOOLS_CRC_OPTION_MSB_FIRST,
        bCrc8,
        PH_TOOLS_CRC8_POLY_EPCUID,
        pDestroyCode,
        PHPAL_EPCUID_DESTROYCODE_LENGTH,
        &bCrc8));

    /* Append CRC and perform exchange */
    aCommand[0] = bCrc8;
    status = phhalHw_Exchange(
        pDataParams->pHalDataParams,
        PH_EXCHANGE_BUFFER_LAST,
        aCommand,
        1,
        &pResponse,
        &wResponseLen);

    /* Timeout is the correct behaviour */
    if ((status & PH_ERR_MASK) != PH_ERR_IO_TIMEOUT)
    {
        if ((status & PH_ERR_MASK) == PH_ERR_SUCCESS)
        {
            /* if data was received return an error */
            return PH_ADD_COMPCODE_FIXED(PH_ERR_PROTOCOL_ERROR, PH_COMP_PAL_EPCUID);
        }
        else
        {
            /* Return the received error */
            return status;
        }
    }

    return PH_ERR_SUCCESS;
}

phStatus_t phpalEpcUid_Sw_GetSerialNo(
                                      phpalEpcUid_Sw_DataParams_t * pDataParams,
                                      uint8_t * pUidOut,
                                      uint8_t * pLenUidOut
                                      )
{
    if (pDataParams->bUidValid != PH_OFF)
    {
        if (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC)
        {
            *pLenUidOut = PHPAL_EPCUID_EPC_LENGTH;
            (void)memcpy(pUidOut, pDataParams->LabelInfo.bEpc, *pLenUidOut);
        }
        else
        {
            *pLenUidOut = PHPAL_EPCUID_IDD_LENGTH;
            (void)memcpy(pUidOut, pDataParams->LabelInfo.bIdd, *pLenUidOut);
        }

        return PH_ERR_SUCCESS;
    }
    else
    {
        return PH_ADD_COMPCODE_FIXED(PH_ERR_USE_CONDITION, PH_COMP_PAL_EPCUID);
    }
}

phStatus_t phpalEpcUid_Sw_ParseResponse(
                                        phpalEpcUid_Sw_DataParams_t * pDataParams,
                                        uint8_t * pRxBuffer,
                                        uint16_t wRxLength
                                        )
{
    phStatus_t  PH_MEMLOC_REM statusTmp;
    uint8_t *   PH_MEMLOC_REM pEpcIddStorage = NULL;
    uint16_t    PH_MEMLOC_REM wCrc16;
    uint16_t    PH_MEMLOC_REM wLabelLenght;

    /* Response check */
    if ((
        (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC) &&
        (wRxLength != (PHPAL_EPCUID_EPC_LENGTH + 2U - (((uint16_t)pDataParams->bMaskBitLength) >> 3U)))
        ) ||
        (
        (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_UID) &&
        (wRxLength != (PHPAL_EPCUID_IDD_LENGTH + 2U - (((uint16_t)pDataParams->bMaskBitLength) >> 3U)))
        ))
    {
        pDataParams->bUidValid = PH_OFF;
        return PH_ADD_COMPCODE_FIXED(PH_ERR_PROTOCOL_ERROR, PH_COMP_PAL_EPCUID);
    }

    /* Check where to store EPC/IDD */
    if (pDataParams->bLabelType == PHPAL_EPCUID_LABEL_EPC)
    {
        /* Copy EPC */
        (void)memcpy(&pDataParams->LabelInfo.bEpc[pDataParams->bMaskBitLength >> 3U], pRxBuffer, (((uint32_t)(wRxLength)) - 2u));

        /* Set data paramters for CRC calculation */
        pEpcIddStorage = pDataParams->LabelInfo.bEpc;
        wLabelLenght = PHPAL_EPCUID_EPC_LENGTH;
    }
    else
    {
        /* Copy IDD */
        (void)memcpy(&pDataParams->LabelInfo.bIdd[pDataParams->bMaskBitLength >> 3U], pRxBuffer, (((uint32_t)(wRxLength)) - 2u));

        /* Set data paramters for CRC calculation */
        pEpcIddStorage = &pDataParams->LabelInfo.bIdd[PHPAL_EPCUID_EPC_LENGTH + 2U];
        wLabelLenght = PHPAL_EPCUID_IDD_LENGTH - (PHPAL_EPCUID_EPC_LENGTH + 2U);
    }

    /* Calculate CRC */
    PH_CHECK_SUCCESS_FCT(statusTmp, phTools_CalculateCrc16(
        PH_TOOLS_CRC_OPTION_MSB_FIRST | PH_TOOLS_CRC_OPTION_OUPUT_INVERTED,
        PH_TOOLS_CRC16_PRESET_EPCUID,
        PH_TOOLS_CRC16_POLY_EPCUID,
        pEpcIddStorage,
        wLabelLenght,
        &wCrc16));

    /* Check CRC */
    if (((uint8_t)(wCrc16 >> 8U) != pRxBuffer[wRxLength-2u]) || ((uint8_t)(wCrc16) != pRxBuffer[wRxLength-1u]))
    {
        pDataParams->bUidValid = PH_OFF;
        return PH_ADD_COMPCODE_FIXED(PH_ERR_INTEGRITY_ERROR, PH_COMP_PAL_EPCUID);
    }

    pDataParams->bUidValid = PH_ON;
    return PH_ERR_SUCCESS;
}

#endif /* NXPBUILD__PHPAL_EPCUID_SW */
