/*
 * 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 phLibNfc_Target.c

 * Project: NFC FRI 1.1
 *
 * $Date: Thu Oct 15 15:24:43 2009 $
 * $Author: ing07299 $
 * $Revision: 1.12 $
 * $Aliases: NFC_FRI1.1_WK943_R32_1,NFC_FRI1.1_WK944_SDK,NFC_FRI1.1_WK949_PREP1,NFC_FRI1.1_WK949_SDK_INT,NFC_FRI1.1_WK943_R32_10,NFC_FRI1.1_WK943_R32_13,NFC_FRI1.1_WK1003_SDK,NFC_FRI1.1_WK943_R32_14,NFC_FRI1.1_WK1007_R33_1,NFC_FRI1.1_WK1008_SDK,NFC_FRI1.1_WK1007_R33_4,NFC_FRI1.1_WK1007_SDK,NFC_FRI1.1_WK1014_SDK,NFC_FRI1.1_WK1017_PREP1,NFC_FRI1.1_WK1017_R34_1,NFC_FRI1.1_WK1017_R34_2,NFC_FRI1.1_WK1019_SDK,NFC_FRI1.1_WK1024_SDK $
 *
 */

/*
************************* Header Files ***************************************
*/

#include <phLibNfcStatus.h>
#include <phLibNfc.h>
#include <phHal4Nfc.h>
#include <phOsalNfc.h>
#include <phLibNfc_Internal.h>
#include <phLibNfc_ndef_raw.h>
#include <phLibNfc_initiator.h>
#include <phLibNfc_discovery.h>

/*
*************************** Macro's  ****************************************
*/

#ifndef STATIC_DISABLE
#define STATIC static
#else
//#undef STATIC
#define STATIC 
#endif
/*
*************************** Global Variables **********************************
*/

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

/* Remote device receive callback */
STATIC void phLibNfc_RemoteDev_Receive_Cb(
                                void            *context,
                                phNfc_sData_t   *rec_rsp_data, 
                                NFCSTATUS       status
                                );

/* Remote device Send callback */
STATIC void phLibNfc_RemoteDev_Send_Cb(
                                void        *Context,
                                NFCSTATUS   status
                                );

/*
*************************** Function Definitions ******************************
*/

/**
* Interface used to receive data from initiator at target side during P2P
* communication.
*/
NFCSTATUS phLibNfc_RemoteDev_Receive(phLibNfc_Handle       hRemoteDevice,  
                                pphLibNfc_Receive_RspCb_t  pReceiveRspCb,  
                                void                       *pContext
                                ) 
{
    NFCSTATUS RetVal = NFCSTATUS_FAILED;
    /*Check Lib Nfc is initialized*/
    if((NULL == gpphLibContext)|| 
        (gpphLibContext->LibNfcState.cur_state == eLibNfcHalStateShutdown))
    {
        RetVal = NFCSTATUS_NOT_INITIALISED;
    }/*Check application has sent valid parameters*/
    else if (gpphLibContext->LibNfcState.cur_state == eLibNfcHalStateRelease)
    {
        RetVal = NFCSTATUS_DESELECTED;
    }
    else if((NULL == pReceiveRspCb)
        || (NULL == pContext)
        || (0 == hRemoteDevice))
    {
        RetVal= NFCSTATUS_INVALID_PARAMETER;
    }   
    else if(gpphLibContext->LibNfcState.next_state == eLibNfcHalStateShutdown)
    {
        RetVal = NFCSTATUS_SHUTDOWN;
    }
    else if((TRUE == gpphLibContext->status.GenCb_pending_status)       
        ||(NULL!=gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb)
        ||(phHal_eNfcIP1_Target==
        ((phHal_sRemoteDevInformation_t*)hRemoteDevice)->RemDevType))
    {
        /*Previous callback is pending or if initiator uses this api */
        RetVal = NFCSTATUS_REJECTED;
    }/*check for Discovered initiator handle and handle sent by application */
    else if(gpphLibContext->sNfcIp_Context.Rem_Initiator_Handle != hRemoteDevice)
    {
        RetVal= NFCSTATUS_INVALID_DEVICE;
    }
#ifdef LLCP_TRANSACT_CHANGES
    else if ((LLCP_STATE_RESET_INIT != gpphLibContext->llcp_cntx.sLlcpContext.state)
            && (LLCP_STATE_CHECKED != gpphLibContext->llcp_cntx.sLlcpContext.state))
    {
        RetVal = NFCSTATUS_BUSY;
    }
#endif /* #ifdef LLCP_TRANSACT_CHANGES */
    else
    {
        if(eLibNfcHalStatePresenceChk ==
                gpphLibContext->LibNfcState.next_state)
        {
            gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb = NULL;
            RetVal = NFCSTATUS_PENDING;
        }
        else
        {
            /*Call below layer receive and register the callback with it*/
            PHDBG_INFO("LibNfc:P2P Receive In Progress");
            RetVal =phHal4Nfc_Receive(                                          
                            gpphLibContext->psHwReference,
                            (phHal4Nfc_TransactInfo_t*)gpphLibContext->psTransInfo,
                            (pphLibNfc_Receive_RspCb_t)
                            phLibNfc_RemoteDev_Receive_Cb,
                            (void *)gpphLibContext
                            );  
        }   
        if(NFCSTATUS_PENDING == RetVal)
        {
            /*Update the Next state as Transaction*/
            gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb= pReceiveRspCb;
            gpphLibContext->sNfcIp_Context.pClientNfcIpRxCntx = pContext;
            gpphLibContext->status.GenCb_pending_status=TRUE;
            gpphLibContext->LibNfcState.next_state = eLibNfcHalStateTransaction;
        }
        else
        {
            RetVal = NFCSTATUS_FAILED;
        }       
    }
    return RetVal;
}
/**
* Response callback for Remote Device Receive.
*/
STATIC void phLibNfc_RemoteDev_Receive_Cb(
                                    void            *context,
                                    phNfc_sData_t   *rec_rsp_data, 
                                    NFCSTATUS       status
                                    )
{
    pphLibNfc_Receive_RspCb_t       pClientCb=NULL;
    
    phLibNfc_LibContext_t   *pLibNfc_Ctxt = (phLibNfc_LibContext_t *)context;
    void                    *pUpperLayerContext=NULL;

    /* Check for the context returned by below layer */
    if(pLibNfc_Ctxt != gpphLibContext)
    {   /*wrong context returned*/
        phOsalNfc_RaiseException(phOsalNfc_e_InternalErr,1);
    }
    else
    {
        pClientCb = gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb;
        pUpperLayerContext = gpphLibContext->sNfcIp_Context.pClientNfcIpRxCntx;

        gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb = NULL;
        gpphLibContext->sNfcIp_Context.pClientNfcIpRxCntx = NULL;
        gpphLibContext->status.GenCb_pending_status = FALSE;
        if(eLibNfcHalStateShutdown == gpphLibContext->LibNfcState.next_state)
        {   /*shutdown called before completion of P2P receive allow
              shutdown to happen */
            phLibNfc_Pending_Shutdown();
            status = NFCSTATUS_SHUTDOWN;    
        }
        else if(eLibNfcHalStateRelease == gpphLibContext->LibNfcState.next_state)
        {
            status = NFCSTATUS_ABORTED;
        }
        else
        {
            if((NFCSTATUS_SUCCESS != status) && 
                (PHNFCSTATUS(status) != NFCSTATUS_MORE_INFORMATION ) )
            {
                /*During p2p receive operation initiator was removed
                from RF field of target*/
                status = NFCSTATUS_DESELECTED;
            }
            else
            {
                status = NFCSTATUS_SUCCESS;
            }
        }   
        /* Update current state */
        phLibNfc_UpdateCurState(status,gpphLibContext);
               
        if (NULL != pClientCb)
        {
            /*Notify to upper layer status and No. of bytes
             actually received */
            pClientCb(pUpperLayerContext, rec_rsp_data, status);          
        }
    }
    return;
}

/**
* Interface used to send data from target to initiator during P2P communication
*/
NFCSTATUS 
phLibNfc_RemoteDev_Send(
                        phLibNfc_Handle      hRemoteDevice,  
                        phNfc_sData_t *      pTransferData,  
                        pphLibNfc_RspCb_t    pSendRspCb,  
                        void                 *pContext
                        )
{
    NFCSTATUS RetVal = NFCSTATUS_FAILED;
    /*Check Lib Nfc stack is initilized*/
    if((NULL == gpphLibContext)|| 
        (gpphLibContext->LibNfcState.cur_state == eLibNfcHalStateShutdown))
    {
        RetVal = NFCSTATUS_NOT_INITIALISED;
    }
    else if (gpphLibContext->LibNfcState.cur_state == eLibNfcHalStateRelease)
    {
        RetVal = NFCSTATUS_DESELECTED;
    }
    /*Check application has sent the valid parameters*/
    else if((NULL == pTransferData)
        || (NULL == pSendRspCb)
        || (NULL == pTransferData->buffer)
        || (0 == pTransferData->length)
        || (NULL == pContext)
        || (0 == hRemoteDevice))
    {
        RetVal= NFCSTATUS_INVALID_PARAMETER;
    }   
    else if(gpphLibContext->LibNfcState.next_state == eLibNfcHalStateShutdown)
    {
        RetVal = NFCSTATUS_SHUTDOWN;
    }
	else if((TRUE == gpphLibContext->status.GenCb_pending_status)       
        ||(NULL!=gpphLibContext->sNfcIp_Context.pClientNfcIpRxCb)
        ||(phHal_eNfcIP1_Target==
        ((phHal_sRemoteDevInformation_t*)hRemoteDevice)->RemDevType))
    {
        /*Previous callback is pending or local device is Initiator
        then don't allow */
        RetVal = NFCSTATUS_REJECTED;
    }/*Check for Discovered initiator handle and handle sent by application */
    else if(gpphLibContext->sNfcIp_Context.Rem_Initiator_Handle != hRemoteDevice)
    {
        RetVal= NFCSTATUS_INVALID_DEVICE;
    }
    else if((NULL!=gpphLibContext->sNfcIp_Context.pClientNfcIpTxCb))
    {
        RetVal =NFCSTATUS_BUSY ;
    }
#ifdef LLCP_TRANSACT_CHANGES
    else if ((LLCP_STATE_RESET_INIT != gpphLibContext->llcp_cntx.sLlcpContext.state)
            && (LLCP_STATE_CHECKED != gpphLibContext->llcp_cntx.sLlcpContext.state))
    {
        RetVal= NFCSTATUS_BUSY;
    }
#endif /* #ifdef LLCP_TRANSACT_CHANGES */
    else
    {
        if(eLibNfcHalStatePresenceChk ==
                gpphLibContext->LibNfcState.next_state)
        {
            gpphLibContext->sNfcIp_Context.pClientNfcIpTxCb = NULL;
            RetVal = NFCSTATUS_PENDING;
        }
        else
        {
            if(gpphLibContext->psTransInfo!=NULL)
            {
                (void)memset(gpphLibContext->psTransInfo,
                                0,
                                sizeof(phLibNfc_sTransceiveInfo_t));                
            
                gpphLibContext->psTransInfo->addr =UNKNOWN_BLOCK_ADDRESS;
                /*pointer to send data */
                gpphLibContext->psTransInfo->sSendData.buffer = 
                                                    pTransferData->buffer;   
                /*size of send data*/
                gpphLibContext->psTransInfo->sSendData.length = 
                                                    pTransferData->length;   

                /* Copy remote device type */
                gpphLibContext->sNfcIp_Context.TransactInfoRole.remotePCDType =
                    ((phHal_sRemoteDevInformation_t*)hRemoteDevice)->RemDevType;
                /*Call Hal4 Send API and register callback with it*/
                PHDBG_INFO("LibNfc:P2P send In Progress");
                RetVal= phHal4Nfc_Send(                                          
                                gpphLibContext->psHwReference,
                                &(gpphLibContext->sNfcIp_Context.TransactInfoRole),
                                gpphLibContext->psTransInfo->sSendData,
                                (pphLibNfc_RspCb_t)
                                phLibNfc_RemoteDev_Send_Cb,
                                (void *)gpphLibContext
                                ); 
            }
        }
        if(NFCSTATUS_PENDING == RetVal)
        {
            /* Update next state to transaction */
            gpphLibContext->sNfcIp_Context.pClientNfcIpTxCb= pSendRspCb;
            gpphLibContext->sNfcIp_Context.pClientNfcIpTxCntx = pContext;
            gpphLibContext->status.GenCb_pending_status=TRUE;
            gpphLibContext->LibNfcState.next_state = eLibNfcHalStateTransaction;
        }
        else
        {
            RetVal = NFCSTATUS_FAILED;
        }
    }
    return RetVal;
}

/*
* Response callback for Remote Device Send.
*/
STATIC void phLibNfc_RemoteDev_Send_Cb(
                            void        *Context,
                            NFCSTATUS   status
                            )
{
    pphLibNfc_RspCb_t       pClientCb=NULL;
    phLibNfc_LibContext_t   *pLibNfc_Ctxt = (phLibNfc_LibContext_t *)Context;
    void                    *pUpperLayerContext=NULL;

    /* Check for the context returned by below layer */
    if(pLibNfc_Ctxt != gpphLibContext)
    {   /*wrong context returned*/
        phOsalNfc_RaiseException(phOsalNfc_e_InternalErr,1);
    }
    else
    {
        if(eLibNfcHalStateShutdown == gpphLibContext->LibNfcState.next_state)
        {   /*shutdown called before completion p2p send allow
              shutdown to happen */
            phLibNfc_Pending_Shutdown();
            status = NFCSTATUS_SHUTDOWN;    
        }
        else if(eLibNfcHalStateRelease == gpphLibContext->LibNfcState.next_state)
        {
            status = NFCSTATUS_ABORTED;
        }
        else
        {
            gpphLibContext->status.GenCb_pending_status = FALSE;
            if((NFCSTATUS_SUCCESS != status) && 
                (PHNFCSTATUS(status) != NFCSTATUS_MORE_INFORMATION ) )
            {
                /*During p2p send operation initator was not present in RF
                field of target*/
                status = NFCSTATUS_DESELECTED;
            }
            else
            {
                status = NFCSTATUS_SUCCESS;
            }
        }
        /* Update current state */
        phLibNfc_UpdateCurState(status,gpphLibContext);

        pClientCb = gpphLibContext->sNfcIp_Context.pClientNfcIpTxCb;
        pUpperLayerContext = gpphLibContext->sNfcIp_Context.pClientNfcIpTxCntx;

        gpphLibContext->sNfcIp_Context.pClientNfcIpTxCb = NULL;
        gpphLibContext->sNfcIp_Context.pClientNfcIpTxCntx = NULL;
        if (NULL != pClientCb)
        {
            /* Notify to upper layer status and No. of bytes
             actually written or send to initiator */
            pClientCb(pUpperLayerContext, status);          
        }
    }
    return;
}