/*
 * 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  phDnldNfc.c                                                          *
* \brief Download Mgmt Interface Source for the Firmware Download.                *
*                                                                             *
*                                                                             *
* Project: NFC-FRI-1.1                                                        *
*                                                                             *
* $Date: Tue Jun 28 14:25:44 2011 $                                           *
* $Author: ing04880 $                                                         *
* $Revision: 1.33 $                                                           *
* $Aliases:  $
*                                                                             *
* =========================================================================== *
*/


/*
################################################################################
***************************** Header File Inclusion ****************************
################################################################################
*/
#include <stdlib.h>
#include <unistd.h>
#include <phNfcConfig.h>
#include <phNfcCompId.h>
#include <phNfcIoctlCode.h>
#include <phDnldNfc.h>
#include <phOsalNfc.h>
#include <phOsalNfc_Timer.h>
#include <phDal4Nfc.h>
#include <utils/Log.h>
/*
################################################################################
****************************** Macro Definitions *******************************
################################################################################
*/

#ifndef STATIC
#define STATIC static
#endif

#if defined (DNLD_SUMMARY) && !defined (DNLD_TRACE)
#define DNLD_TRACE
#endif

/* #if defined(PHDBG_INFO) && defined (PHDBG_CRITICAL_ERROR) */
#if defined(DNLD_TRACE)
extern char phOsalNfc_DbgTraceBuffer[];

#define MAX_TRACE_BUFFER    0x0410
#define Trace_buffer    phOsalNfc_DbgTraceBuffer
/* #define DNLD_PRINT( str )  phOsalNfc_DbgTrace(str) */
#define DNLD_PRINT( str )  phOsalNfc_DbgString(str)
#define DNLD_DEBUG(str, arg) \
    {                                               \
        snprintf(Trace_buffer,MAX_TRACE_BUFFER,str,arg);    \
        phOsalNfc_DbgString(Trace_buffer);              \
    }
#define DNLD_PRINT_BUFFER(msg,buf,len)              \
    {                                               \
        snprintf(Trace_buffer,MAX_TRACE_BUFFER,"\n\t %s:",msg); \
        phOsalNfc_DbgString(Trace_buffer);              \
        phOsalNfc_DbgTrace(buf,len);                \
        phOsalNfc_DbgString("\r");                  \
    }
#else
#define DNLD_PRINT( str )
#define DNLD_DEBUG(str, arg)
#define DNLD_PRINT_BUFFER(msg,buf,len)
#endif

#define DO_DELAY(period)        usleep(period)

/* delay after SW reset cmd in ms, required on uart for XTAL stability */
#define PHDNLD_DNLD_DELAY        5000
//#define PHDNLD_MAX_PACKET        0x0200U  /* Max Total Packet Size is 512 */
#define PHDNLD_MAX_PACKET        32U  /* Max Total Packet Size is 512 */
#define PHDNLD_DATA_SIZE         ((PHDNLD_MAX_PACKET)- 8U) /* 0x01F8U */
                                                     /* Max Data Size is 504 */
#define PHDNLD_MIN_PACKET        0x03U    /* Minimum Packet Size is 3*/

#define DNLD_DEFAULT_RESPONSE_TIMEOUT   0x4000U

#define NXP_FW_MIN_TX_RX_LEN     0x0AU


#if defined( NXP_FW_MAX_TX_RX_LEN ) && \
     ( NXP_FW_MAX_TX_RX_LEN > NXP_FW_MIN_TX_RX_LEN )

#define PHDNLD_FW_TX_RX_LEN   NXP_FW_MAX_TX_RX_LEN

#elif !defined( NXP_FW_MAX_TX_RX_LEN )

/* To specify the Maximum TX/RX Len */
#define NXP_FW_MAX_TX_RX_LEN   0x200
#define PHDNLD_FW_TX_RX_LEN   NXP_FW_MAX_TX_RX_LEN

#else

#define PHDNLD_FW_TX_RX_LEN   NXP_FW_MIN_TX_RX_LEN

#endif

#define PHDNLD_FRAME_LEN_SIZE    0x02U
#define PHDNLD_ADDR_SIZE         0x03U
#define PHDNLD_DATA_LEN_SIZE     0x02U
#define PHDNLD_FRAME_DATA_OFFSET 0x03U

#define DNLD_SM_UNLOCK_MASK      0x01U
#define DNLD_TRIM_MASK           0x02U
#define DNLD_RESET_MASK          0x04U
#define DNLD_VERIFY_MASK         0x08U
#define DNLD_CRITICAL_MASK       0x10U


#define NXP_NFC_IMAG_FW_MAX      0x05U

#define PHDNLD_FW_PATCH_SEC      0x5FU

#define PHDNLD_PAGE_SIZE         0x80U    /* Page Size Configured for 64 Bytes */

#define FW_MAX_SECTION           0x15U    /* Max Number of Sections */

#define DNLD_CRC16_SIZE			 0x02U

#define DNLD_CRC32_SIZE			 0x04U

#define DNLD_CFG_PG_ADDR         0x00008000U
#define DNLD_FW_CODE_ADDR        0x00800000U
#define DNLD_PATCH_CODE_ADDR     0x00018800U
#define DNLD_PATCH_TABLE_ADDR    0x00008200U


/* Raw Command to pass the Data in Download Mode */
#define PHDNLD_CMD_RAW                  0x00U
/* Command to Reset the Device in Download Mode */
#define PHDNLD_CMD_RESET                0x01U
/* Command to Read from the Address specified in Download Mode */
#define PHDNLD_CMD_READ                 0x07U
#define PHDNLD_CMD_READ_LEN             0x0005U
/* Command to write to the Address specified in Download Mode */
#define PHDNLD_CMD_WRITE                0x08U
#define PHDNLD_CMD_SEC_WRITE            0x0CU
#define PHDNLD_CMD_WRITE_MIN_LEN        0x0005U
#define PHDNLD_CMD_WRITE_MAX_LEN        PHDNLD_DATA_SIZE
/* Command to verify the data written */
#define PHDNLD_CMD_CHECK                0x06U
#define PHDNLD_CMD_CHECK_LEN            0x0007U

/* Command to Lock the  */
#define PHDNLD_CMD_LOCK                 0x40U
#define PHDNLD_CMD_LOCK_LEN             0x0002U


/* Command to set the Host Interface properties */
#define PHDNLD_CMD_SET_HIF              0x09U

/* Command to Activate the Patches Updated  */
#define PHDNLD_CMD_ACTIVATE_PATCH       0x0AU

/* Command to verify the Integrity of the data written */
#define PHDNLD_CMD_CHECK_INTEGRITY      0x0BU

/* Command to verify the Integrity of the data written */
#define PHDNLD_CMD_ENCAPSULATE          0x0DU

#define CHECK_INTEGRITY_RESP_CRC16_LEN  0x03U
#define CHECK_INTEGRITY_RESP_CRC32_LEN  0x05U
#define CHECK_INTEGRITY_RESP_COMP_LEN   0x10U


/* Success Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_SUCCESS             0x00U
/* Timeout Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_TIMEOUT             0x01U
/* CRC Error Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_CRC_ERROR           0x02U
/* Access Denied Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_ACCESS_DENIED       0x08U
/* PROTOCOL Error Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_PROTOCOL_ERROR      0x0BU
/* Invalid parameter Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_INVALID_PARAMETER   0x11U
/* Command Not Supported Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_CMD_NOT_SUPPORTED   0x13U
/* Length parameter error Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_INVALID_LENGTH      0x18U
/* Checksum Error Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_CHKSUM_ERROR        0x19U
/* Version already uptodate Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_VERSION_UPTODATE    0x1DU
/*  Memory operation error during the processing of
                 the Command Frame in the Download Mode */
#define PHDNLD_RESP_MEMORY_UPDATE_ERROR 0x20U
/*  The Chaining of the Command Frame was Successful in the Download Mode */
#define PHDNLD_RESP_CHAINING_SUCCESS    0x21U
/*  The Command is not allowed anymore in the Download Mode */
#define PHDNLD_RESP_CMD_NOT_ALLOWED     0xE0U
/*  The Error during the Chaining the Command Frame in the Download Mode */
#define PHDNLD_RESP_CHAINING_ERROR      0xE6U
/* Write Error Response to a Command Sent in the Download Mode */
#define PHDNLD_RESP_WRITE_ERROR         0x74U

#define PNDNLD_WORD_LEN                 0x04U

#define NXP_MAX_DNLD_RETRY              0x02U

#define NXP_MAX_SECTION_WRITE           0x05U

#define NXP_PATCH_VER_INDEX             0x05U


/*
################################################################################
******************** Enumeration and Structure Definition **********************
################################################################################
*/

typedef enum phDnldNfc_eSeqType{
    DNLD_SEQ_RESET                              = 0x00U,
    DNLD_SEQ_INIT,
    DNLD_SEQ_RAW,
    DNLD_SEQ_LOCK,
    DNLD_SEQ_UNLOCK,
    DNLD_SEQ_UPDATE,
    DNLD_SEQ_ROLLBACK,
    DNLD_SEQ_COMPLETE
} phDnldNfc_eSeqType_t;

typedef enum phDnldNfc_eState
{
    phDnld_Reset_State        = 0x00,
    phDnld_Unlock_State,
    phDnld_Upgrade_State,
    phDnld_Verify_State,
    phDnld_Complete_State,
    phDnld_Invalid_State
}phDnldNfc_eState_t;


typedef enum phDnldNfc_eSeq
{
    phDnld_Reset_Seq        = 0x00,
    phDnld_Activate_Patch,
    phDnld_Deactivate_Patch,
    phDnld_Update_Patch,
    phDnld_Update_Patchtable,
    phDnld_Lock_System,
    phDnld_Unlock_System,
    phDnld_Upgrade_Section,
    phDnld_Verify_Integrity,
    phDnld_Verify_Section,
    phDnld_Complete_Seq,
    phDnld_Raw_Upgrade,
    phDnld_Invalid_Seq
}phDnldNfc_eSeq_t;

typedef enum phDnldNfc_eChkCrc{
    CHK_INTEGRITY_CONFIG_PAGE_CRC     = 0x00U,
    CHK_INTEGRITY_PATCH_TABLE_CRC     = 0x01U,
    CHK_INTEGRITY_FLASH_CODE_CRC      = 0x02U,
    CHK_INTEGRITY_PATCH_CODE_CRC      = 0x03U,
    CHK_INTEGRITY_COMPLETE_CRC        = 0xFFU
} phDnldNfc_eChkCrc_t;



typedef struct hw_comp_tbl
{
   uint8_t           hw_version[3];
   uint8_t           compatibility;
}hw_comp_tbl_t;


typedef struct img_data_hdr
{
  /* Image Identification */
  uint32_t          img_id;
  /* Offset of the Data from the header */
  uint8_t           img_data_offset;
  /* Number of fimware images available in the img_data */
  uint8_t           no_of_fw_img;
  /* Fimware image Padding in the img_data */
  uint8_t           fw_img_pad[2];
 /* HW Compatiblity table for the set of the Hardwares */
  hw_comp_tbl_t     comp_tbl;
  /* This data consists of the firmware images required to download */
}img_data_hdr_t;


typedef struct fw_data_hdr
{
 /* The data offset from the firmware header.
  * Just in case if in future we require to
  * add some more information.
  */
  uint8_t            fw_hdr_len;
  /* Total size of all the sections which needs to be updated */
  uint8_t            no_of_sections;
  uint8_t            hw_comp_no;
  uint8_t            fw_patch;
  uint32_t           fw_version;
}fw_data_hdr_t;



  /* This data consists all the sections that needs to be downloaded */
typedef struct section_hdr
{
  uint8_t            section_hdr_len;
  uint8_t            section_mem_type;
  uint8_t            section_checksum;
  uint8_t            section_conf;
  uint32_t           section_address;
  uint32_t           section_length;
}section_hdr_t;

typedef struct section_info
{
   section_hdr_t *p_sec_hdr;
   uint8_t       *p_trim_data;
  /* The section data consist of the Firmware binary required
   * to be loaded to the particular address.
   */
   uint8_t       *p_sec_data;
  /* The Section checksum to verify the integrity of the section
   * data.
   */
   uint8_t       *p_sec_chksum;
   /** \internal Index used to refer and process the
    *    Firmware Section Data */
   volatile uint32_t           section_offset;

   /** \internal Section Read Sequence */
   volatile uint8_t            section_read;

   /** \internal Section Write Sequence */
   volatile uint8_t            section_write;

   /** \internal TRIM Write Sequence */
   volatile uint8_t            trim_write;

   volatile uint8_t            sec_verify_retry;

}section_info_t;


typedef struct phDnldNfc_sParam
{
    uint8_t data_addr[PHDNLD_ADDR_SIZE];
    uint8_t data_len[PHDNLD_DATA_LEN_SIZE];
    uint8_t data_packet[PHDNLD_DATA_SIZE];
}phDnldNfc_sParam_t;

typedef struct phDnldNfc_sDataHdr
{
    uint8_t frame_type;
    uint8_t frame_length[PHDNLD_FRAME_LEN_SIZE];
}phDnldNfc_sData_Hdr_t;

typedef struct phDnldNfc_sRawHdr
{
    uint8_t frame_type;
    uint8_t frame_length[PHDNLD_FRAME_LEN_SIZE];
}phDnldNfc_sRawHdr_t;

typedef struct phDnldNfc_sRawDataHdr
{
    uint8_t data_addr[PHDNLD_ADDR_SIZE];
    uint8_t data_len[PHDNLD_DATA_LEN_SIZE];
}phDnldNfc_sRawDataHdr_t;

typedef struct phDnldNfc_sChkCrc16_Resp
{
    uint8_t Chk_status;
    uint8_t Chk_Crc16[2];

}phDnldNfc_sChkCrc16_Resp_t;

typedef struct phDnldNfc_sChkCrc32_Resp
{
    uint8_t Chk_status;
    uint8_t Chk_Crc32[4];

}phDnldNfc_sChkCrc32_Resp_t;


typedef struct phDnldNfc_sChkCrcComplete
{
    phDnldNfc_sChkCrc16_Resp_t config_page;
    phDnldNfc_sChkCrc16_Resp_t patch_table;
    phDnldNfc_sChkCrc32_Resp_t flash_code;
    phDnldNfc_sChkCrc32_Resp_t patch_code;
}phDnldNfc_sChkCrcComplete_t;

typedef struct phDnldNfc_sData
{
    uint8_t frame_type;
    uint8_t frame_length[PHDNLD_FRAME_LEN_SIZE];
    union param
    {
        phDnldNfc_sParam_t data_param;
        uint8_t            response_data[PHDNLD_MAX_PACKET];
        uint8_t            cmd_param;
    }param_info;
}phDnldNfc_sData_t;

#ifdef NXP_NFC_MULTIPLE_FW

typedef struct phDnldNfc_sFwImageInfo
{
    /** \internal Data Pointer to the Firmware header section of the Firmware */
    fw_data_hdr_t               *p_fw_hdr;
    /** \internal Buffer pointer to store the Firmware Section Data */
    section_info_t              *p_fw_sec;
    /** \internal Buffer pointer to store the Firmware Raw Data */
    uint8_t                     *p_fw_raw;
}phDnldNfc_sFwImageInfo_t;

#endif /* #ifdef NXP_NFC_MULTIPLE_FW */


typedef struct phDnldNfc_TxInfo
{
    uint8_t       *transmit_frame;

    uint16_t      tx_offset;

    /** \internal Remaining amount of data to be sent */
    uint16_t      tx_len;

    uint16_t      tx_total;

    /** \internal Chain information for the data to be sent */
    uint8_t       tx_chain;

}phDnldNfc_TxInfo_t;


typedef struct phDnldNfc_RxInfo
{
    /** \internal Total length of the received buffer */
    uint16_t      rx_total;
    /** \internal Chain information of the received buffer */
    uint16_t      rx_chain;
    /** \internal Remaining Data information to be read to complete the
      * Data Information.
      */
    uint16_t      rx_remain;

    /** \internal Buffer to Send the Raw Data Frame */
    uint8_t                     raw_buffer_data[PHDNLD_MAX_PACKET
                                                    + PHDNLD_PAGE_SIZE];
}phDnldNfc_RxInfo_t;


typedef struct phDnldNfc_sContext
{
    /** \internal Structure to store the lower interface operations */
    phNfc_sLowerIF_t            lower_interface;

    phNfc_sData_t               *p_fw_version;

    /** \internal Pointer to the Hardware Reference Sturcture */
    phHal_sHwReference_t        *p_hw_ref;

    /** \internal Pointer to the upper layer notification callback function */
    pphNfcIF_Notification_CB_t  p_upper_notify;
    /** \internal Pointer to the upper layer context */
    void                        *p_upper_context;

    /** \internal Timer ID for the Download Abort */
    uint32_t                    timer_id;
    /** \internal Internal Download for the Download Abort */
    uint32_t                    dnld_timeout;
    /** \internal Data Pointer to the Image header section of the Firmware */
    img_data_hdr_t              *p_img_hdr;

#ifdef NXP_NFC_MULTIPLE_FW
    /** \internal Data Pointer to the Firmware Image Information */
    phDnldNfc_sFwImageInfo_t    *p_img_info;
#endif /* #ifdef NXP_NFC_MULTIPLE_FW */

    /** \internal Data Pointer to the Firmware header section of the Firmware */
    fw_data_hdr_t               *p_fw_hdr;
    /** \internal Buffer pointer to store the Firmware Data */
    section_info_t              *p_fw_sec;
    /** \internal Buffer pointer to store the Firmware Raw Data */
    uint8_t                     *p_fw_raw;

    /** \internal Previous Download Size */
    uint32_t                    prev_dnld_size;

    /** \internal Single Data Block to download the Firmware */
    uint8_t                     dnld_data[PHDNLD_MAX_PACKET
                                                    + PHDNLD_PAGE_SIZE];
    /** \internal Index used to refer and process the Download Data */
    volatile uint32_t           dnld_index;

    /** \internal Response Data to process the response */
    phDnldNfc_sData_t           dnld_resp;

    /** \internal Previously downloaded data stored
	  * to compare the written data */
    phNfc_sData_t               dnld_store;

    /** \internal Previously downloaded trimmed data stored  
	  * to compare the written data */
    phNfc_sData_t               trim_store;

    uint8_t                    *p_resp_buffer;

    phDnldNfc_sChkCrcComplete_t chk_integrity_crc;

    phDnldNfc_eChkCrc_t         chk_integrity_param;

#define NXP_FW_SW_VMID_TRIM
#ifdef  NXP_FW_SW_VMID_TRIM

#define NXP_FW_VMID_TRIM_CHK_ADDR   0x0000813DU
#define NXP_FW_VMID_CARD_MODE_ADDR  0x00009931U
#define NXP_FW_VMID_RD_MODE_ADDR    0x00009981U

    uint8_t                     vmid_trim_update;
#endif /* #ifdef  NXP_FW_SW_VMID_TRIM */

    uint8_t						cur_frame_info;

    uint8_t						raw_mode_upgrade;

	uint8_t						*p_patch_table_crc;

	uint8_t						*p_flash_code_crc;

	uint8_t						*p_patch_code_crc;

    uint16_t                    resp_length;

    /** \internal Current FW Section in Process */
    volatile uint8_t            section_index;

    /** \internal Previous Command sent */
    volatile uint8_t            prev_cmd;

    uint8_t                     dnld_retry;

	/** \internal Current Download State */
    volatile uint8_t            cur_dnld_state;
    /** \internal Next Download State */
    volatile uint8_t            next_dnld_state;

    /** \internal Current step in Download Sequence */
    volatile uint8_t            cur_dnld_seq;
    /** \internal Next step in Download Sequence */
    volatile uint8_t            next_dnld_seq;

    /* \internal Data Transmit information */
    phDnldNfc_TxInfo_t          tx_info;

    /* \internal Data Receive information */
    phDnldNfc_RxInfo_t          rx_info;


}phDnldNfc_sContext_t;


/*
################################################################################
******************** Global and Static Variables Definition ********************
################################################################################
*/

#ifndef NFC_TIMER_CONTEXT
static phDnldNfc_sContext_t *gpphDnldContext = NULL;
#endif

#ifdef NXP_FW_DNLD_CHECK_PHASE

#define   NXP_FW_DNLD_COMPLETE_PHASE 0x00U
#define   NXP_FW_DNLD_SYSTEM_PHASE   0x01U
#define   NXP_FW_DNLD_CFG_PHASE      0x02U
#define   NXP_FW_DNLD_DATA_PHASE     0x03U
#define   NXP_FW_DNLD_RAW_PHASE      0x04U
#define   NXP_FW_DNLD_INVALID_PHASE  0xFFU

static uint8_t  gphDnldPhase = NXP_FW_DNLD_COMPLETE_PHASE;

#endif /* #ifdef NXP_FW_DNLD_CHECK_PHASE */

/**/

/*
*************************** Static Function Declaration **************************
*/

STATIC
NFCSTATUS
phDnldNfc_Send_Command(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        uint8_t                 cmd,
                        void                    *params,
                        uint16_t                param_length
                      );

static
NFCSTATUS
phDnldNfc_Process_FW(
                        phDnldNfc_sContext_t    *psDnldContext,
                        phHal_sHwReference_t    *pHwRef
#ifdef NXP_FW_PARAM
                        ,
                        uint8_t                 *nxp_nfc_fw,
                        uint32_t                fw_length
#endif
                     );

STATIC
void
phDnldNfc_Send_Complete (
                                void                    *psContext,
                                void                    *pHwRef,
                                phNfc_sTransactionInfo_t *pInfo
                       );

STATIC
void
phDnldNfc_Receive_Complete (
                                void                    *psContext,
                                void                    *pHwRef,
                                phNfc_sTransactionInfo_t *pInfo
                                );

STATIC
NFCSTATUS
phDnldNfc_Process_Response(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                     );


static
NFCSTATUS
phDnldNfc_Resume(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                     );

static
NFCSTATUS
phDnldNfc_Resume_Write(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef
                     );

static
NFCSTATUS
phDnldNfc_Process_Write(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        section_info_t          *p_sec_info,
                        uint32_t                *p_sec_offset
                     );

static
NFCSTATUS
phDnldNfc_Sequence(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                        );

static
NFCSTATUS
phDnldNfc_Upgrade_Sequence(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                        );

STATIC
NFCSTATUS
phDnldNfc_Receive(
                        void                *psContext,
                        void                *pHwRef,
                        uint8_t             *pdata,
                        uint16_t             length
                    );


STATIC
NFCSTATUS
phDnldNfc_Send (
                    void                    *psContext,
                    void                    *pHwRef,
                    uint8_t                 *pdata,
                    uint16_t                length
                    );

STATIC
NFCSTATUS
phDnldNfc_Set_Seq(
                                phDnldNfc_sContext_t    *psDnldContext,
                                phDnldNfc_eSeqType_t    seq_type
                        );

static
void
phDnldNfc_Notify(
                    pphNfcIF_Notification_CB_t  p_upper_notify,
                    void                        *p_upper_context,
                    void                        *pHwRef,
                    uint8_t                     type,
                    void                        *pInfo
               );

STATIC
NFCSTATUS
phDnldNfc_Allocate_Resource (
                                void                **ppBuffer,
                                uint16_t            size
                            );

STATIC
void
phDnldNfc_Release_Resources (
                                phDnldNfc_sContext_t    **ppsDnldContext
                            );

STATIC
void
phDnldNfc_Release_Lower(
                    phDnldNfc_sContext_t        *psDnldContext,
                    void                        *pHwRef
               );


static
NFCSTATUS
phDnldNfc_Read(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        section_info_t          *p_sec_info
                   );

STATIC
void
phDnldNfc_Abort (
                    uint32_t abort_id
#ifdef NFC_TIMER_CONTEXT
                    , void     *dnld_cntxt
#endif
                );


#ifdef DNLD_CRC_CALC

static
void
phDnldNfc_UpdateCrc16(
    uint8_t     crcByte,
    uint16_t    *pCrc
);

STATIC
uint16_t
phDnldNfc_ComputeCrc16(
    uint8_t     *pData,
    uint16_t    length
);


/*
*************************** Function Definitions **************************
*/
#define CRC32_POLYNOMIAL     0xEDB88320L

static uint32_t CRC32Table[0x100];
 
void BuildCRCTable()
{
    unsigned long crc;
    uint8_t i = 0, j = 0;
 
    for ( i = 0; i <= 0xFF ; i++ ) 
    {
        crc = i;
        for ( j = 8 ; j> 0; j-- ) 
        {
            if ( crc & 1 )
            {
                crc = ( crc>> 1 ) ^ CRC32_POLYNOMIAL;
            }
            else
            {
                crc>>= 1;
            }
        }
        CRC32Table[ i ] = crc;
    }
}

/*
* This routine calculates the CRC for a block of data using the
* table lookup method. It accepts an original value for the crc,
* and returns the updated value.
*/
 
uint32_t CalculateCRC32( void *buffer , uint32_t count, uint32_t crc )
{
    uint8_t *p;
    uint32_t temp1;
    uint32_t temp2;
 
    p = (uint8_t *) buffer;
    while ( count-- != 0 ) {
        temp1 = ( crc>> 8 ) & 0x00FFFFFFL;
        temp2 = CRC32Table[ ( (int) crc ^ *p++ ) & 0xff ];
        crc = temp1 ^ temp2;
    }
    return( crc );
}


static
void
phDnldNfc_UpdateCrc16(
    uint8_t     crcByte,
    uint16_t    *pCrc
)
{
    crcByte = (crcByte ^ (uint8_t)((*pCrc) & 0x00FF));
    crcByte = (crcByte ^ (uint8_t)(crcByte << 4));
    *pCrc = (*pCrc >> 8) ^ ((uint16_t)crcByte << 8) ^
                ((uint16_t)crcByte << 3) ^
                ((uint16_t)crcByte >> 4);
}


STATIC
uint16_t
phDnldNfc_ComputeCrc16(
    uint8_t     *pData,
    uint16_t    length
)
{
    uint8_t     crc_byte = 0;
    uint16_t    index = 0;
    uint16_t    crc = 0;

#ifdef CRC_A
        crc = 0x6363; /* ITU-V.41 */
#else
        crc = 0xFFFF; /* ISO/IEC 13239 (formerly ISO/IEC 3309) */
#endif /* #ifdef CRC_A */

    do
    {
        crc_byte = pData[index];
        phDnldNfc_UpdateCrc16(crc_byte, &crc);
        index++;
    } while (index < length);

#ifndef INVERT_CRC
    crc = ~crc; /* ISO/IEC 13239 (formerly ISO/IEC 3309) */
#endif /* #ifndef INVERT_CRC */

/*    *pCrc1 = (uint8_t) (crc & BYTE_MASK);
      *pCrc2 = (uint8_t) ((crc >> 8) & BYTE_MASK); */
    return crc ;
}

#endif /* #ifdef DNLD_CRC_CALC */


/*!
 * \brief Allocation of the Download Interface resources.
 *
 * This function releases and frees all the resources used by Download Mode
 * Feature.
 */

STATIC
NFCSTATUS
phDnldNfc_Allocate_Resource (
                                void                **ppBuffer,
                                uint16_t            size
                            )
{
    NFCSTATUS           status = NFCSTATUS_SUCCESS;

    *ppBuffer = (void *) phOsalNfc_GetMemory(size);
    if( *ppBuffer != NULL )
    {
        (void )memset(((void *)*ppBuffer), 0,
                                    size);
    }
    else
    {
        *ppBuffer = NULL;
        status = PHNFCSTVAL(CID_NFC_DNLD,
                        NFCSTATUS_INSUFFICIENT_RESOURCES);
    }
    return status;
}


/*!
 * \brief Release of the Download Interface resources.
 *
 * This function releases and frees all the resources used by Download layer.
 */

STATIC
void
phDnldNfc_Release_Resources (
                                phDnldNfc_sContext_t    **ppsDnldContext
                            )
{

    if(NULL != (*ppsDnldContext)->p_resp_buffer)
    {
        phOsalNfc_FreeMemory((*ppsDnldContext)->p_resp_buffer);
        (*ppsDnldContext)->p_resp_buffer = NULL;
    }
    if(NULL != (*ppsDnldContext)->dnld_store.buffer)
    {
        phOsalNfc_FreeMemory((*ppsDnldContext)->dnld_store.buffer);
        (*ppsDnldContext)->dnld_store.buffer = NULL;
        (*ppsDnldContext)->dnld_store.length = 0;
    }
    if(NULL != (*ppsDnldContext)->trim_store.buffer)
    {
        phOsalNfc_FreeMemory((*ppsDnldContext)->trim_store.buffer);
        (*ppsDnldContext)->trim_store.buffer = NULL;
        (*ppsDnldContext)->trim_store.length = 0;
    }
    if(NULL != (*ppsDnldContext)->p_fw_sec)
    {
        phOsalNfc_FreeMemory((*ppsDnldContext)->p_fw_sec);
        (*ppsDnldContext)->p_fw_sec = NULL;
    }
    if ( NXP_INVALID_TIMER_ID != (*ppsDnldContext)->timer_id )
    {
        phOsalNfc_Timer_Stop((*ppsDnldContext)->timer_id );
        phOsalNfc_Timer_Delete((*ppsDnldContext)->timer_id );
        (*ppsDnldContext)->timer_id = NXP_INVALID_TIMER_ID;
    }

    phOsalNfc_FreeMemory((*ppsDnldContext));
    (*ppsDnldContext) = NULL;

    return ;
}


STATIC
void
phDnldNfc_Release_Lower(
                    phDnldNfc_sContext_t        *psDnldContext,
                    void                        *pHwRef
               )
{
    phNfc_sLowerIF_t    *plower_if =
                            &(psDnldContext->lower_interface);
    NFCSTATUS            status = NFCSTATUS_SUCCESS;

    PHNFC_UNUSED_VARIABLE(status);

    if(NULL != plower_if->release)
    {
#ifdef DNLD_LOWER_RELEASE
        status = plower_if->release((void *)plower_if->pcontext,
                                        (void *)pHwRef);
#else
        PHNFC_UNUSED_VARIABLE(pHwRef);

#endif
        (void)memset((void *)plower_if,
                                                0, sizeof(phNfc_sLowerIF_t));
        DNLD_DEBUG(" FW_DNLD: Releasing the Lower Layer Resources: Status = %02X\n"
                                                                    ,status);
    }

    return;
}



static
void
phDnldNfc_Notify(
                    pphNfcIF_Notification_CB_t  p_upper_notify,
                    void                        *p_upper_context,
                    void                        *pHwRef,
                    uint8_t                     type,
                    void                        *pInfo
               )
{
    if( ( NULL != p_upper_notify) )
    {
        /* Notify the to the Upper Layer */
        (p_upper_notify)(p_upper_context, pHwRef, type, pInfo);
    }
}


STATIC
NFCSTATUS
phDnldNfc_Set_Seq(
                                phDnldNfc_sContext_t    *psDnldContext,
                                phDnldNfc_eSeqType_t    seq_type
                        )
{
    NFCSTATUS                       status = NFCSTATUS_SUCCESS;
    static  uint8_t                 prev_temp_state = 0;
    static  uint8_t                 prev_temp_seq =
                                (uint8_t) phDnld_Activate_Patch;

    switch(seq_type)
    {
        case DNLD_SEQ_RESET:
        case DNLD_SEQ_INIT:
        {
            psDnldContext->cur_dnld_state =
                                (uint8_t) phDnld_Reset_State;
            psDnldContext->next_dnld_state =
                            (uint8_t)phDnld_Upgrade_State;
            psDnldContext->cur_dnld_seq =
                            (uint8_t)phDnld_Upgrade_Section;
            psDnldContext->next_dnld_seq =
                                psDnldContext->cur_dnld_seq;
            break;
        }
        case DNLD_SEQ_RAW:
        {
            psDnldContext->cur_dnld_state =
                                (uint8_t) phDnld_Reset_State;
            psDnldContext->next_dnld_state =
                            (uint8_t)phDnld_Upgrade_State;
            psDnldContext->cur_dnld_seq =
                            (uint8_t)phDnld_Raw_Upgrade;
            psDnldContext->next_dnld_seq =
                                psDnldContext->cur_dnld_seq;
            break;
        }
        case DNLD_SEQ_UNLOCK:
        {
            psDnldContext->cur_dnld_state =
                                (uint8_t) phDnld_Reset_State;

#ifdef NXP_FW_DNLD_CHECK_PHASE
            if( NXP_FW_DNLD_SYSTEM_PHASE < gphDnldPhase )
            {
                psDnldContext->next_dnld_state =
                                (uint8_t)phDnld_Upgrade_State;
                psDnldContext->cur_dnld_seq =
                                (uint8_t)phDnld_Upgrade_Section;
            }
            else
#endif /* NXP_FW_DNLD_CHECK_PHASE */
            {
                psDnldContext->next_dnld_state =
                                    (uint8_t) phDnld_Unlock_State;
                psDnldContext->cur_dnld_seq =
                                    (uint8_t) phDnld_Activate_Patch;
            }
            psDnldContext->next_dnld_seq =
                                psDnldContext->cur_dnld_seq;
            break;
        }
        case DNLD_SEQ_LOCK:
        {
            psDnldContext->cur_dnld_state =
                                (uint8_t) phDnld_Reset_State;
            psDnldContext->next_dnld_state =
                                (uint8_t) phDnld_Reset_State;
            psDnldContext->cur_dnld_seq =
                                (uint8_t) phDnld_Lock_System;
            psDnldContext->next_dnld_seq =
                                psDnldContext->cur_dnld_seq;
            break;
        }
        case DNLD_SEQ_UPDATE:
        {
            prev_temp_state = (uint8_t) psDnldContext->cur_dnld_state;
            psDnldContext->cur_dnld_state =
                        psDnldContext->next_dnld_state;
            /* psDnldContext->next_dnld_state =
                        (uint8_t)phDnld_Invalid_State ; */
            prev_temp_seq = (uint8_t) psDnldContext->cur_dnld_seq;
            psDnldContext->cur_dnld_seq =
                        psDnldContext->next_dnld_seq;
            break;
        }
        case DNLD_SEQ_ROLLBACK:
        {
            psDnldContext->cur_dnld_seq = (uint8_t)  prev_temp_seq;
            psDnldContext->next_dnld_seq =
                        (uint8_t)phDnld_Invalid_Seq ;
            prev_temp_seq = 0;

            psDnldContext->cur_dnld_state = (uint8_t)  prev_temp_state;
            /* psDnldContext->next_dnld_state =
                        (uint8_t)phDnld_Invalid_State ; */
            prev_temp_state = 0;
            break;
        }
        case DNLD_SEQ_COMPLETE:
        {
            psDnldContext->cur_dnld_state =
                                (uint8_t) phDnld_Reset_State;
            psDnldContext->next_dnld_state =
                                (uint8_t) phDnld_Verify_State;
            psDnldContext->cur_dnld_seq =
                                (uint8_t) phDnld_Verify_Integrity;
            psDnldContext->next_dnld_seq =
                                psDnldContext->cur_dnld_seq ;
            break;
        }
        default:
        {
            break;
        }
    }

    return status;
}



/*!
 * \brief Sends the data the corresponding peripheral device.
 *
 * This function sends the Download data to the connected NFC Pheripheral device
 */


 STATIC
 NFCSTATUS
 phDnldNfc_Send (
                      void                  *psContext,
                      void                  *pHwRef,
                      uint8_t               *pdata,
                      uint16_t              length
                     )
{
    phDnldNfc_sContext_t    *psDnldContext= (phDnldNfc_sContext_t  *)psContext;
    NFCSTATUS               status = NFCSTATUS_SUCCESS;

    phNfc_sLowerIF_t        *plower_if = &(psDnldContext->lower_interface);

    if( (NULL != plower_if)
        && (NULL != plower_if->send)
      )
    {
#ifndef DNLD_SUMMARY
        DNLD_PRINT_BUFFER("Send Buffer",pdata,length);
#endif
        status = plower_if->send((void *)plower_if->pcontext,
                                (void *)pHwRef, pdata, length);

#if defined(FW_DOWNLOAD_TIMER) && \
                (FW_DOWNLOAD_TIMER == 2)
    if (
         (NFCSTATUS_PENDING == status)
        && ( NXP_INVALID_TIMER_ID != psDnldContext->timer_id )
       )
    {
        psDnldContext->dnld_timeout = NXP_DNLD_COMPLETE_TIMEOUT;

        if ( psDnldContext->dnld_timeout
                        <   DNLD_DEFAULT_RESPONSE_TIMEOUT)
        {
            psDnldContext->dnld_timeout
                            = DNLD_DEFAULT_RESPONSE_TIMEOUT;
        }
        /* Start the Download Timer */
        phOsalNfc_Timer_Start( psDnldContext->timer_id,
                psDnldContext->dnld_timeout,
                (ppCallBck_t) phDnldNfc_Abort
#ifdef NFC_TIMER_CONTEXT
                , (void *) psDnldContext
#endif
                );

        DNLD_DEBUG(" DNLD : Timer %X Started ", psDnldContext->timer_id);
        DNLD_DEBUG(" \t\t With %U Timeout \n", psDnldContext->dnld_timeout);
    }

#endif /* (NXP_NFC_DNLD_TIMER == 1) */
    }

    return status;
}


/*!
 * \brief Receives the Download Mode Response from the corresponding peripheral device.
 *
 * This function receives the Download Command Response to the connected NFC
 * Pheripheral device.
 */

STATIC
NFCSTATUS
phDnldNfc_Receive(
                        void                *psContext,
                        void                *pHwRef,
                        uint8_t             *pdata,
                        uint16_t             length
                    )
{
    phDnldNfc_sContext_t    *psDnldContext= (phDnldNfc_sContext_t  *)psContext;
    phNfc_sLowerIF_t *plower_if = NULL ;
    NFCSTATUS         status = NFCSTATUS_SUCCESS;

    if(NULL == psDnldContext )
    {
        status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        plower_if = &(psDnldContext->lower_interface);

        if( (NULL != plower_if)
            && (NULL != plower_if->receive)
          )
        {
            status = plower_if->receive((void *)plower_if->pcontext,
                                    (void *)pHwRef, pdata, length);
        }
    }
    return status;
}


static
NFCSTATUS
phDnldNfc_Read(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        section_info_t          *p_sec_info
                   )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;
    phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;
    phDnldNfc_sParam_t      *p_data_param =
                        &p_dnld_data->param_info.data_param;
    uint32_t        read_addr = (p_sec_info->p_sec_hdr->section_address
                                            + p_sec_info->section_offset);
    static unsigned sec_type = 0;
    uint8_t         i = 0;
    uint16_t        read_size = 0 ;

    sec_type = (unsigned int)p_sec_info->p_sec_hdr->section_mem_type;

    if( ( FALSE ==  p_sec_info->section_read )
    && ((sec_type & DNLD_TRIM_MASK))
    && (FALSE == p_sec_info->trim_write) )
    {
        read_size = (uint16_t) p_sec_info->p_sec_hdr->section_length;
        DNLD_DEBUG(" FW_DNLD: Section Read  = %X \n", read_size);
    }
    else
    {
        if (( FALSE ==  p_sec_info->section_read )
            && ((sec_type & DNLD_VERIFY_MASK))
            )
        {
            read_size = (uint16_t)(psDnldContext->prev_dnld_size );
            DNLD_DEBUG(" FW_DNLD: Section Read  = %X \n", read_size);
        }
        else if( ( TRUE ==  p_sec_info->section_read )
            && ( TRUE ==  p_sec_info->section_write )
            )
        {
            /*Already Read the Data Hence Ignore the Read */
           DNLD_DEBUG(" FW_DNLD: Already Read, Read Ignored, read_size = %X \n", read_size);
        }
        else
        {
            /* Ignore the Read */
           DNLD_DEBUG(" FW_DNLD: Section Read Status = %X \n", p_sec_info->section_read);
           DNLD_DEBUG(" FW_DNLD: Section Write Status = %X \n", p_sec_info->section_write);
           DNLD_DEBUG(" FW_DNLD: No Read Required, Read_size = %X \n", read_size);
        }
    }

    if (read_size != 0)
    {

        read_size = (uint16_t)((PHDNLD_DATA_SIZE >= read_size)?
                                            read_size: PHDNLD_DATA_SIZE);

        p_dnld_data->frame_length[i] = (uint8_t)0;
        /* Update the LSB of the Data and the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)((read_addr  >>
                                 (BYTE_SIZE + BYTE_SIZE)) & BYTE_MASK);
        p_data_param->data_len[i] = (uint8_t)((read_size >>
                                    BYTE_SIZE) & BYTE_MASK);
        i++;

        p_dnld_data->frame_length[i] = (uint8_t)
                            ( PHDNLD_CMD_READ_LEN & BYTE_MASK);
        /* Update the 2nd byte of the Data and the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)((read_addr  >>
                               BYTE_SIZE) & BYTE_MASK);
        p_data_param->data_len[i] = (uint8_t) (read_size & BYTE_MASK);
        i++;

        /* Update the 3rd byte of the the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)(read_addr & BYTE_MASK);

        status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                    PHDNLD_CMD_READ, NULL , 0 );

        if ( NFCSTATUS_PENDING == status )
        {
            p_sec_info->section_read = TRUE ;
            psDnldContext->next_dnld_state =  phDnld_Upgrade_State;
            DNLD_DEBUG(" FW_DNLD: Memory Read at Address %X : ", read_addr);
            DNLD_DEBUG(" of Size %X \n", read_size);
        }

    }
    return status;
}



static
NFCSTATUS
phDnldNfc_Process_Write(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        section_info_t          *p_sec_info,
                        uint32_t                *p_sec_offset
                     )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;
    phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;
    phDnldNfc_sParam_t      *dnld_data =
                             &p_dnld_data->param_info.data_param;
    uint8_t                 *p_sm_trim_data = (uint8_t *)psDnldContext->
                                        dnld_resp.param_info.response_data;
    uint32_t                dnld_addr = 0;
#ifdef  NXP_FW_SW_VMID_TRIM
    uint32_t                trim_addr = 0;
#endif /* #ifdef NXP_FW_SW_VMID_TRIM */
    static unsigned         sec_type = 0;
    uint8_t                 i = 0;
    uint16_t                dnld_size = 0;
    int cmp_val = 0x00;


    sec_type = (unsigned int)p_sec_info->p_sec_hdr->section_mem_type;

    status = phDnldNfc_Read(psDnldContext, pHwRef, p_sec_info);
    if( NFCSTATUS_PENDING != status )
    {
        if( (TRUE == p_sec_info->trim_write)
            && (TRUE == p_sec_info->section_read)
               && ((sec_type & DNLD_VERIFY_MASK))
          )
        {
            if(NULL != psDnldContext->trim_store.buffer)
            {
                uint32_t  trim_cmp_size = psDnldContext->prev_dnld_size;

                if( p_sec_info->p_sec_hdr->section_address
                              < (DNLD_CFG_PG_ADDR + PHDNLD_PAGE_SIZE) )
                {
                    trim_cmp_size = trim_cmp_size - 2;
                }

                /* Below Comparison fails due to the checksum */
                 cmp_val = phOsalNfc_MemCompare(
                                psDnldContext->trim_store.buffer,
                                  &psDnldContext->dnld_resp.
                                       param_info.response_data[0]
                                          ,trim_cmp_size);
                DNLD_DEBUG(" FW_DNLD: %X Bytes Trim Write Complete ",
                                    psDnldContext->prev_dnld_size);
                DNLD_DEBUG(" Comparison Status %X\n", cmp_val);
            }
            p_sec_info->trim_write = FALSE;
            DNLD_DEBUG(" FW_DNLD: TRIMMED %X Bytes Write Complete\n", psDnldContext->prev_dnld_size);
        }
        else
        {
            if((NULL != psDnldContext->dnld_store.buffer)
                && ((sec_type & DNLD_VERIFY_MASK))
                && (TRUE == p_sec_info->section_write)
                && (TRUE == p_sec_info->section_read)
                )
            {
                cmp_val = phOsalNfc_MemCompare(
                             psDnldContext->dnld_store.buffer,
                               &psDnldContext->dnld_resp.
                                 param_info.response_data[0]
                                 ,psDnldContext->dnld_store.length);
                p_sec_info->section_read = FALSE;
                p_sec_info->section_write = FALSE;
                DNLD_DEBUG(" FW_DNLD: %X Bytes Write Complete ", 
                                    psDnldContext->dnld_store.length);
                DNLD_DEBUG(" Comparison Status %X\n", cmp_val);
            }
            else
            {
                if(( TRUE == p_sec_info->section_write)
                     && ( FALSE == p_sec_info->section_read)
                   )
                {
                  p_sec_info->section_write = FALSE;
                }
            }
            /* p_sec_info->section_read = FALSE; */
        }

        if (( 0 == psDnldContext->dnld_retry )
            && (0 == cmp_val)
            )
        {
            p_sec_info->sec_verify_retry = 0;
            p_sec_info->section_offset = p_sec_info->section_offset +
                            psDnldContext->prev_dnld_size;
            psDnldContext->prev_dnld_size = 0;
            DNLD_DEBUG(" FW_DNLD: Memory Write Retry - %X \n",
                                    psDnldContext->dnld_retry);
        }
        else
        {
            p_sec_info->sec_verify_retry++;
            DNLD_DEBUG(" FW_DNLD: Memory Verification Failed, Retry =  %X \n",
                               p_sec_info->sec_verify_retry);
        }

        if( p_sec_info->sec_verify_retry < NXP_MAX_SECTION_WRITE )
        {

            dnld_addr =  (p_sec_info->p_sec_hdr->section_address + *p_sec_offset);
            dnld_size = (uint16_t)(p_sec_info->p_sec_hdr->section_length
                                                            - *p_sec_offset);
        }
        else
        {
            status = NFCSTATUS_FAILED;
            DNLD_DEBUG(" FW_DNLD: Memory Verification - Maximum Limit, Retry =  %X \n",
                               p_sec_info->sec_verify_retry);
        }
    }


    if (dnld_size != 0)
    {

        dnld_size = (uint16_t)((PHDNLD_DATA_SIZE >= dnld_size)?
                                        dnld_size: PHDNLD_DATA_SIZE);

        /* Update the LSB of the Data and the Address Parameter*/
        dnld_data->data_addr[i] = (uint8_t)((dnld_addr  >>
                                  (BYTE_SIZE + BYTE_SIZE)) & BYTE_MASK);
        dnld_data->data_len[i] = (uint8_t)((dnld_size >> BYTE_SIZE)
                                        & BYTE_MASK);
        p_dnld_data->frame_length[i] = (uint8_t)
                    (((dnld_size + PHDNLD_CMD_WRITE_MIN_LEN) >> BYTE_SIZE)
                                        & BYTE_MASK);
        i++;
        /* Update the 2nd byte of the Data and the Address Parameter*/
        dnld_data->data_addr[i] = (uint8_t)((dnld_addr  >> BYTE_SIZE)
                                        & BYTE_MASK);
        dnld_data->data_len[i] = (uint8_t) (dnld_size & BYTE_MASK);
        p_dnld_data->frame_length[i] = (uint8_t) ((dnld_size +
                            PHDNLD_CMD_WRITE_MIN_LEN) & BYTE_MASK);
        i++;
        /* Update the 3rd byte of the the Address Parameter*/
        dnld_data->data_addr[i] = (uint8_t)(dnld_addr & BYTE_MASK);

        (void)memcpy( dnld_data->data_packet,
                    (p_sec_info->p_sec_data + *p_sec_offset), dnld_size );

        if( ((sec_type & DNLD_TRIM_MASK))
            && (p_sec_info->sec_verify_retry != 0)
            && (NULL != psDnldContext->trim_store.buffer)
            )
        {
            (void)memcpy( dnld_data->data_packet,
                        psDnldContext->trim_store.buffer, dnld_size );
        }
        else if(((sec_type & DNLD_TRIM_MASK))
            && ( TRUE ==  p_sec_info->section_read )
            )
        {
            for(i = 0; i < *(p_sec_info->p_trim_data);i++)
            {

#ifdef  NXP_FW_SW_VMID_TRIM

/*
if(bit 0 of 0x813D is equal to 1) then
   
   Do not overwrite 0x9931 / 0x9981 during download

else

   @0x9931 = 0x79 // card Mode
   @0x9981 = 0x79 // Reader Mode
*/
                trim_addr = p_sec_info->p_sec_hdr->section_address 
                                    + p_sec_info->p_trim_data[i+1];
                if (NXP_FW_VMID_TRIM_CHK_ADDR == trim_addr)
                {
                    psDnldContext->vmid_trim_update = 
                            p_sm_trim_data[p_sec_info->p_trim_data[i+1]] ;
                }

                if((NXP_FW_VMID_CARD_MODE_ADDR == trim_addr) 
                        || (NXP_FW_VMID_RD_MODE_ADDR == trim_addr))
                {
                    if (TRUE == psDnldContext->vmid_trim_update) 
                    {
                        dnld_data->data_packet[p_sec_info->p_trim_data[i+1]] =
                                p_sm_trim_data[p_sec_info->p_trim_data[i+1]] ;
                    }
                }
                else
                     
#endif
                {
                    dnld_data->data_packet[p_sec_info->p_trim_data[i+1]] =
                            p_sm_trim_data[p_sec_info->p_trim_data[i+1]] ;
                }
            }
            if(NULL != psDnldContext->trim_store.buffer)
            {
                phOsalNfc_FreeMemory(psDnldContext->trim_store.buffer);
                psDnldContext->trim_store.buffer = NULL;
                psDnldContext->trim_store.length = 0;
            }
#if 1
            (void)
                phDnldNfc_Allocate_Resource((void **)
                              &(psDnldContext->trim_store.buffer),dnld_size);
#else
            psDnldContext->trim_store.buffer =
                                (uint8_t *) phOsalNfc_GetMemory(dnld_size);
#endif

            if(NULL != psDnldContext->trim_store.buffer)
            {
                (void )memset((void *)psDnldContext->trim_store.buffer,0,
                                            dnld_size);
                (void)memcpy( psDnldContext->trim_store.buffer,
                                dnld_data->data_packet,  dnld_size );
                psDnldContext->trim_store.length = dnld_size;
                DNLD_DEBUG(" FW_DNLD: Write with Trimming at Address %X ", dnld_addr );
                DNLD_DEBUG(" of Size %X and ", dnld_size );
                DNLD_DEBUG(" with %X Trimming Values \n", *(p_sec_info->p_trim_data) );

            }
        }
        else
        {
            if(NULL != psDnldContext->dnld_store.buffer)
            {
                phOsalNfc_FreeMemory(psDnldContext->dnld_store.buffer);
                psDnldContext->dnld_store.buffer = NULL;
                psDnldContext->dnld_store.length = 0;
            }
#if 1
            (void)
                phDnldNfc_Allocate_Resource((void **)
                              &(psDnldContext->dnld_store.buffer),dnld_size);
#else
            psDnldContext->dnld_store.buffer =
                                (uint8_t *) phOsalNfc_GetMemory(dnld_size);
#endif
            if(NULL != psDnldContext->dnld_store.buffer)
            {
                (void )memset((void *)psDnldContext->dnld_store.buffer,0,
                                            dnld_size);
                (void)memcpy( psDnldContext->dnld_store.buffer,
                                dnld_data->data_packet,  dnld_size );
                psDnldContext->dnld_store.length = dnld_size;
                DNLD_DEBUG(" FW_DNLD: Memory Write at Address %X ", dnld_addr );
                DNLD_DEBUG(" of Size %X ", dnld_size );
            }
        }

        if(PHDNLD_FW_PATCH_SEC !=  psDnldContext->p_fw_hdr->fw_patch)
        {
        status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                    PHDNLD_CMD_WRITE, NULL , 0 );
        }
        else
        {
            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                        PHDNLD_CMD_SEC_WRITE, NULL , 0 );
        }

        DNLD_DEBUG(" FW_DNLD: Memory Write Status = %X \n", status);
        if ( NFCSTATUS_PENDING == status )
        {
            psDnldContext->prev_dnld_size = dnld_size;
            cmp_val = 0x00;
            if((sec_type & DNLD_TRIM_MASK))
            {
                p_sec_info->trim_write = TRUE;
                DNLD_DEBUG(" FW_DNLD: Bytes Downloaded (Trimming Values) = %X Bytes \n",
                                                        dnld_size);
            }
            else
            {
                p_sec_info->section_write = TRUE;
                DNLD_DEBUG(" FW_DNLD: Bytes Downloaded  = %X : ",
                                        (*p_sec_offset + dnld_size));
                DNLD_DEBUG(" Bytes Remaining  = %X \n",
                        (p_sec_info->p_sec_hdr->section_length -
                                        (*p_sec_offset + dnld_size)));
            }

            p_sec_info->section_read = FALSE;
        }
    }
    return status;
}



static
NFCSTATUS
phDnldNfc_Resume_Write(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef
                     )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;
    uint8_t                 sec_index = psDnldContext->section_index;
    section_info_t         *p_sec_info = (psDnldContext->p_fw_sec + sec_index);

    while((sec_index < psDnldContext->p_fw_hdr->no_of_sections)
        && (NFCSTATUS_SUCCESS == status )
        )
    {

        status =  phDnldNfc_Process_Write(psDnldContext, pHwRef,
                p_sec_info, (uint32_t *)&(p_sec_info->section_offset));
        if (NFCSTATUS_SUCCESS == status)
        {
            unsigned sec_type = 0;
            sec_type = (unsigned int)p_sec_info->p_sec_hdr->section_mem_type;

            p_sec_info->section_offset = 0;
            p_sec_info->section_read = FALSE;
            p_sec_info->section_write = FALSE;
            p_sec_info->trim_write = FALSE;

            DNLD_DEBUG(" FW_DNLD: Section %02X Download Complete\n", sec_index);
            if((sec_type & DNLD_RESET_MASK))
            {
                DNLD_DEBUG(" FW_DNLD: Reset After Section %02X Download \n", sec_index);
                status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                            PHDNLD_CMD_RESET , NULL, 0 );
            }
            DNLD_PRINT("*******************************************\n\n");

            sec_index++;

#ifdef NXP_FW_DNLD_CHECK_PHASE
            if( p_sec_info->p_sec_hdr->section_address
                               < (DNLD_CFG_PG_ADDR + PHDNLD_PAGE_SIZE) )
            {
                gphDnldPhase = NXP_FW_DNLD_DATA_PHASE;

            }

            p_sec_info = (psDnldContext->p_fw_sec + sec_index);

            if( (sec_index < psDnldContext->p_fw_hdr->no_of_sections)
                && ( p_sec_info->p_sec_hdr->section_address
                               < (DNLD_CFG_PG_ADDR + PHDNLD_PAGE_SIZE) )
               )
            {
                 if( NXP_FW_DNLD_CFG_PHASE >= gphDnldPhase )
                 {
                    gphDnldPhase = NXP_FW_DNLD_CFG_PHASE;
                 }
                 else
                 {
                   sec_index++;
                   p_sec_info = (psDnldContext->p_fw_sec + sec_index);
                 }
            }
#else
            p_sec_info = (psDnldContext->p_fw_sec + sec_index);
#endif /* #ifdef NXP_FW_DNLD_CHECK_PHASE */

            psDnldContext->section_index = sec_index;
        /* psDnldContext->next_dnld_state = (uint8_t) phDnld_Upgrade_State; */
        }
    }
    if (NFCSTATUS_PENDING == status)
    {
        psDnldContext->next_dnld_state = (uint8_t) phDnld_Upgrade_State;
    }
    else if (NFCSTATUS_SUCCESS == status)
    {
        /* Reset the PN544 Device */
        psDnldContext->next_dnld_state = (uint8_t) phDnld_Complete_State;
    }
    else
    {

    }
    return status;
}


#define NXP_DNLD_SM_UNLOCK_ADDR         0x008002U

#if !defined (ES_HW_VER)
#define ES_HW_VER 32
#endif

#if (ES_HW_VER <= 30)
#define NXP_DNLD_PATCH_ADDR             0x01AFFFU
#else
#define NXP_DNLD_PATCH_ADDR             0x01A1E0U
#endif

#if  (ES_HW_VER <= 30)
#define NXP_DNLD_PATCH_TABLE_ADDR       0x008107U
#else
#define NXP_DNLD_PATCH_TABLE_ADDR       0x00825AU
#endif


static
NFCSTATUS
phDnldNfc_Sequence(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                        )
{
    NFCSTATUS           status = NFCSTATUS_SUCCESS;
    uint32_t            dnld_addr = 0;
    phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;
    phDnldNfc_sParam_t  *p_data_param =
                          & p_dnld_data->param_info.data_param;
    uint8_t             *p_data = NULL;
    static  uint32_t    patch_size = 0;

#if (ES_HW_VER == 32)

    static  uint8_t     patch_table[] = {0xA0, 0xA1, 0xE0, 0x80, 0xA9, 0x6C };
    static  uint8_t     patch_data[] = {0xA5, 0xD0, 0xFE, 0xA5, 0xD0, 0xFD, 0xA5,
                            0xD0, 0xFC, 0xA5, 0x02, 0x80, 0xA9, 0x75};

#elif (ES_HW_VER == 31)

    static  uint8_t     patch_table[] = {0xA0, 0xAF, 0xE0, 0x80, 0x78, 0x84 };
    static  uint8_t     patch_data[] = {0xA5, 0xD0, 0xFE, 0xA5, 0xD0, 0xFD, 0xA5,
                            0xD0, 0xFC, 0xD0, 0xE0, 0xA5, 0x02, 0x80, 0x78, 0x8D};

#elif (ES_HW_VER == 30)

    static  uint8_t     patch_table[] = {0x80, 0x91, 0x51, 0xA0, 0xAF,
                                                0xFF, 0x80, 0x91, 0x5A};
    static  uint8_t     patch_data[] = {0x22};

#endif

    static  uint8_t     unlock_data[] = {0x00, 0x00};
    static  uint8_t     lock_data[] = {0x0C, 0x00};

    uint8_t             i = 0;

    PHNFC_UNUSED_VARIABLE(pdata);
    PHNFC_UNUSED_VARIABLE(length);
    switch(psDnldContext->cur_dnld_seq)
    {
        case phDnld_Reset_Seq:
        {
            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                        PHDNLD_CMD_RESET , NULL , 0 );
            /* status = (NFCSTATUS_PENDING == status)? NFCSTATUS_SUCCESS:
                                 status; */
            DNLD_DEBUG(" FW_DNLD: Reset Seq.. Status = %X \n", status);

            break;
        }
        case phDnld_Activate_Patch:
        {
            uint8_t     patch_activate = 0x01;
            psDnldContext->next_dnld_seq =
                            (uint8_t)phDnld_Update_Patch;
#ifdef NXP_FW_DNLD_CHECK_PHASE
            gphDnldPhase = NXP_FW_DNLD_SYSTEM_PHASE;
#endif /* NXP_FW_DNLD_CHECK_PHASE */

            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                PHDNLD_CMD_ACTIVATE_PATCH , &patch_activate, sizeof(patch_activate) );
            DNLD_PRINT(" FW_DNLD: Activate the Patch Update .... \n");
            break;
        }
        case phDnld_Deactivate_Patch:
        {
            uint8_t     patch_activate = 0x00;

            psDnldContext->next_dnld_state =
                            (uint8_t)phDnld_Reset_State;

            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                PHDNLD_CMD_ACTIVATE_PATCH , &patch_activate, sizeof(patch_activate) );
            DNLD_PRINT(" FW_DNLD: Deactivate the Patch Update .... \n");
            break;
        }
        case phDnld_Update_Patch:
        {
            dnld_addr = NXP_DNLD_PATCH_ADDR;
            patch_size = sizeof(patch_data) ;
            p_data = patch_data;
            psDnldContext->next_dnld_seq =
                            (uint8_t)phDnld_Update_Patchtable;
            DNLD_PRINT(" FW_DNLD: Patch Update Seq.... \n");
            break;
        }
        case phDnld_Update_Patchtable:
        {
            dnld_addr = NXP_DNLD_PATCH_TABLE_ADDR;
            patch_size = sizeof(patch_table) ;
            p_data = patch_table;

            psDnldContext->next_dnld_state =
                            (uint8_t)phDnld_Reset_State;

            DNLD_PRINT(" FW_DNLD: Patch Table Update Seq.... \n");
            break;
        }
        case phDnld_Unlock_System:
        {
            dnld_addr = NXP_DNLD_SM_UNLOCK_ADDR;
            patch_size = sizeof(unlock_data) ;
            p_data = unlock_data;
#define NXP_FW_PATCH_DISABLE
#ifdef NXP_FW_PATCH_DISABLE
            psDnldContext->next_dnld_seq =
                            (uint8_t)phDnld_Deactivate_Patch;
#else
            psDnldContext->next_dnld_state =
                            (uint8_t)phDnld_Reset_State;
#endif

            DNLD_PRINT(" FW_DNLD: System Memory Unlock Seq.... \n");
            break;
        }
        case phDnld_Lock_System:
        {
            dnld_addr = NXP_DNLD_SM_UNLOCK_ADDR;
            patch_size = sizeof(lock_data) ;
            p_data = lock_data;
            psDnldContext->next_dnld_state =
                            (uint8_t) phDnld_Reset_State;

            DNLD_PRINT(" FW_DNLD: System Memory Lock Seq.... \n");
            break;
        }
        case phDnld_Upgrade_Section:
        {
            status = phDnldNfc_Resume_Write(
                        psDnldContext, pHwRef );
            break;
        }
        case phDnld_Verify_Integrity:
        {
            psDnldContext->next_dnld_state =
                            (uint8_t) phDnld_Reset_State;

            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                PHDNLD_CMD_CHECK_INTEGRITY , NULL, 0 );
            DNLD_PRINT(" FW_DNLD: System Memory Integrity Check Sequence.... \n");
            break;
        }
        case phDnld_Verify_Section:
        {
            break;
        }
        default:
        {
            break;
        }
    }

    if( NFCSTATUS_SUCCESS == status)
    {

        /* Update the LSB of the Data and the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)((dnld_addr  >>
                                            (BYTE_SIZE + BYTE_SIZE))
                                                    & BYTE_MASK);
        p_data_param->data_len[i] = (uint8_t)((patch_size >> BYTE_SIZE)
                                                    & BYTE_MASK);
        p_dnld_data->frame_length[i] = (uint8_t)
                    (((patch_size + PHDNLD_CMD_WRITE_MIN_LEN) >> BYTE_SIZE)
                                                    & BYTE_MASK);
        i++;
        /* Update the 2nd byte of the Data and the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)((dnld_addr  >> BYTE_SIZE)
                                                & BYTE_MASK);
        p_data_param->data_len[i] = (uint8_t) (patch_size & BYTE_MASK);
        p_dnld_data->frame_length[i] = (uint8_t)
                            ((patch_size + PHDNLD_CMD_WRITE_MIN_LEN)
                                                    & BYTE_MASK);
        i++;
        /* Update the 3rd byte of the the Address Parameter*/
        p_data_param->data_addr[i] = (uint8_t)(dnld_addr & BYTE_MASK);

        status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                    PHDNLD_CMD_WRITE,(void *)p_data , (uint8_t)patch_size );

        if (NFCSTATUS_PENDING != status)
        {
             status = phDnldNfc_Set_Seq(psDnldContext,
                                            DNLD_SEQ_ROLLBACK);
        }
    }
    return status;
}

#define FRAME_HEADER_LEN   0x03U


static
void
phDnldNfc_Tx_Reset(phDnldNfc_sContext_t    *psDnldContext)
{
    psDnldContext->tx_info.transmit_frame = NULL;
    psDnldContext->tx_info.tx_total = 0x00;
    psDnldContext->tx_info.tx_offset = 0x00;
    psDnldContext->tx_info.tx_len = 0x00;
    psDnldContext->tx_info.tx_chain = FALSE;
}

STATIC
bool_t
phDnldNfc_Extract_Chunks(
                       uint8_t  *frame_data,
                       uint16_t  frame_offset,
                       uint16_t  frame_length,
                       uint16_t  max_frame ,
                       uint16_t  *chunk_length
                       );


STATIC
bool_t
phDnldNfc_Extract_Chunks(
                       uint8_t  *frame_data,
                       uint16_t  frame_offset,
                       uint16_t  frame_length,
                       uint16_t  max_frame ,
                       uint16_t  *chunk_length
                       )
{
    bool_t  chunk_present = FALSE;

    if( 0 == frame_offset)
    {
        if( max_frame >= (frame_length
                - frame_offset))
        {
           *chunk_length = (frame_length - frame_offset);
        }
        else
        {
            *chunk_length = max_frame
                            - FRAME_HEADER_LEN;
            chunk_present = TRUE;
        }
    }
    else
    {
        if( max_frame >= (frame_length
                - frame_offset))
        {
           *chunk_length = (frame_length - frame_offset);
        }
        else
        {
            *chunk_length = max_frame
                            - FRAME_HEADER_LEN;
            chunk_present = TRUE;
        }
    }

    return chunk_present;
}


STATIC
NFCSTATUS
phDnldNfc_Send_Raw(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        uint8_t                 *raw_frame,
                        uint16_t                frame_offset,
                        uint16_t                frame_length
                      )
{
	NFCSTATUS status = NFCSTATUS_SUCCESS;
	phDnldNfc_sRawHdr_t *raw_frame_hdr = ( phDnldNfc_sRawHdr_t * ) raw_frame;

    switch(raw_frame_hdr->frame_type)
	{
        case PHDNLD_CMD_RESET:
        {
            break;
        }
        case PHDNLD_CMD_READ:
        {
            /* TODO: To Update the length and the buffer to receive data */
            break;
        }
        case PHDNLD_CMD_WRITE:
        {
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;

            break;
        }
        case PHDNLD_CMD_SEC_WRITE:
        {
            uint16_t    tx_length = 0x00;
            uint16_t    frame_offset =
                          psDnldContext->tx_info.tx_offset;
            uint16_t    chain =
                    psDnldContext->tx_info.tx_chain;

            chain =
            phDnldNfc_Extract_Chunks(
                         raw_frame,
                         frame_offset,
                         frame_length,
                         PHDNLD_FW_TX_RX_LEN,
                         &tx_length
                       );

            if( TRUE == chain )
            {
                status = phDnldNfc_Send_Command( psDnldContext,
                                    pHwRef, PHDNLD_CMD_ENCAPSULATE,
                                    (raw_frame + frame_offset),
                                    tx_length);
                if(NFCSTATUS_PENDING == status)
                {
                    psDnldContext->prev_cmd = raw_frame_hdr->frame_type;
                    /* TODO: Update for the Chaining */
                    psDnldContext->tx_info.tx_offset += tx_length;
                    psDnldContext->tx_info.tx_chain = chain;
                }
            }
            else if (0 != frame_offset)
            {
                status = phDnldNfc_Send_Command( psDnldContext,
                                    pHwRef, PHDNLD_CMD_ENCAPSULATE,
                                    (raw_frame + frame_offset),
                                    tx_length);
                if(NFCSTATUS_PENDING == status)
                {
                    psDnldContext->prev_cmd = raw_frame_hdr->frame_type;
                    /* TODO: Update for the Chaining */
                    psDnldContext->prev_dnld_size = frame_length;
                    phDnldNfc_Tx_Reset(psDnldContext);
                }
            }
            else
            {
                psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            }

            break;
        }
        case PHDNLD_CMD_CHECK:
        {
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            break;
        }
        case PHDNLD_CMD_SET_HIF:
        {
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            break;
        }
        case PHDNLD_CMD_ACTIVATE_PATCH:
        {
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            break;
        }
        case PHDNLD_CMD_CHECK_INTEGRITY:
        {
			uint8_t integrity_param =
				 *(raw_frame + FRAME_HEADER_LEN);
            switch(integrity_param)
            {
                case CHK_INTEGRITY_CONFIG_PAGE_CRC:
                case CHK_INTEGRITY_PATCH_TABLE_CRC:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET
                                         + CHECK_INTEGRITY_RESP_CRC16_LEN;
                    break;
                }
                case CHK_INTEGRITY_FLASH_CODE_CRC:
                case CHK_INTEGRITY_PATCH_CODE_CRC:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET
                                        +  CHECK_INTEGRITY_RESP_CRC32_LEN;
                    break;
                }
                case CHK_INTEGRITY_COMPLETE_CRC:
                default:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET
                                        +  CHECK_INTEGRITY_RESP_COMP_LEN;
                    break;
                }
            }
            break;
        }
        default:
        {
            status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FEATURE_NOT_SUPPORTED);
            break;
        }
    }

    if (NFCSTATUS_SUCCESS == status)
    {
        status = phDnldNfc_Send( psDnldContext, pHwRef ,
                            raw_frame, frame_length);

        if(NFCSTATUS_PENDING == status)
        {
            psDnldContext->prev_cmd = raw_frame_hdr->frame_type;
            /* TODO: Update for the Chaining */
            psDnldContext->prev_dnld_size = frame_length;
        }
    }

    return status;
}


static
NFCSTATUS
phDnldNfc_Raw_Write(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef
                     )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;
    uint32_t                dnld_index = psDnldContext->dnld_index;
    uint32_t                tx_length = 0;
    uint8_t                 *p_raw_sec_hdr = NULL;
    uint8_t                 dnld_flag = FALSE;
    uint8_t                 skip_frame = FALSE;

    if(NULL != psDnldContext->p_fw_raw)
    {

        if( (TRUE != psDnldContext->tx_info.tx_chain)
            && (0x00 == psDnldContext->dnld_retry)
          )
        {
            dnld_index = dnld_index + psDnldContext->prev_dnld_size;
            p_raw_sec_hdr = psDnldContext->p_fw_raw + dnld_index;
            dnld_index = dnld_index + *p_raw_sec_hdr;
        }
        else
        {
            phDnldNfc_sData_Hdr_t *p_dnld_raw = (phDnldNfc_sData_Hdr_t *)
									(psDnldContext->p_fw_raw +
                                              psDnldContext->dnld_index);

            tx_length = ((p_dnld_raw->frame_length[0] << BYTE_SIZE) |
                                    p_dnld_raw->frame_length[1]);

            tx_length = tx_length + PHDNLD_MIN_PACKET;

            status = phDnldNfc_Send_Raw( psDnldContext, pHwRef,
                            (uint8_t *)(p_dnld_raw),
                            psDnldContext->tx_info.tx_offset,
                                (uint16_t)tx_length);
        }


#define PHDNLD_MAJOR_OFFSET        0x04U
#define PHDNLD_MINOR_OFFSET        0x05U
#define PHDNLD_PHASE_OFFSET        0x06U
#define PHDNLD_FRAMETYPE_OFFSET    0x07U

#define PHDNLD_NO_OPERATION        0x00U
#define PHDNLD_NORMAL_OPERATION    0x10U
#define PHDNLD_ADVANCED_OPERATION  0x20U
#define PHDNLD_SETUP_OPERATION	   0x40U
#define PHDNLD_RECOVER_OPERATION   0x80U
#define PHDNLD_COMPLETE_OPERATION  0xF0U

#define PHDNLD_TERMINATE_TYPE      0x0EU

#define PHDNLD_MARKER_MASK         0x0FU

        while((NFCSTATUS_SUCCESS == status )
                && (FALSE == dnld_flag)
            )
       {
            phDnldNfc_sData_Hdr_t *p_dnld_raw = (phDnldNfc_sData_Hdr_t *)
												(psDnldContext->p_fw_raw + dnld_index);
            uint8_t               frame_type = *(p_raw_sec_hdr + PHDNLD_FRAMETYPE_OFFSET);

            tx_length = ((p_dnld_raw->frame_length[0] << BYTE_SIZE) |
                                    p_dnld_raw->frame_length[1]);

            tx_length = tx_length + PHDNLD_MIN_PACKET;

            skip_frame = FALSE;

            if(  (0x00 == *(p_raw_sec_hdr + PHDNLD_PHASE_OFFSET))
                    || (0xFF == *(p_raw_sec_hdr + PHDNLD_PHASE_OFFSET))
                    || !( psDnldContext->raw_mode_upgrade
                     & (frame_type & (~PHDNLD_MARKER_MASK)) )
                     )
            {
                dnld_index = dnld_index + tx_length;
                p_raw_sec_hdr = psDnldContext->p_fw_raw + dnld_index;
                dnld_index = dnld_index + *p_raw_sec_hdr;
                skip_frame = TRUE;
            }
            if (PHDNLD_TERMINATE_TYPE ==
                        (frame_type & PHDNLD_MARKER_MASK))
            {
                if(TRUE != skip_frame)
	        {
                   psDnldContext->raw_mode_upgrade = 
                       (psDnldContext->raw_mode_upgrade &
                              ~(frame_type & ~PHDNLD_MARKER_MASK));
		}

                if(PHDNLD_NO_OPERATION ==
                        psDnldContext->raw_mode_upgrade)
                {
                   dnld_flag = TRUE;
                }
            }
            else
            {

            }

            if((FALSE == skip_frame)
                && (FALSE == dnld_flag)
                )
            {
                status = phDnldNfc_Send_Raw( psDnldContext, pHwRef,
                               (uint8_t *)(p_dnld_raw),
                               psDnldContext->tx_info.tx_offset,
                                    (uint16_t)tx_length);
            }

            if( NFCSTATUS_PENDING == status )
            {
                psDnldContext->dnld_index = dnld_index;
				psDnldContext->cur_frame_info= frame_type;
            }
        }
    }

    return status;
}

static
NFCSTATUS
phDnldNfc_Upgrade_Sequence(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                        )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;

    PHNFC_UNUSED_VARIABLE(pdata);
    PHNFC_UNUSED_VARIABLE(length);

    if(phDnld_Raw_Upgrade == psDnldContext->cur_dnld_seq)
    {
       status = phDnldNfc_Raw_Write( psDnldContext, pHwRef );
    }
    else
    {
       status = phDnldNfc_Resume_Write( psDnldContext, pHwRef );
    }

    return status;
}



static
NFCSTATUS
phDnldNfc_Resume(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                     )
{
    NFCSTATUS             status = NFCSTATUS_SUCCESS;
    phDnldNfc_eState_t    dnld_next_state = (phDnldNfc_eState_t)
                                    psDnldContext->cur_dnld_state;
    phNfc_sCompletionInfo_t comp_info = {0,0,0};

    switch( dnld_next_state )
    {
        case phDnld_Reset_State:
        {
            status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                        PHDNLD_CMD_RESET , NULL, 0 );
            switch( psDnldContext->cur_dnld_seq )
            {
                case phDnld_Update_Patchtable:
                {
                    psDnldContext->next_dnld_state =
                                    (uint8_t)phDnld_Unlock_State;
                    psDnldContext->next_dnld_seq =
                                    (uint8_t)phDnld_Unlock_System;
                    break;
                }
#ifdef NXP_FW_PATCH_DISABLE
                case phDnld_Deactivate_Patch:
#else
                case phDnld_Unlock_System:
#endif
                {
                    psDnldContext->next_dnld_state =
                                   (uint8_t)phDnld_Upgrade_State;
                    psDnldContext->next_dnld_seq =
                                    (uint8_t)phDnld_Upgrade_Section;
#ifdef NXP_FW_DNLD_CHECK_PHASE
                    gphDnldPhase = NXP_FW_DNLD_CFG_PHASE;
#endif /* NXP_FW_DNLD_CHECK_PHASE */
                    break;
                }
                case phDnld_Lock_System:
                {
#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
                    psDnldContext->next_dnld_state =
                                    (uint8_t)phDnld_Verify_State;
                    psDnldContext->next_dnld_seq =
                                    (uint8_t)phDnld_Verify_Integrity;
#else
                    /* (void ) memset( (void *) &psDnldContext->chk_integrity_crc,
                                0, sizeof(psDnldContext->chk_integrity_crc)); */
                    psDnldContext->next_dnld_state =
                                            (uint8_t) phDnld_Complete_State;
#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
                    break;
                }
                case phDnld_Verify_Integrity:
                {
                    psDnldContext->next_dnld_state =
                                            (uint8_t) phDnld_Complete_State;
                    break;
                }
                default:
                {
                    status = (NFCSTATUS_PENDING == status)?
                            NFCSTATUS_SUCCESS: status;
                    break;
                }
            }
            break;
        }
        case phDnld_Unlock_State:
        {

            status = phDnldNfc_Sequence( psDnldContext, pHwRef,
                                                            pdata, length);
            break;
        }
        case phDnld_Upgrade_State:
        {
            status = phDnldNfc_Upgrade_Sequence( psDnldContext, pHwRef,
                                                            pdata, length);
            if ((NFCSTATUS_SUCCESS == status )
                && (phDnld_Complete_State == psDnldContext->next_dnld_state))
            {
#if 0
                psDnldContext->cur_dnld_seq =
                                    (uint8_t)phDnld_Lock_System;
                psDnldContext->next_dnld_seq =
                                    psDnldContext->cur_dnld_seq;
#endif
#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
                psDnldContext->next_dnld_state =
                                (uint8_t)phDnld_Verify_State;
                psDnldContext->next_dnld_seq =
                                (uint8_t)phDnld_Verify_Integrity;
                psDnldContext->cur_dnld_seq =
                                    psDnldContext->next_dnld_seq;
                status = phDnldNfc_Sequence( psDnldContext,
                                                        pHwRef, pdata, length);
#else
                /* (void ) memset( (void *) &psDnldContext->chk_integrity_crc,
                            0, sizeof(psDnldContext->chk_integrity_crc)); */
                psDnldContext->next_dnld_state =
                                        (uint8_t) phDnld_Complete_State;
#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
            }
            break;
        }
        case phDnld_Verify_State:
        {
            status = phDnldNfc_Sequence( psDnldContext,
                                                 pHwRef, pdata, length);
            break;
        }
        case phDnld_Complete_State:
        {
            uint8_t integrity_chk = 0xA5;

#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
            uint8_t verify_crc = 0x96;

            if ( (NULL != psDnldContext->p_flash_code_crc) 
                  && (NULL != psDnldContext->p_patch_code_crc) 
                   && (NULL != psDnldContext->p_patch_table_crc) 
                  )
            {
                uint8_t     crc_i = 0;
                uint16_t    patch_table_crc = 0;
                uint32_t    flash_code_crc = 0;
                uint32_t    patch_code_crc = 0;

                for (crc_i = 0; crc_i < DNLD_CRC32_SIZE; crc_i++ )
                {
                    if (crc_i < DNLD_CRC16_SIZE )
                    {
                        patch_table_crc = patch_table_crc
                            | psDnldContext->chk_integrity_crc.patch_table.Chk_Crc16[crc_i]
                                    << (crc_i * BYTE_SIZE)  ;
                    }
                    flash_code_crc  = flash_code_crc
                        | psDnldContext->chk_integrity_crc.flash_code.Chk_Crc32[crc_i]
                                << (crc_i * BYTE_SIZE)  ;
                    patch_code_crc  = patch_code_crc
                        | psDnldContext->chk_integrity_crc.patch_code.Chk_Crc32[crc_i]
                                << (crc_i * BYTE_SIZE)  ;
                }
                verify_crc =(uint8_t)( (*((uint32_t *) psDnldContext->p_flash_code_crc)) != 
                          flash_code_crc );
                verify_crc |=(uint8_t)( (*((uint32_t *) psDnldContext->p_patch_code_crc)) != 
                          patch_code_crc );
                verify_crc |=(uint8_t)( (*((uint16_t *) psDnldContext->p_patch_table_crc)) != 
                          patch_table_crc );
            }
            else
            {
                DNLD_PRINT(" FW_DNLD: Flash, Patch code and Patch Table CRC ");
                DNLD_PRINT(" Not Available in the Firmware \n");
            }

#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */

            integrity_chk = psDnldContext->chk_integrity_crc.config_page.Chk_status + 
                        psDnldContext->chk_integrity_crc.patch_table.Chk_status +
                        psDnldContext->chk_integrity_crc.flash_code.Chk_status +
                        psDnldContext->chk_integrity_crc.patch_code.Chk_status;

            if ( ( 0 != integrity_chk ) 
#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
                || ( 0 != verify_crc )
#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
                )
            {
                status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
            }
            break;
        }
        default:
        {
            status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FAILED);
            break;
        }
    }

    if (NFCSTATUS_PENDING == status)
    {
        /* Write/Receive is still pending */
    }
    else
    {
        pphNfcIF_Notification_CB_t  p_upper_notify =
                        psDnldContext->p_upper_notify;
        void                        *p_upper_context =
                        psDnldContext->p_upper_context;

        DNLD_DEBUG(" FW_DNLD: Resume Termination Status = %X \n", status);

        comp_info.status = status;

        (void) phDal4Nfc_Unregister(
                            psDnldContext->lower_interface.pcontext, pHwRef);
        phDnldNfc_Release_Lower(psDnldContext, pHwRef);
        phDnldNfc_Release_Resources(&psDnldContext);
#ifndef NFC_TIMER_CONTEXT
        gpphDnldContext = psDnldContext;
#endif
        /* Notify the Error/Success Scenario to the upper layer */
        phDnldNfc_Notify( p_upper_notify, p_upper_context, pHwRef, (uint8_t)
            ((NFCSTATUS_SUCCESS == comp_info.status )? NFC_IO_SUCCESS: NFC_IO_ERROR),
                    &comp_info );
    }
    return status;
}

STATIC
NFCSTATUS
phDnldNfc_Process_Response(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        void                    *pdata,
                        uint16_t                length
                     )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS;
    phDnldNfc_sData_Hdr_t   *resp_data =
                        (phDnldNfc_sData_Hdr_t *) pdata;

    PHNFC_UNUSED_VARIABLE(pHwRef);
    DNLD_DEBUG(" FW_DNLD: Receive Length = %X \n", length );
    if(( psDnldContext->rx_info.rx_total == 0 )
        && (PHDNLD_MIN_PACKET <= length)
        )
    {
        psDnldContext->rx_info.rx_total =
            ((uint16_t)resp_data->frame_length[0] << BYTE_SIZE)|
                        resp_data->frame_length[1];
        if( psDnldContext->rx_info.rx_total + PHDNLD_MIN_PACKET == length )
        {

            DNLD_DEBUG(" FW_DNLD: Success Memory Read = %X \n",
                                                psDnldContext->rx_info.rx_total);
#ifndef DNLD_SUMMARY
            /* DNLD_PRINT_BUFFER("Receive Buffer",pdata,length); */
#endif

        }
        else
        {
           /* status = phDnldNfc_Receive( psDnldContext, pHwRef,
                psDnldContext->p_resp_buffer,
               (uint8_t)((psDnldContext->rx_info.rx_total <= PHDNLD_MAX_PACKET)?
                    psDnldContext->rx_info.rx_total: PHDNLD_MAX_PACKET) ); */
            DNLD_PRINT(" FW_DNLD: Invalid Receive length ");
            DNLD_DEBUG(": Length Expected = %X \n",
            (psDnldContext->rx_info.rx_total + PHDNLD_MIN_PACKET));
            status = PHNFCSTVAL( CID_NFC_DNLD,
                                    NFCSTATUS_INVALID_RECEIVE_LENGTH );
        }
    }
    else
    {
        /*TODO:*/
        psDnldContext->rx_info.rx_total = 0 ;
        status = PHNFCSTVAL( CID_NFC_DNLD,
                                NFCSTATUS_INVALID_RECEIVE_LENGTH );
    }

    return status;
}



STATIC
void
phDnldNfc_Receive_Complete (
                                void                    *psContext,
                                void                    *pHwRef,
                                phNfc_sTransactionInfo_t *pInfo
                                )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS ;
    void                    *pdata = NULL ;
    phDnldNfc_sData_Hdr_t   *resp_data = NULL;
    uint16_t                length = 0 ;
    phNfc_sCompletionInfo_t comp_info = {0,0,0};

    DNLD_PRINT("\n FW_DNLD: Receive Response .... ");
    if ( (NULL != psContext)
        && (NULL != pInfo)
        && (NULL != pHwRef)
        )
    {
        phDnldNfc_sContext_t *psDnldContext =
                                (phDnldNfc_sContext_t *)psContext;
        status = pInfo->status ;
        length = pInfo->length ;
        pdata = pInfo->buffer;

        if(status != NFCSTATUS_SUCCESS)
        {
            DNLD_DEBUG(" Failed. Status = %02X\n",status);
            /* Handle the Error Scenario */
        }
        else if (NULL == pdata)
        {
            DNLD_DEBUG(" Failed. No data received. pdata = %02X\n",pdata);
            /* Handle the Error Scenario */
            status = PHNFCSTVAL( CID_NFC_DNLD,  NFCSTATUS_FAILED );
        }
        else if ((0 == length) 
            || (PHDNLD_MIN_PACKET > length ))
        {
            DNLD_DEBUG(" Receive Response Length = %u .... \n",length);
            /* Handle the Error Scenario */
#ifndef HAL_SW_DNLD_RLEN
             status = PHNFCSTVAL( CID_NFC_DNLD,
                           NFCSTATUS_INVALID_RECEIVE_LENGTH );
#endif
        }
        else
        {

#if defined(FW_DOWNLOAD_TIMER) && \
                (FW_DOWNLOAD_TIMER == 2)
        if ( NXP_INVALID_TIMER_ID != psDnldContext->timer_id )
        {
            phOsalNfc_Timer_Stop( psDnldContext->timer_id );
        }

#endif

#ifndef DNLD_SUMMARY
            DNLD_PRINT_BUFFER("Receive Buffer",pdata,length);
#endif
            DNLD_DEBUG(" Receive Response Length = %X. \n", length);

            resp_data = (phDnldNfc_sData_Hdr_t *) pdata;

            switch(resp_data->frame_type)
            {
                case PHDNLD_RESP_SUCCESS:
                {
                    uint16_t resp_length =
                        ((uint16_t)resp_data->frame_length[0] << BYTE_SIZE)|
                                    resp_data->frame_length[1];
                    switch ( psDnldContext->prev_cmd )
                    {
                        case PHDNLD_CMD_READ :
                        {
							if( PHDNLD_NO_OPERATION
							       == psDnldContext->raw_mode_upgrade)
                            {
                            status = phDnldNfc_Process_Response(
                                    psDnldContext, pHwRef, pdata , length);

                            if (NFCSTATUS_SUCCESS != status)
                            {
                                /* psDnldContext->dnld_retry++; */
                                psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                                /* psDnldContext->dnld_retry < NXP_MAX_DNLD_RETRY */
                            }
                            }
                            else
                            {

                            }
                            break;
                        }
                        case PHDNLD_CMD_CHECK_INTEGRITY :
                        {
							if( PHDNLD_NO_OPERATION
							       == psDnldContext->raw_mode_upgrade)
							{
#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
                            phDnldNfc_sChkCrcComplete_t *p_dnld_crc_all = 
                                &psDnldContext->chk_integrity_crc;
                            switch(psDnldContext->chk_integrity_param)
                            {
                                case CHK_INTEGRITY_CONFIG_PAGE_CRC:
                                {
                                    (void)memcpy(&p_dnld_crc_all->config_page,  
                                                (((uint8_t *)pdata) + PHDNLD_MIN_PACKET), resp_length); 
                                    break;
                                }
                                case CHK_INTEGRITY_PATCH_TABLE_CRC:
                                {
                                    (void)memcpy(&p_dnld_crc_all->patch_table,  
                                                (((uint8_t *)pdata) + PHDNLD_MIN_PACKET), resp_length); 
                                    break;
                                }
                                case CHK_INTEGRITY_FLASH_CODE_CRC:
                                {
                                    (void)memcpy(&p_dnld_crc_all->flash_code,  
                                                (((uint8_t *)pdata) + PHDNLD_MIN_PACKET), resp_length); 
                                    break;
                                }
                                case CHK_INTEGRITY_PATCH_CODE_CRC:
                                {
                                    (void)memcpy(&p_dnld_crc_all->patch_code,  
                                                (((uint8_t *)pdata) + PHDNLD_MIN_PACKET), resp_length); 
                                    break;
                                }
                                case CHK_INTEGRITY_COMPLETE_CRC:
                                {
                                    (void)memcpy(p_dnld_crc_all,
                                            (((uint8_t *)pdata) + PHDNLD_MIN_PACKET), resp_length); 
                                    DNLD_DEBUG(" FW_DNLD: Check Integrity Complete Structure Size  = %X \n", 
                                                    sizeof(psDnldContext->chk_integrity_crc));
                                    break;
                                }
                                default:
                                {
                                    status = PHNFCSTVAL(CID_NFC_DNLD,
                                            NFCSTATUS_FEATURE_NOT_SUPPORTED);
                                    break;
                                }
                            }
#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
							}
							else
							{
                                psDnldContext->raw_mode_upgrade =
                                     (PHDNLD_SETUP_OPERATION | PHDNLD_ADVANCED_OPERATION);
                                /* psDnldContext->raw_mode_upgrade =
                                    (psDnldContext->raw_mode_upgrade &
                                     ( psDnldContext->cur_frame_info & ~PHDNLD_MARKER_MASK )); */
							}
                            break;
                        }
                        case PHDNLD_CMD_WRITE:
                        {
                            psDnldContext->dnld_retry = 0;
                            break;
                        }
                        case PHDNLD_CMD_SEC_WRITE:
                        {
                            psDnldContext->dnld_retry = 0;
                            break;
                        }
                        case PHDNLD_CMD_ACTIVATE_PATCH:
                        case PHDNLD_CMD_CHECK:
                        default:
                        {
							if( PHDNLD_NO_OPERATION
							       == psDnldContext->raw_mode_upgrade)
							{
                            if( ( (PHDNLD_MIN_PACKET > length)
                                || ( 0 != resp_length) )
                                )
                            {
                                psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                                status = PHNFCSTVAL( CID_NFC_DNLD,
                                                NFCSTATUS_INVALID_RECEIVE_LENGTH );
                            }
                            else
                            {
                                psDnldContext->dnld_retry = 0;
                            }
							}
							else
							{
                                psDnldContext->raw_mode_upgrade =
                                        (psDnldContext->raw_mode_upgrade & ~PHDNLD_RECOVER_OPERATION);
							}
                            break;
                        }
                    } /* End of the Previous Command Switch Case */
                    break;
                }/* Case PHDNLD_RESP_SUCCESS*/
                case PHDNLD_RESP_TIMEOUT:
                case PHDNLD_RESP_CRC_ERROR:
                case PHDNLD_RESP_WRITE_ERROR:
                {
                    if(psDnldContext->dnld_retry < NXP_MAX_DNLD_RETRY )
                    {
                        psDnldContext->dnld_retry++;
                    }
                    status = PHNFCSTVAL(CID_NFC_DNLD,
                                                resp_data->frame_type);
                    break;
                }
                /* fall through */
                case PHDNLD_RESP_ACCESS_DENIED:
                case PHDNLD_RESP_INVALID_PARAMETER:
                case PHDNLD_RESP_INVALID_LENGTH:
                    /*  Initial Frame Checksum */
                case PHDNLD_RESP_CHKSUM_ERROR:
                case PHDNLD_RESP_MEMORY_UPDATE_ERROR:
                {
                    psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                    status = PHNFCSTVAL(CID_NFC_DNLD,
                                                resp_data->frame_type);
                    break;
                }
                case PHDNLD_RESP_PROTOCOL_ERROR:
                {
					if(( PHDNLD_NO_OPERATION
							== psDnldContext->raw_mode_upgrade)
                            || ( PHDNLD_ADVANCED_OPERATION
							== psDnldContext->raw_mode_upgrade)
                            )
                    {
                        psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                        status = PHNFCSTVAL(CID_NFC_DNLD,
                                            NFCSTATUS_INVALID_FORMAT);
                    }
					else if( (PHDNLD_NORMAL_OPERATION
                                 & psDnldContext->raw_mode_upgrade)
                            )
					{
                        psDnldContext->raw_mode_upgrade =
                               (psDnldContext->raw_mode_upgrade & ~PHDNLD_NORMAL_OPERATION);
					}
                    else if ( PHDNLD_RECOVER_OPERATION
                                 & psDnldContext->raw_mode_upgrade )
                    {
                        psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                        status = PHNFCSTVAL(CID_NFC_DNLD,
                                            NFCSTATUS_INVALID_FORMAT);
                    }
                    else
                    {
                       psDnldContext->raw_mode_upgrade =
                        (psDnldContext->raw_mode_upgrade &
                            ~( psDnldContext->cur_frame_info & ~PHDNLD_MARKER_MASK ));
                    }
                    break;
                }
                case PHDNLD_RESP_VERSION_UPTODATE:
                {
					/* TODO: to make sure that the Advance Frames are sent to get
					 *       the updated status */
					if ( PHDNLD_ADVANCED_OPERATION
                                 == psDnldContext->raw_mode_upgrade)
					{
						status = ( CID_NFC_DNLD << BYTE_SIZE ) ;
					}
                    else if ( PHDNLD_NO_OPERATION
                                != psDnldContext->raw_mode_upgrade)
                    {

                       psDnldContext->raw_mode_upgrade =
                        (psDnldContext->raw_mode_upgrade &
                            ~( psDnldContext->cur_frame_info & ~PHDNLD_MARKER_MASK ));
                    }
                    else
                    {
                    }
                    break;
                }
                case PHDNLD_RESP_CMD_NOT_SUPPORTED:
                {

                    if ( PHDNLD_NO_OPERATION
                                 == psDnldContext->raw_mode_upgrade)
                    {
                        status = PHNFCSTVAL(CID_NFC_DNLD,
                            NFCSTATUS_FEATURE_NOT_SUPPORTED);
                    }
                    else if ( PHDNLD_ADVANCED_OPERATION
                                 == psDnldContext->raw_mode_upgrade)
					{
						status = PHNFCSTVAL(CID_NFC_DNLD,
										 NFCSTATUS_FEATURE_NOT_SUPPORTED);
					}
#if 0
					else if( (PHDNLD_NORMAL_OPERATION
                                 & psDnldContext->raw_mode_upgrade)
                            )
					{
                        psDnldContext->raw_mode_upgrade =
                               (psDnldContext->raw_mode_upgrade & ~PHDNLD_NORMAL_OPERATION);
					}
                    else if ( PHDNLD_SETUP_OPERATION
                                 & psDnldContext->raw_mode_upgrade )
                    {
                        psDnldContext->raw_mode_upgrade =
                               (psDnldContext->raw_mode_upgrade & ~PHDNLD_SETUP_OPERATION);
                    }
#endif
                    else
                    {
                       psDnldContext->raw_mode_upgrade =
                        (psDnldContext->raw_mode_upgrade &
                            ~( psDnldContext->cur_frame_info & ~PHDNLD_MARKER_MASK ));
                    }
                    break;
                }
               /*  The Chaining of the Command Frame
                                  was Successful in the Download Mode */
                case PHDNLD_RESP_CHAINING_SUCCESS:
                {
					/* TODO: Handle the Corner Case Scenarios
					 *       the updated status */
                    psDnldContext->dnld_retry = 0x00;
                    break;
                }
/*  The Error during the Chaining the Command Frame in the Download Mode */
                case PHDNLD_RESP_CHAINING_ERROR:
                {
					/* TODO: Restart the Chunk in Corner Case
					 *       the updated status */
                    psDnldContext->dnld_retry++;
                    phDnldNfc_Tx_Reset(psDnldContext);
                    break;
                }
/*  The Command is not allowed anymore in the Download Mode */
                case PHDNLD_RESP_CMD_NOT_ALLOWED:
                default:
                {
                    psDnldContext->dnld_retry = NXP_MAX_DNLD_RETRY;
                    status = PHNFCSTVAL(CID_NFC_DNLD,
                                        NFCSTATUS_NOT_ALLOWED);
                    break;
                }

            } /* End of the Response Frame Type Switch */

            if (NFCSTATUS_PENDING != status)
            {
                if ((NFCSTATUS_SUCCESS != status) &&
                    (psDnldContext->dnld_retry >= NXP_MAX_DNLD_RETRY))
                {
                    pphNfcIF_Notification_CB_t  p_upper_notify =
                        psDnldContext->p_upper_notify;
                    void                        *p_upper_context =
                                        psDnldContext->p_upper_context;

                    comp_info.status = status;
                    DNLD_DEBUG(" FW_DNLD: Termination in Receive, Status = %X \n", status);
                    status = phDal4Nfc_Unregister(
                                psDnldContext->lower_interface.pcontext, pHwRef);
                    phDnldNfc_Release_Lower(psDnldContext, pHwRef);
                    phDnldNfc_Release_Resources(&psDnldContext);
#ifndef NFC_TIMER_CONTEXT
                    gpphDnldContext = psDnldContext;
#endif
                    /* Notify the Error/Success Scenario to the upper layer */
                    phDnldNfc_Notify( p_upper_notify, p_upper_context, pHwRef,
                                    (uint8_t) NFC_IO_ERROR, &comp_info );
                }
                else if ( (NFCSTATUS_SUCCESS != status) &&
                           (NFCSTATUS_SUCCESS == PHNFCSTATUS(status))
                         )
                {
                    pphNfcIF_Notification_CB_t  p_upper_notify =
                        psDnldContext->p_upper_notify;
                    void                        *p_upper_context =
                                        psDnldContext->p_upper_context;

                    comp_info.status = NFCSTATUS_SUCCESS;
                    DNLD_DEBUG(" FW_DNLD: Termination in Receive, Status = %X \n", status);
                    status = phDal4Nfc_Unregister(
                                psDnldContext->lower_interface.pcontext, pHwRef);
                    phDnldNfc_Release_Lower(psDnldContext, pHwRef);
                    phDnldNfc_Release_Resources(&psDnldContext);
#ifndef NFC_TIMER_CONTEXT
                    gpphDnldContext = psDnldContext;
#endif
                    /* Notify the Error/Success Scenario to the upper layer */
                    phDnldNfc_Notify( p_upper_notify, p_upper_context, pHwRef,
                        (uint8_t) NFC_IO_SUCCESS, &comp_info );

                }
                else if (NFCSTATUS_FEATURE_NOT_SUPPORTED == PHNFCSTATUS(status))
                {
                    pphNfcIF_Notification_CB_t  p_upper_notify =
                        psDnldContext->p_upper_notify;
                    void                        *p_upper_context =
                                        psDnldContext->p_upper_context;

                    comp_info.status = status;
                    DNLD_DEBUG(" FW_DNLD: Termination in Receive, Status = %X \n", status);
                    status = phDal4Nfc_Unregister(
                                psDnldContext->lower_interface.pcontext, pHwRef);
                    phDnldNfc_Release_Lower(psDnldContext, pHwRef);
                    phDnldNfc_Release_Resources(&psDnldContext);
#ifndef NFC_TIMER_CONTEXT
                    gpphDnldContext = psDnldContext;
#endif
                    /* Notify the Error/Success Scenario to the upper layer */
                    phDnldNfc_Notify( p_upper_notify, p_upper_context, pHwRef,
                        (uint8_t) NFC_IO_SUCCESS, &comp_info );

                }
                else
                {
                    /* DNLD_PRINT(" FW_DNLD: Successful.\n"); */
                    psDnldContext->resp_length = /* PHDNLD_MIN_PACKET */ 0 ;
                    status = phDnldNfc_Set_Seq(psDnldContext,
                                                    DNLD_SEQ_UPDATE);
                    status = phDnldNfc_Resume( psDnldContext,
                                            pHwRef, pdata, length );
                }
            }
        } /* End of status != Success */
    }
}


STATIC
void
phDnldNfc_Send_Complete (
                                void                    *psContext,
                                void                    *pHwRef,
                                phNfc_sTransactionInfo_t *pInfo
                       )
{
    NFCSTATUS               status = NFCSTATUS_SUCCESS ;
    uint16_t                    length = 0;

    DNLD_PRINT(" FW_DNLD: Send Data .... ");
    if ( (NULL != psContext)
        && (NULL != pInfo)
        && (NULL != pHwRef)
        )
    {
        phDnldNfc_sContext_t *psDnldContext =
                                    (phDnldNfc_sContext_t *)psContext;
        status = pInfo->status ;
        length = pInfo->length ;
        if(status != NFCSTATUS_SUCCESS)
        {
            DNLD_DEBUG(" Failed. Status = %02X\n",status);
            /* Handle the Error Scenario */
        }
        else
        {
            DNLD_PRINT(" Successful.\n");
            (void)memset((void *)&psDnldContext->dnld_data, 0,
                                sizeof(psDnldContext->dnld_data));
            if ((PHDNLD_CMD_SET_HIF != psDnldContext->prev_cmd)
                && (PHDNLD_CMD_RESET != psDnldContext->prev_cmd))
            {
                psDnldContext->rx_info.rx_total = 0;
                status = phDnldNfc_Receive( psDnldContext, pHwRef,
                            (uint8_t *)(&psDnldContext->dnld_resp),
                                           psDnldContext->resp_length);
            }
            else
            {
                psDnldContext->resp_length = 0;
                psDnldContext->dnld_retry = 0;
                /* clock unstable after SW reset command, especially on UART
                 * platform because of its sensitivity to clock. Experimentally
                 * we found clock unstable for 750us. Delay for 5ms to be sure.
                 */
                if( PHDNLD_CMD_RESET == psDnldContext->prev_cmd )
                {
                    DO_DELAY(PHDNLD_DNLD_DELAY);
                }
#if defined(FW_DOWNLOAD_TIMER) && \
                (FW_DOWNLOAD_TIMER == 2)

                if ( NXP_INVALID_TIMER_ID != psDnldContext->timer_id )
                {
                    phOsalNfc_Timer_Stop( psDnldContext->timer_id );
                }
#endif

                status = phDnldNfc_Set_Seq(psDnldContext,
                                                DNLD_SEQ_UPDATE);
            }

            if(NFCSTATUS_SUCCESS == status )
            {
                status = phDnldNfc_Resume( psDnldContext, pHwRef, NULL, length);
            }

        } /* End of status != Success */

    } /* End of Context != NULL  */
}



STATIC
NFCSTATUS
phDnldNfc_Send_Command(
                        phDnldNfc_sContext_t    *psDnldContext,
                        void                    *pHwRef,
                        uint8_t                 cmd,
                        void                    *params,
                        uint16_t                param_length
                      )
{
    NFCSTATUS   status = NFCSTATUS_SUCCESS;
    uint16_t    tx_length = 0;
    uint16_t    rx_length = 0;
    uint8_t     **pp_resp_data = &psDnldContext->p_resp_buffer;
    phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;

    switch(cmd)
    {
        case PHDNLD_CMD_RESET:
        {
            (void)memset((void *)&psDnldContext->dnld_data, 0,
                                sizeof(psDnldContext->dnld_data));
            break;
        }
        case PHDNLD_CMD_READ:
        {
            phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;
            phDnldNfc_sParam_t  *param_info = /* (phDnldNfc_sParam_t *)params */
                               &p_dnld_data->param_info.data_param;
            tx_length = PHDNLD_CMD_READ_LEN;
            if (NULL != *pp_resp_data)
            {
                phOsalNfc_FreeMemory(*pp_resp_data);
                *pp_resp_data = NULL;
            }
            rx_length = (uint16_t) (((uint16_t)param_info->data_len[0]
                                   << BYTE_SIZE) + param_info->data_len[1]);

            psDnldContext->resp_length =
                (( rx_length + PHDNLD_MIN_PACKET ));
            (void)phDnldNfc_Allocate_Resource( (void **) pp_resp_data,
                     rx_length);
            break;
        }
        case PHDNLD_CMD_WRITE:
        case PHDNLD_CMD_SEC_WRITE:
        {
            phDnldNfc_sData_t       *p_dnld_data =
                 (phDnldNfc_sData_t *)psDnldContext->dnld_data;
            phDnldNfc_sParam_t  *param_info = /* (phDnldNfc_sParam_t *)params */
                                &p_dnld_data->param_info.data_param;
            tx_length = (uint16_t) (((uint16_t)param_info->data_len[0]
                        << BYTE_SIZE) + param_info->data_len[1]
                                    + PHDNLD_CMD_WRITE_MIN_LEN );

            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            if ((0 != param_length) && (NULL != params))
            {
                (void)memcpy(param_info->data_packet,  params, param_length);
            }
            break;
        }
        case PHDNLD_CMD_CHECK:
        {
            tx_length = PHDNLD_CMD_CHECK_LEN;
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            break;
        }
        case PHDNLD_CMD_ENCAPSULATE:
        {
            uint8_t  i = 0x00;
            if ((0 != param_length) && (NULL != params))
            {
                p_dnld_data->frame_type =
                            PHDNLD_CMD_ENCAPSULATE;
                (void)memcpy((void *)( ((uint8_t *)p_dnld_data)
                                           + PHDNLD_FRAME_DATA_OFFSET)
                                        , params, param_length);
                tx_length = param_length;

                p_dnld_data->frame_length[i++] =
                           (uint8_t)(tx_length >> BYTE_SIZE);
                p_dnld_data->frame_length[i]   =
                           (uint8_t)( tx_length & BYTE_MASK );
                tx_length += PHDNLD_FRAME_DATA_OFFSET;

                psDnldContext->resp_length = PHDNLD_MIN_PACKET;

                status = phDnldNfc_Send( psDnldContext, pHwRef ,
                                    (uint8_t *)p_dnld_data, tx_length);
            }
            else
            {
               status = PHNFCSTVAL(CID_NFC_DNLD,
                              NFCSTATUS_NOT_ALLOWED);
            }
            break;
        }
        case PHDNLD_CMD_SET_HIF:
        {
            tx_length++;
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            break;
        }
        case PHDNLD_CMD_ACTIVATE_PATCH:
        {
            psDnldContext->resp_length = PHDNLD_MIN_PACKET;
            if ((NULL != params) && ( param_length > 0 ))
            {
                p_dnld_data->param_info.cmd_param =
                                            (*(uint8_t *)params);
                tx_length = param_length;
            }
            else
            {
                p_dnld_data->param_info.cmd_param = FALSE;
                tx_length++;
            }
            break;
        }
        case PHDNLD_CMD_CHECK_INTEGRITY:
        {
#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
            if ((NULL != params) && ( param_length > 0 ))
            {
                psDnldContext->chk_integrity_param = 
                            (phDnldNfc_eChkCrc_t)(*(uint8_t *)params);
                tx_length = param_length;
            }
            else
            {
                psDnldContext->chk_integrity_param = CHK_INTEGRITY_COMPLETE_CRC;
                tx_length++;
            }
            p_dnld_data->param_info.cmd_param =
                                (uint8_t) psDnldContext->chk_integrity_param;
            switch(psDnldContext->chk_integrity_param)
            {
                case CHK_INTEGRITY_CONFIG_PAGE_CRC:
                case CHK_INTEGRITY_PATCH_TABLE_CRC:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET 
                                         + CHECK_INTEGRITY_RESP_CRC16_LEN;
                    break;
                }
                case CHK_INTEGRITY_FLASH_CODE_CRC:
                case CHK_INTEGRITY_PATCH_CODE_CRC:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET 
                                        +  CHECK_INTEGRITY_RESP_CRC32_LEN;
                    break;
                }
                case CHK_INTEGRITY_COMPLETE_CRC:
                default:
                {
                    psDnldContext->resp_length = PHDNLD_MIN_PACKET 
                                        +  CHECK_INTEGRITY_RESP_COMP_LEN;
                    break;
                }
            }
#else
            tx_length++;
            p_dnld_data->param_info.cmd_param =
                                (uint8_t) CHK_INTEGRITY_COMPLETE_CRC;

#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
            break;
        }
        default:
        {
            status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_FEATURE_NOT_SUPPORTED);
            break;
        }
    }
    if (NFCSTATUS_SUCCESS == status)
    {
        uint8_t     i = 0;

        p_dnld_data->frame_type = cmd;
        p_dnld_data->frame_length[i++] =
                                    (uint8_t)(tx_length >> BYTE_SIZE);
        p_dnld_data->frame_length[i]   =
                                    (uint8_t)( tx_length & BYTE_MASK );
        tx_length = tx_length + PHDNLD_MIN_PACKET;
        status = phDnldNfc_Send( psDnldContext, pHwRef ,
                            (uint8_t *)p_dnld_data, tx_length);
        if(NFCSTATUS_PENDING == status)
        {
            psDnldContext->prev_cmd = cmd;

            if( PHDNLD_CMD_RESET == cmd )
                DO_DELAY(PHDNLD_DNLD_DELAY);  //this seems like its on the wrong thread
        }
    }

    return status;
}

static
NFCSTATUS
phDnldNfc_Check_FW(
                    phHal_sHwReference_t    *pHwRef,
                    fw_data_hdr_t           *cur_fw_hdr
                     )
{
    NFCSTATUS               status = NFCSTATUS_FAILED;

        if ( !pHwRef->device_info.fw_version )
        {
            /* Override the Firmware Version Check and upgrade*/;
            DNLD_PRINT(" FW_DNLD_CHK: Forceful Upgrade of the Firmware .... Required \n");
            status = NFCSTATUS_SUCCESS;
        }
        else    if ( (pHwRef->device_info.fw_version >> (BYTE_SIZE * 2)) 
                != ( cur_fw_hdr->fw_version >> (BYTE_SIZE * 2) ))
        {
            /* Check for the Compatible Romlib Version for the Hardware */
            DNLD_PRINT(" FW_DNLD: IC Hardware Version Mismatch.. \n");
            status = PHNFCSTVAL( CID_NFC_DNLD, NFCSTATUS_NOT_ALLOWED );
        }
        else if (( pHwRef->device_info.fw_version < cur_fw_hdr->fw_version  )
            )
        {
            /* TODO: Firmware Version Check and upgrade*/
            DNLD_PRINT(" FW_DNLD: Older Firmware Upgrading to newerone.... \n");
            status = NFCSTATUS_SUCCESS;
        }
#ifdef NXP_FW_CHK_LATEST
        else if (( pHwRef->device_info.fw_version > cur_fw_hdr->fw_version  )
            )
        {
            DNLD_PRINT(" FW_DNLD: Newer than the Stored One .... \n");
            status = PHNFCSTVAL( CID_NFC_DNLD, NFCSTATUS_NOT_ALLOWED );
        }
#endif /* NXP_FW_CHK_LATEST */
        else
        {
            DNLD_PRINT(" FW_DNLD: Already Updated .... \n");
            status = ( CID_NFC_DNLD << BYTE_SIZE ) ;
        }

    return status;
    }


static
NFCSTATUS
phDnldNfc_Process_FW(
                        phDnldNfc_sContext_t    *psDnldContext,
                        phHal_sHwReference_t    *pHwRef
#ifdef NXP_FW_PARAM
                        ,uint8_t                 *nxp_nfc_fw
                        ,uint32_t                 nxp_fw_len
#endif
                     )
{
    NFCSTATUS               status = NFCSTATUS_FAILED;
    section_info_t          *p_cur_sec = NULL;
    static unsigned         sec_type;
    uint32_t                fw_index = 0;
#ifdef NXP_NFC_MULTIPLE_FW
    phDnldNfc_sFwImageInfo_t  *p_cur_fw = NULL;
#endif /* #ifdef NXP_NFC_MULTIPLE_FW */
    fw_data_hdr_t           *cur_fw_hdr = NULL;
    uint8_t                 sec_index = 0;
    uint8_t                 i = 0;

    psDnldContext->p_img_hdr = (img_data_hdr_t *) nxp_nfc_fw;

#ifdef NXP_NFC_MULTIPLE_FW

    /* TODO: Create a memory of pointers to store all the Firmwares */
    if( (NXP_NFC_IMAG_FW_MAX > psDnldContext->p_img_hdr->no_of_fw_img)
        && (0 != psDnldContext->p_img_hdr->no_of_fw_img)
        )
    {
        ( void )phDnldNfc_Allocate_Resource((void **)&psDnldContext->p_img_info,
            (psDnldContext->p_img_hdr->no_of_fw_img * sizeof(phDnldNfc_sFwImageInfo_t)));

        if(NULL != psDnldContext->p_img_info)
        {
            p_cur_fw = psDnldContext->p_img_info;
        }
    }
#endif /* #ifdef NXP_NFC_MULTIPLE_FW */

    fw_index = sizeof (img_data_hdr_t);

    for ( i=0; i < psDnldContext->p_img_hdr->no_of_fw_img; i++ )
    {

        psDnldContext->p_fw_hdr = (fw_data_hdr_t *) ( nxp_nfc_fw + fw_index );

#ifdef NXP_NFC_MULTIPLE_FW
        if(NULL != p_cur_fw)
        {
            ( p_cur_fw + i)->p_fw_hdr = psDnldContext->p_fw_hdr;
        }
#endif /* #ifdef NXP_NFC_MULTIPLE_FW */
        cur_fw_hdr = psDnldContext->p_fw_hdr;

        fw_index = fw_index + (cur_fw_hdr->fw_hdr_len * PNDNLD_WORD_LEN);

        status = phDnldNfc_Check_FW( pHwRef, cur_fw_hdr);

    }

    if ( ( NFCSTATUS_SUCCESS == status )
#if  defined (NXP_FW_INTEGRITY_VERIFY)
        || (NFCSTATUS_SUCCESS == PHNFCSTATUS(status) )
#endif /* !defined (NXP_FW_INTEGRITY_VERIFY) */
        )
    {
        if( (BYTE_MASK > cur_fw_hdr->no_of_sections)
           && (0 != cur_fw_hdr->no_of_sections)
          )
        {
        (void) phDnldNfc_Allocate_Resource((void **)&psDnldContext->p_fw_sec,
            (cur_fw_hdr->no_of_sections * sizeof(section_info_t)));

            if(NULL != psDnldContext->p_fw_sec)
        {
            DNLD_DEBUG(" FW_DNLD: FW Index : %x \n",
                                            fw_index );

            DNLD_DEBUG(" FW_DNLD: No of Sections : %x \n\n",
                                            cur_fw_hdr->no_of_sections);

            for(sec_index = 0; sec_index
                            < cur_fw_hdr->no_of_sections; sec_index++ )
            {
                p_cur_sec = ((section_info_t *)
                                (psDnldContext->p_fw_sec + sec_index ));

                p_cur_sec->p_sec_hdr = (section_hdr_t *)
                                        (nxp_nfc_fw + fw_index);

                DNLD_DEBUG(" FW_DNLD: Section %x \n",   sec_index);
                DNLD_DEBUG(" FW_DNLD: Section Header Len : %x   ",
                                        p_cur_sec->p_sec_hdr->section_hdr_len);
                DNLD_DEBUG(" Section Address : %x   ",
                                        p_cur_sec->p_sec_hdr->section_address);
                DNLD_DEBUG(" Section Length : %x   ",
                                        p_cur_sec->p_sec_hdr->section_length);
                DNLD_DEBUG(" Section Memory Type : %x   \n",
                                        p_cur_sec->p_sec_hdr->section_mem_type);

                sec_type = (unsigned int)p_cur_sec->p_sec_hdr->section_mem_type;

                if((sec_type & DNLD_TRIM_MASK))
                {
                    p_cur_sec->p_trim_data = (uint8_t *)
                               (nxp_nfc_fw + fw_index + sizeof(section_hdr_t));
                }
                else
                {
                    p_cur_sec->p_trim_data = NULL;
                }

                    if (0 == sec_index)
                    {
                        if ((sec_type & DNLD_SM_UNLOCK_MASK))
                        {
                            (void)phDnldNfc_Set_Seq(psDnldContext,
                                                            DNLD_SEQ_UNLOCK);
                        }
                        else
                        {
                            (void)phDnldNfc_Set_Seq(psDnldContext,
                                                            DNLD_SEQ_INIT);
                        }
                    }
                p_cur_sec->section_read = FALSE;

                p_cur_sec->section_offset = 0;

                p_cur_sec->p_sec_data = ((uint8_t *) nxp_nfc_fw) + fw_index +
                    (p_cur_sec->p_sec_hdr->section_hdr_len * PNDNLD_WORD_LEN);

                fw_index = fw_index +
                    (p_cur_sec->p_sec_hdr->section_hdr_len * PNDNLD_WORD_LEN)
                   + p_cur_sec->p_sec_hdr->section_length;


                    if( 0 != p_cur_sec->p_sec_hdr->section_checksum )
                    {
                            DNLD_DEBUG(" FW_DNLD: Section checksum : %x \n",
                                            p_cur_sec->p_sec_hdr->section_checksum );

                            p_cur_sec->p_sec_chksum = ( uint8_t *)(nxp_nfc_fw + fw_index);

                            fw_index = fw_index +
                                p_cur_sec->p_sec_hdr->section_checksum;
                    }

               DNLD_DEBUG(" FW_DNLD: FW Index : %x \n", fw_index );

#if  (NXP_FW_INTEGRITY_CHK >= 0x01)
              switch( p_cur_sec->p_sec_hdr->section_address )
               {
                   case DNLD_FW_CODE_ADDR:
                   {
                       psDnldContext->p_flash_code_crc = 
                           p_cur_sec->p_sec_data 
                             + p_cur_sec->p_sec_hdr->section_length
                                - DNLD_CRC32_SIZE;
                       break;
                   }
                   case DNLD_PATCH_CODE_ADDR:
                   {
                       psDnldContext->p_patch_code_crc = 
                           p_cur_sec->p_sec_data 
                             + p_cur_sec->p_sec_hdr->section_length
                                - DNLD_CRC32_SIZE;
                       break;
                   }
                   case DNLD_PATCH_TABLE_ADDR:
                   {
                       psDnldContext->p_patch_table_crc = 
                           p_cur_sec->p_sec_data 
                             + p_cur_sec->p_sec_hdr->section_length
                                - DNLD_CRC16_SIZE;
                       break;
                   }
                   default:
                   {
                       break;
                   }

                    } /* End of Address Switch */
#endif /* #if  (NXP_FW_INTEGRITY_CHK >= 0x01) */
                } /* End of For Loop */
            } /* End of the Null Check */
            else
            {
                status = PHNFCSTVAL(CID_NFC_DNLD,
                        NFCSTATUS_INSUFFICIENT_RESOURCES);
               }

            }
        else if (
                   (0 == cur_fw_hdr->no_of_sections)
                   && (PHDNLD_FW_PATCH_SEC == cur_fw_hdr->fw_patch)
                )
        {
            psDnldContext->p_fw_raw = (uint8_t *)(nxp_nfc_fw + fw_index);

			psDnldContext->raw_mode_upgrade = PHDNLD_COMPLETE_OPERATION;

            (void)phDnldNfc_Set_Seq(psDnldContext,
                                            DNLD_SEQ_RAW);
        }
        else
        {
          DNLD_PRINT("*********  Empty Section and Firmware ******************\n\n");
        }

            DNLD_PRINT("*******************************************\n\n");

    }
    return status;
}

#if  !defined (NXP_FW_INTEGRITY_VERIFY)

NFCSTATUS
phDnldNfc_Run_Check(
                        phHal_sHwReference_t    *pHwRef
#ifdef NXP_FW_PARAM
                        ,uint8_t                 *nxp_nfc_fw
                         uint32_t                  fw_length
#endif
                   )
{
    NFCSTATUS               status = NFCSTATUS_FAILED;
    uint32_t                fw_index = 0;
    img_data_hdr_t          *p_img_hdr = NULL;
    fw_data_hdr_t           *p_fw_hdr = NULL;
    fw_data_hdr_t           *cur_fw_hdr = NULL;
    uint8_t                  i = 0;

    p_img_hdr = (img_data_hdr_t *) nxp_nfc_fw;

    fw_index = sizeof (img_data_hdr_t);

    for ( i=0; i < p_img_hdr->no_of_fw_img; i++ )
    {
        p_fw_hdr = (fw_data_hdr_t *) ( nxp_nfc_fw + fw_index );
        /* TODO: Create a memory of pointers to store all the Firmwares */
        cur_fw_hdr = p_fw_hdr;

        fw_index = fw_index + (cur_fw_hdr->fw_hdr_len * PNDNLD_WORD_LEN);

        status = phDnldNfc_Check_FW( pHwRef, cur_fw_hdr);
    }
    return status;
}

#endif /* #if  !defined (NXP_FW_INTEGRITY_VERIFY) */


STATIC
void
phDnldNfc_Abort (
                    uint32_t    abort_id
#ifdef NFC_TIMER_CONTEXT
                    , void     *dnld_cntxt
#endif
                )
{

    phNfc_sCompletionInfo_t  comp_info = {0,0,0};

    phDnldNfc_sContext_t *p_dnld_context = NULL;

#ifdef NFC_TIMER_CONTEXT
    p_dnld_context = (phDnldNfc_sContext_t *)dnld_cntxt;
#else
    p_dnld_context = gpphDnldContext;
#endif

    if ( ( NULL != p_dnld_context)
            && (abort_id == p_dnld_context->timer_id ))
    {
        pphNfcIF_Notification_CB_t  p_upper_notify =
            p_dnld_context->p_upper_notify;
        void                        *p_upper_context =
                                p_dnld_context->p_upper_context;
        phHal_sHwReference_t        *pHwRef = p_dnld_context->p_hw_ref;

        (void)phDal4Nfc_Unregister(
                     p_dnld_context->lower_interface.pcontext, pHwRef );
        phDnldNfc_Release_Lower(p_dnld_context, pHwRef);
        phDnldNfc_Release_Resources(&p_dnld_context);
#ifndef NFC_TIMER_CONTEXT
        gpphDnldContext = p_dnld_context;
#endif

        /* Notify the Error/Success Scenario to the upper layer */
        DNLD_DEBUG(" FW_DNLD: FW_DNLD Aborted with %x Timer Timeout \n",
                                                                abort_id);
        comp_info.status = NFCSTATUS_FAILED ;
        phDnldNfc_Notify( p_upper_notify, p_upper_context,
                        pHwRef, (uint8_t) NFC_IO_ERROR, &comp_info );
    }

    return ;
}



NFCSTATUS
phDnldNfc_Upgrade (
                        phHal_sHwReference_t            *pHwRef,
#ifdef NXP_FW_PARAM
                        uint8_t                          type,
                        uint8_t                         *nxp_nfc_fw,
                        uint32_t                         fw_length,
#endif
                        pphNfcIF_Notification_CB_t      upgrade_complete,
                        void                            *context
                 )
 {
    phDnldNfc_sContext_t    *psDnldContext = NULL;
    phNfcIF_sReference_t    dnldReference = { NULL,0,0 };
    phNfcIF_sCallBack_t     if_callback = { NULL, NULL, NULL, NULL };
    phNfc_sLowerIF_t        *plower_if = NULL;
    NFCSTATUS                status = NFCSTATUS_SUCCESS;

    if( (NULL == pHwRef)
      )
    {
        status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        DNLD_PRINT(" FW_DNLD: Starting the FW Upgrade Sequence .... \n");

        (void)
             phDnldNfc_Allocate_Resource((void **)
                              &psDnldContext,sizeof(phDnldNfc_sContext_t));
        if(psDnldContext != NULL)
        {
#ifndef NFC_TIMER_CONTEXT
            gpphDnldContext = psDnldContext;
#endif
            psDnldContext->p_hw_ref = pHwRef;
            psDnldContext->timer_id = NXP_INVALID_TIMER_ID;

            DNLD_PRINT(" FW_DNLD: Initialisation in Progress.... \n");

            if_callback.pif_ctxt = psDnldContext ;
            if_callback.send_complete = &phDnldNfc_Send_Complete;
            if_callback.receive_complete= &phDnldNfc_Receive_Complete;
            /* if_callback.notify = &phDnldNfc_Notify_Event; */
            plower_if = dnldReference.plower_if = &(psDnldContext->lower_interface);
            status = phDal4Nfc_Register(&dnldReference, if_callback,
                                    NULL);
            DNLD_DEBUG(" FW_DNLD: Lower Layer Register, Status = %02X\n",status);

            if(  (NFCSTATUS_SUCCESS == status) && (NULL != plower_if->init))
            {
                /* psDnldContext->p_config_params = pHwConfig ; */
                status = plower_if->init((void *)plower_if->pcontext,
                                        (void *)pHwRef);
                DNLD_DEBUG(" FW_DNLD: Lower Layer Initialisation, Status = %02X\n",status);
            }
            else
            {
                /* TODO: Handle Initialisation in the Invalid State */
            }
            /* The Lower layer Initialisation successful */
            if (NFCSTATUS_SUCCESS == status)
            {
                psDnldContext->p_upper_notify = upgrade_complete;
                psDnldContext->p_upper_context = context;

                status = phDnldNfc_Process_FW( psDnldContext, pHwRef
#ifdef NXP_FW_PARAM
                ,*nxp_nfc_fw , fw_length
#endif
                 );

                if (NFCSTATUS_SUCCESS == status)
                {
                    status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                            PHDNLD_CMD_RESET , NULL , 0 );
                    if (NFCSTATUS_PENDING == status)
                    {
                        DNLD_PRINT("\n FW_DNLD: Initial Reset .... \n");

#if defined(FW_DOWNLOAD_TIMER) 

                        psDnldContext->timer_id = phOsalNfc_Timer_Create( );

#if (FW_DOWNLOAD_TIMER < 2)
                        phOsalNfc_Timer_Start( psDnldContext->timer_id,
                                NXP_DNLD_COMPLETE_TIMEOUT,
                                (ppCallBck_t) phDnldNfc_Abort
#ifdef NFC_TIMER_CONTEXT
                                , (void *) psDnldContext
#endif
                                );

#endif  /* #if (FW_DOWNLOAD_TIMER < 2) */

#endif /* #if defined(FW_DOWNLOAD_TIMER)  */

                    }
                }
                else if (NFCSTATUS_SUCCESS == PHNFCSTATUS(status))
                {
#if  defined (NXP_FW_INTEGRITY_VERIFY)
                    /* 
                     * To check for the integrity if the firmware is already 
                     * Upgraded.
                     */
                    status = phDnldNfc_Send_Command( psDnldContext, pHwRef,
                            PHDNLD_CMD_RESET , NULL , 0 );
                    if (NFCSTATUS_PENDING == status)
                    {
                        DNLD_PRINT("\n FW_DNLD: Integrity Reset .... \n");
                        (void)phDnldNfc_Set_Seq(psDnldContext, DNLD_SEQ_COMPLETE);
                        status = PHNFCSTVAL( CID_NFC_DNLD, 
                                        NFCSTATUS_PENDING );
#if defined(FW_DOWNLOAD_TIMER) 
                        psDnldContext->timer_id = phOsalNfc_Timer_Create( );
#if (FW_DOWNLOAD_TIMER < 2)
                        phOsalNfc_Timer_Start( psDnldContext->timer_id,
                                NXP_DNLD_COMPLETE_TIMEOUT, 
                                (ppCallBck_t) phDnldNfc_Abort
#ifdef NFC_TIMER_CONTEXT
                                , (void *) psDnldContext
#endif
                                );

#endif  /* #if (FW_DOWNLOAD_TIMER < 2) */

#endif /* #if defined(FW_DOWNLOAD_TIMER)  */
                    }

#else
                    status = NFCSTATUS_SUCCESS;

#endif /* #if  defined (NXP_FW_INTEGRITY_VERIFY) */

                }
                else
                {
                    DNLD_PRINT(" FW_DNLD Initialisation in Failed \n");
                }
            }

            if (NFCSTATUS_PENDING != PHNFCSTATUS(status))
            {
                (void)phDal4Nfc_Unregister(
                            psDnldContext->lower_interface.pcontext, pHwRef);
                phDnldNfc_Release_Lower(psDnldContext, pHwRef);
                phDnldNfc_Release_Resources(&psDnldContext);
#ifndef NFC_TIMER_CONTEXT
                gpphDnldContext = psDnldContext;
#endif
            }
        } /* End of Status Check for Memory */
        else
        {
            status = PHNFCSTVAL(CID_NFC_DNLD, NFCSTATUS_INSUFFICIENT_RESOURCES);

            DNLD_PRINT(" FW_DNLD: Memory Allocation of Context Failed\n");
        }
    }

    return status;
 }
