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

 * Project: NFC FRI 1.1
 *
 * $Date: Mon Mar  1 19:02:41 2010 $
 * $Author: ing07385 $
 * $Revision: 1.36 $
 * $Aliases: 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
#define STATIC
#endif

/*
*************************** Global Variables **********************************
*/



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


/*Remote device Presence check callback*/
STATIC void phLibNfc_RemoteDev_CheckPresence_Cb(void  *context,
                                NFCSTATUS status);

/**Used for presence chk incase of mifare std tags*/
STATIC void phLibNfc_ChkPresence_Trcv_Cb(
                            void *context,
                            phHal_sRemoteDevInformation_t *psRemoteDevInfo,
                            phNfc_sData_t *response, 
                            NFCSTATUS status
                            );

/*
*************************** Function Definitions ******************************
*/
void phLibNfc_config_discovery_cb(void     *context,
                                  NFCSTATUS status)
{
    
    if((phLibNfc_LibContext_t *)context == gpphLibContext)      
    {   /*check for same context*/
        
        if(eLibNfcHalStateShutdown == gpphLibContext->LibNfcState.next_state)
        {
            /*If shutdown called in between allow shutdown to happen*/
            phLibNfc_Pending_Shutdown();
            status = NFCSTATUS_SHUTDOWN;
        }
        else
        {
            gpphLibContext->status.GenCb_pending_status = FALSE;
            gpphLibContext->status.DiscEnbl_status = FALSE;
            phLibNfc_UpdateCurState(status,gpphLibContext);
#ifdef RESTART_CFG
            if(gpphLibContext->status.Discovery_pending_status == TRUE)
            {
                NFCSTATUS RetStatus = NFCSTATUS_FAILED;
                /* Application has called discovery before receiving this callback,
                so NO notification to the upper layer, instead lower layer
                discovery is called */
                gpphLibContext->status.Discovery_pending_status = FALSE;
                RetStatus =  phHal4Nfc_ConfigureDiscovery(
                        gpphLibContext->psHwReference,
                        gpphLibContext->eLibNfcCfgMode,
                        &gpphLibContext->sADDconfig,
                        (pphLibNfc_RspCb_t)
                        phLibNfc_config_discovery_cb,
                        (void *)gpphLibContext);
                if (NFCSTATUS_PENDING == RetStatus)
                {
                    (void)phLibNfc_UpdateNextState(gpphLibContext,
                                            eLibNfcHalStateConfigReady);
                    gpphLibContext->status.GenCb_pending_status = TRUE;
                    gpphLibContext->status.DiscEnbl_status = TRUE;
                }
                else
                {
                    status = NFCSTATUS_FAILED;
                }
            }
#endif /* #ifdef RESTART_CFG */
        }
    } /*End of if-context check*/
    else
    {   /*exception: wrong context pointer returned*/
        phOsalNfc_RaiseException(phOsalNfc_e_InternalErr,1);
        status = NFCSTATUS_FAILED;
    }
    if(gpphLibContext->CBInfo.pClientDisConfigCb!=NULL)
    {
        gpphLibContext->CBInfo.pClientDisConfigCb(gpphLibContext->CBInfo.pClientDisCfgCntx,status);
        gpphLibContext->CBInfo.pClientDisConfigCb=NULL;
    }
    return;
}
/**
* Configure Discovery Modes.
* This function is used to configure ,start and stop the discovery wheel.
*/
NFCSTATUS phLibNfc_Mgt_ConfigureDiscovery (
                        phLibNfc_eDiscoveryConfigMode_t DiscoveryMode,   
                        phLibNfc_sADD_Cfg_t             sADDSetup,
                        pphLibNfc_RspCb_t               pConfigDiscovery_RspCb,
                        void*                           pContext
                        )
 {
    NFCSTATUS RetVal = NFCSTATUS_FAILED;
    phHal_sADD_Cfg_t           *psADDConfig;
    psADDConfig = (phHal_sADD_Cfg_t *)&(sADDSetup);

    
   if((NULL == gpphLibContext) ||
        (gpphLibContext->LibNfcState.cur_state
                            == eLibNfcHalStateShutdown))
    {
        /*Lib Nfc not initialized*/
        RetVal = NFCSTATUS_NOT_INITIALISED;
    }
    /* Check for Valid parameters*/
    else if((NULL == pContext) || (NULL == pConfigDiscovery_RspCb))
    {
        RetVal= NFCSTATUS_INVALID_PARAMETER;
    }
    else if(gpphLibContext->LibNfcState.next_state
                            == eLibNfcHalStateShutdown)
    {
        RetVal= NFCSTATUS_SHUTDOWN;
    }    
    else
    {
        gpphLibContext->eLibNfcCfgMode =DiscoveryMode;
        gpphLibContext->sADDconfig = sADDSetup;
        if(gpphLibContext->status.DiscEnbl_status != TRUE)
        {
            
            /* call lower layer config API for the discovery 
            configuration sent by the application */
            RetVal = phHal4Nfc_ConfigureDiscovery ( gpphLibContext->psHwReference,
                                        DiscoveryMode,
                                        psADDConfig,
                                        (pphLibNfc_RspCb_t)
                                        phLibNfc_config_discovery_cb,
                                        (void*)gpphLibContext);
            if(PHNFCSTATUS(RetVal) == NFCSTATUS_PENDING)
            {
                gpphLibContext->status.DiscEnbl_status = TRUE;
                /* Copy discovery callback and its context */
                gpphLibContext->CBInfo.pClientDisConfigCb = pConfigDiscovery_RspCb;
                gpphLibContext->CBInfo.pClientDisCfgCntx = pContext;
                gpphLibContext->status.GenCb_pending_status = TRUE;
				gpphLibContext->LibNfcState.next_state = eLibNfcHalStateConfigReady;                
            }
            else
            {
                RetVal=NFCSTATUS_FAILED;
            }

        }
        else
        {
            RetVal=NFCSTATUS_BUSY;            
        }
    }
    return RetVal;
 }

/**
* Check for target presence.
* Checks given target is present in RF filed or not
*/
NFCSTATUS phLibNfc_RemoteDev_CheckPresence( phLibNfc_Handle     hTargetDev,
                                            pphLibNfc_RspCb_t   pPresenceChk_RspCb,
                                            void*               pRspCbCtx
                                           )
{
    NFCSTATUS RetVal = NFCSTATUS_FAILED;
    phHal_sRemoteDevInformation_t *ps_rem_dev_info = NULL;
    /* Check for valid sate */
    if((NULL == gpphLibContext) ||
        (gpphLibContext->LibNfcState.cur_state
                            == eLibNfcHalStateShutdown))
    {
        RetVal = NFCSTATUS_NOT_INITIALISED;
    }
    /* Check for valid parameters*/
    else if((NULL == pRspCbCtx) || (NULL == pPresenceChk_RspCb)
        || (hTargetDev == 0) )
    {
        RetVal= NFCSTATUS_INVALID_PARAMETER;
    }
    /* Check for DeInit call*/
    else if(gpphLibContext->LibNfcState.next_state
                            == eLibNfcHalStateShutdown)
    {
        RetVal = NFCSTATUS_SHUTDOWN;
    }
    /* Check target is connected or not */
    else if( gpphLibContext->Connected_handle == 0)
    {
        RetVal = NFCSTATUS_TARGET_NOT_CONNECTED;
    }
    /* Check given handle is valid or not*/
    else if(hTargetDev != gpphLibContext->Connected_handle)
    {
        RetVal = NFCSTATUS_INVALID_HANDLE;
    }
#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
    {
        ps_rem_dev_info = (phHal_sRemoteDevInformation_t *)
                                    gpphLibContext->Connected_handle;
        if((phHal_eMifare_PICC == ps_rem_dev_info->RemDevType)
            &&(0 != ps_rem_dev_info->RemoteDevInfo.Iso14443A_Info.Sak)
            &&(TRUE == gpphLibContext->LastTrancvSuccess))
        {
            /* Call HAL4 API */
            RetVal =  phHal4Nfc_Transceive(
                                gpphLibContext->psHwReference,
                                gpphLibContext->psBufferedAuth,
                                (phHal_sRemoteDevInformation_t *)
                                    gpphLibContext->Connected_handle,
                                (pphHal4Nfc_TransceiveCallback_t )
                                    phLibNfc_ChkPresence_Trcv_Cb,
                                (void *)gpphLibContext
                                );
                
        }
        else
        {
            /* Call lower layer PresenceCheck function */
            RetVal = phHal4Nfc_PresenceCheck(gpphLibContext->psHwReference,
                                        phLibNfc_RemoteDev_CheckPresence_Cb,
                                        (void *)gpphLibContext);
        }
        if( NFCSTATUS_PENDING == PHNFCSTATUS(RetVal))
        {
            gpphLibContext->CBInfo.pClientPresChkCb = pPresenceChk_RspCb;
            gpphLibContext->CBInfo.pClientPresChkCntx = pRspCbCtx;
            /* Mark General callback pending status as TRUE*/
            gpphLibContext->status.GenCb_pending_status = TRUE;

            /* Update the state machine*/
			gpphLibContext->LibNfcState.next_state = eLibNfcHalStatePresenceChk;           
        }
        else /* If return value is internal error(other than pending ) return NFCSTATUS_FAILED*/
        {
          RetVal = NFCSTATUS_FAILED;
        }
    }
    return RetVal;
}

/**
* Response Callback for Remote device Presence Check.
*/
STATIC
void phLibNfc_RemoteDev_CheckPresence_Cb(void     *context,
                                        NFCSTATUS status)
{
    void                    *pUpperLayerContext=NULL;
    pphLibNfc_RspCb_t       pClientCb=NULL;

    /*check valid context is returned or not*/
    if((phLibNfc_LibContext_t *)context != gpphLibContext)
    {
        /*exception: wrong context pointer returned*/
        phOsalNfc_RaiseException(phOsalNfc_e_InternalErr,1);
    }    
    /* Mark general callback pending status as FALSE*/
    gpphLibContext->status.GenCb_pending_status = FALSE;
    pClientCb =gpphLibContext->CBInfo.pClientPresChkCb ;
    pUpperLayerContext = gpphLibContext->CBInfo.pClientPresChkCntx;
    gpphLibContext->CBInfo.pClientPresChkCntx = NULL;
    gpphLibContext->CBInfo.pClientPresChkCb =NULL;
    /* Check DeInit call is called, if yes call pending 
    shutdown and return NFCSTATUS_SHUTDOWN */
    if(eLibNfcHalStateShutdown == gpphLibContext->LibNfcState.next_state)
    {
        phLibNfc_Pending_Shutdown();
        status = NFCSTATUS_SHUTDOWN;    
    }
    else
    {
        if (status != NFCSTATUS_SUCCESS)
        {
           /*If status is other than SUCCESS (Internal error) return
           NFCSTATUS_TARGET_LOST */
            status= NFCSTATUS_TARGET_LOST;
        }   
        else
        {
            status = NFCSTATUS_SUCCESS;
        }
    }
     /* Update the current state */
    phLibNfc_UpdateCurState(status,gpphLibContext);
    if(NULL != pClientCb)
    {
        /* call the upper layer callback */
        pClientCb(pUpperLayerContext,status);
    }
    return;
}

/**Used for presence chk incase of mifare std tags*/
STATIC void phLibNfc_ChkPresence_Trcv_Cb(
                            void *context,
                            phHal_sRemoteDevInformation_t *psRemoteDevInfo,
                            phNfc_sData_t *response, 
                            NFCSTATUS status
                            )
{
    PHNFC_UNUSED_VARIABLE(psRemoteDevInfo);
    PHNFC_UNUSED_VARIABLE(response);
    phLibNfc_RemoteDev_CheckPresence_Cb(context,status);
    return;
}