/*
 * 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 phDalNfc.c
 * \brief DAL Implementation for linux
 *
 * Project: Trusted NFC Linux Lignt
 *
 * $Date: 07 aug 2009
 * $Author: Jonathan roux
 * $Revision: 1.0 $
 *
 */

#define _DAL_4_NFC_C

#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#ifdef ANDROID
#include <linux/ipc.h>
#include <cutils/log.h>
#include <cutils/properties.h> // for property_get
#else
#include <sys/msg.h>
#endif
#include <semaphore.h>
#include <phDal4Nfc.h>
#include <phOsalNfc.h>
#include <phNfcStatus.h>
#include <phDal4Nfc_DeferredCall.h>
#include <phDal4Nfc_debug.h>
#include <phDal4Nfc_uart.h>
#include <phDal4Nfc_i2c.h>
#include <phDal4Nfc_link.h>
#include <phDal4Nfc_messageQueueLib.h>
#include <hardware/hardware.h>
#include <hardware/nfc.h>


/*-----------------------------------------------------------------------------------
                                       TYPES
------------------------------------------------------------------------------------*/
/*structure holds members related for both read and write operations*/
typedef struct Dal_RdWr_st
{
    /* Read members */
    pthread_t             nReadThread;             /* Read thread Hanlde */
    uint8_t *             pReadBuffer;             /* Read local buffer */
    int                   nNbOfBytesToRead;        /* Number of bytes to read */
    int                   nNbOfBytesRead;          /* Number of read bytes */
    char                  nReadBusy;               /* Read state machine */
    char                  nReadThreadAlive;        /* Read state machine */
    char                  nWaitingOnRead;          /* Read state machine */

    /* Read wait members */
    uint8_t *             pReadWaitBuffer;         /* Read wait local Buffer */
    int                   nNbOfBytesToReadWait;    /* Number of bytes to read */
    int                   nNbOfBytesReadWait;      /* Number of read bytes */
    char                  nReadWaitBusy;           /* Read state machine */
    char                  nWaitingOnReadWait;      /* Read state machine */
    char                  nCancelReadWait;         /* Read state machine */

    /* Write members */
    pthread_t             nWriteThread;            /* Write thread Hanlde */
    uint8_t *             pWriteBuffer;            /* Write local buffer */
    uint8_t *             pTempWriteBuffer;        /* Temp Write local buffer */
    int                   nNbOfBytesToWrite;       /* Number of bytes to write */
    int                   nNbOfBytesWritten;       /* Number of bytes written */
    char                  nWaitingOnWrite;         /* Write state machine */
    char                  nWriteThreadAlive;       /* Write state machine */
    char                  nWriteBusy;              /* Write state machine */
} phDal4Nfc_RdWr_t;

typedef void   (*pphDal4Nfc_DeferFuncPointer_t) (void * );
typedef void * (*pphDal4Nfc_thread_handler_t)   (void * pParam);


/*-----------------------------------------------------------------------------------
                                      VARIABLES
------------------------------------------------------------------------------------*/
static phDal4Nfc_RdWr_t               gReadWriteContext;
static phDal4Nfc_SContext_t           gDalContext;
static pphDal4Nfc_SContext_t          pgDalContext;
static phHal_sHwReference_t   *       pgDalHwContext;
static sem_t                          nfc_read_sem;
static int                            low_level_traces;
#ifdef USE_MQ_MESSAGE_QUEUE
static phDal4Nfc_DeferredCall_Msg_t   nDeferedMessage;
static mqd_t                          nDeferedCallMessageQueueId;

#else
int                            nDeferedCallMessageQueueId = 0;
#endif
static phDal4Nfc_link_cbk_interface_t gLinkFunc;
/*-----------------------------------------------------------------------------------
                                     PROTOTYPES
------------------------------------------------------------------------------------*/
static void      phDal4Nfc_DeferredCb     (void  *params);
static NFCSTATUS phDal4Nfc_StartThreads   (void);
static void      phDal4Nfc_FillMsg        (phDal4Nfc_Message_t *pDalMsg, phOsalNfc_Message_t *pOsalMsg);

/*-----------------------------------------------------------------------------------
                                DAL API IMPLEMENTATION
------------------------------------------------------------------------------------*/

static void refresh_low_level_traces() {
#ifdef LOW_LEVEL_TRACES
    low_level_traces = 1;
    return;
#else

#ifdef ANDROID
    char value[PROPERTY_VALUE_MAX];

    property_get("ro.debuggable", value, "");
    if (!value[0] || !atoi(value)) {
        low_level_traces = 0;  // user build, do not allow debug
        return;
    }

    property_get("debug.nfc.LOW_LEVEL_TRACES", value, "0");
    if (value[0]) {
        low_level_traces = atoi(value);
        return;
    }
#endif
    low_level_traces = 0;
#endif
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Register

PURPOSE:  DAL register function.

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Register( phNfcIF_sReference_t  *psRefer,
                              phNfcIF_sCallBack_t if_cb, void *psIFConf )
{
    NFCSTATUS               result = NFCSTATUS_SUCCESS;

    if ((NULL != psRefer) &&
        (NULL != psRefer->plower_if) &&
        (NULL != if_cb.receive_complete) &&
        (NULL != if_cb.send_complete)
        )
    {
        /* Register the LLC functions to the upper layer */
        psRefer->plower_if->init           = phDal4Nfc_Init;
        psRefer->plower_if->release        = phDal4Nfc_Shutdown;
        psRefer->plower_if->send           = phDal4Nfc_Write;
        psRefer->plower_if->receive        = phDal4Nfc_Read;
        psRefer->plower_if->receive_wait   = phDal4Nfc_ReadWait;
        psRefer->plower_if->transact_abort = phDal4Nfc_ReadWaitCancel;
        psRefer->plower_if->unregister     = phDal4Nfc_Unregister;


        if (NULL != pgDalContext)
        {
            /* Copy the DAL context to the upper layer */
            psRefer->plower_if->pcontext = pgDalContext;
            /* Register the callback function from the upper layer */
            pgDalContext->cb_if.receive_complete = if_cb.receive_complete;
            pgDalContext->cb_if.send_complete = if_cb.send_complete;
            pgDalContext->cb_if.notify = if_cb.notify;
            /* Get the upper layer context */
            pgDalContext->cb_if.pif_ctxt = if_cb.pif_ctxt;
            /* Update the error state */
            result = NFCSTATUS_SUCCESS;
        }
        else
        {
            result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_NOT_INITIALISED);
        }
    }
    else /*Input parameters invalid*/
    {
        result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_PARAMETER);
    }
    return result;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Unregister

PURPOSE:  DAL unregister function.

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Unregister(void *pContext, void *pHwRef )
{
    NFCSTATUS               result = NFCSTATUS_SUCCESS;

    if ((NULL == pContext) && (NULL == pHwRef))
    {
        result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_PARAMETER);
    }
    else
    {
        if (NULL != pgDalContext)
        {
            /* Register the callback function from the upper layer */
            pgDalContext->cb_if.receive_complete = NULL;
            pgDalContext->cb_if.send_complete = NULL ;
            pgDalContext->cb_if.notify = NULL ;
            /* Get the upper layer context */
            pgDalContext->cb_if.pif_ctxt =  NULL ;
            
        }
        else
        {
            result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_NOT_INITIALISED);
        }
    }
    return result;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Init

PURPOSE:  DAL Init function.

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Init(void *pContext, void *pHwRef )
{
    NFCSTATUS        result = NFCSTATUS_SUCCESS;

    refresh_low_level_traces();

    if ((NULL != pContext) && (NULL != pHwRef))
    {
        pContext  = pgDalContext;
        pgDalHwContext = (phHal_sHwReference_t *)pHwRef;

        if ( gDalContext.hw_valid == TRUE )
        {
            /* The link has been opened from the application interface */
            gLinkFunc.open_from_handle(pgDalHwContext);

            if (!gLinkFunc.is_opened())
            {
                result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
            }
            else
            {
                /* Clear link buffers */
                gLinkFunc.flush();
            }
        }
        else
        {
            static phDal4Nfc_sConfig_t hw_config;
            hw_config.deviceNode = NULL;
            result = phDal4Nfc_Config(&hw_config, pHwRef );
        }
    }
    else /*Input parametrs invalid*/
    {
        result = NFCSTATUS_INVALID_PARAMETER;
    }

    return result;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Shutdown

PURPOSE:  DAL Shutdown function.

-----------------------------------------------------------------------------*/

NFCSTATUS phDal4Nfc_Shutdown( void *pContext, void *pHwRef)
{
   NFCSTATUS result = NFCSTATUS_SUCCESS;
   void * pThreadReturn;

//   if (pContext == NULL)
//      return NFCSTATUS_INVALID_PARAMETER;

   if (gDalContext.hw_valid == TRUE)
   {
      /* Flush the link */
      gLinkFunc.flush();

      /* Close the message queue */
#ifdef USE_MQ_MESSAGE_QUEUE
       mq_close(nDeferedCallMessageQueueId);
#endif

   }

   return result;
}

NFCSTATUS phDal4Nfc_ConfigRelease(void *pHwRef)
{

   NFCSTATUS result = NFCSTATUS_SUCCESS;
   void * pThreadReturn;
   
   DAL_PRINT("phDal4Nfc_ConfigRelease ");

   if (gDalContext.hw_valid == TRUE)
   {
       /* Signal the read and write threads to exit.  NOTE: there
          actually is no write thread!  :)  */
       DAL_PRINT("Stop Reader Thread");
       gReadWriteContext.nReadThreadAlive = 0;
       gReadWriteContext.nWriteThreadAlive = 0;

       /* Wake up the read thread so it can exit */
       DAL_PRINT("Release Read Semaphore");
       sem_post(&nfc_read_sem);

       DAL_DEBUG("phDal4Nfc_ConfigRelease - doing pthread_join(%d)",
                 gReadWriteContext.nReadThread);
       if (pthread_join(gReadWriteContext.nReadThread,  &pThreadReturn) != 0)
       {
           result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_FAILED);
           DAL_PRINT("phDal4Nfc_ConfigRelease  KO");
       }

      /* Close the message queue */
#ifdef USE_MQ_MESSAGE_QUEUE
       mq_close(nDeferedCallMessageQueueId);
#endif

       /* Shutdown NFC Chip */
       phDal4Nfc_Reset(0);

      /* Close the link */
      gLinkFunc.close();

      if (gDalContext.pDev != NULL) {
          nfc_pn544_close(gDalContext.pDev);
      }
      /* Reset the Read Writer context to NULL */
      memset((void *)&gReadWriteContext,0,sizeof(gReadWriteContext));
      /* Reset the DAL context values to NULL */
      memset((void *)&gDalContext,0,sizeof(gDalContext));
   }

   gDalContext.hw_valid = FALSE;
   
   DAL_DEBUG("phDal4Nfc_ConfigRelease(): %04x\n", result);


   return result;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Write

PURPOSE:  DAL Write function.

-----------------------------------------------------------------------------*/

NFCSTATUS phDal4Nfc_Write( void *pContext, void *pHwRef,uint8_t *pBuffer, uint16_t length)
{
    NFCSTATUS result = NFCSTATUS_SUCCESS;
    static int       MsgType= PHDAL4NFC_WRITE_MESSAGE;
    int *            pmsgType=&MsgType;
    phDal4Nfc_Message_t      sMsg;
    phOsalNfc_Message_t      OsalMsg;

    if ((NULL != pContext) && (NULL != pHwRef)&&
        (NULL != pBuffer) && (0 != length))
    {
        if( gDalContext.hw_valid== TRUE)
        {
            if((!gReadWriteContext.nWriteBusy)&&
                (!gReadWriteContext.nWaitingOnWrite))
            {
		DAL_PRINT("phDal4Nfc_Write() : Temporary buffer !! \n");
		gReadWriteContext.pTempWriteBuffer = (uint8_t*)malloc(length * sizeof(uint8_t));
		/* Make a copy of the passed arguments */
		memcpy(gReadWriteContext.pTempWriteBuffer,pBuffer,length);
                DAL_DEBUG("phDal4Nfc_Write(): %d\n", length);
                gReadWriteContext.pWriteBuffer = gReadWriteContext.pTempWriteBuffer;
                gReadWriteContext.nNbOfBytesToWrite  = length;
                /* Change the write state so that thread can take over the write */
                gReadWriteContext.nWriteBusy = TRUE;
                /* Just set variable here. This is the trigger for the Write thread */
                gReadWriteContext.nWaitingOnWrite = TRUE;
                /* Update the error state */
                result = NFCSTATUS_PENDING;
                /* Send Message and perform physical write in the DefferedCallback */
                /* read completed immediately */
		sMsg.eMsgType= PHDAL4NFC_WRITE_MESSAGE;
		/* Update the state */
		phDal4Nfc_FillMsg(&sMsg,&OsalMsg);
		phDal4Nfc_DeferredCall((pphDal4Nfc_DeferFuncPointer_t)phDal4Nfc_DeferredCb,(void *)pmsgType);
		memset(&sMsg,0,sizeof(phDal4Nfc_Message_t));
		memset(&OsalMsg,0,sizeof(phOsalNfc_Message_t));
            }
            else
            {
                /* Driver is BUSY with previous Write */
                DAL_PRINT("phDal4Nfc_Write() : Busy \n");
                result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_BUSY) ;
            }
        }
        else
        {
            /* TBD :Additional error code : NOT_INITIALISED */
            result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
        }

    }/*end if-Input parametrs valid-check*/
    else
    {
        result = NFCSTATUS_INVALID_PARAMETER;
    }
    return result;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Read

PURPOSE:  DAL Read  function.

-----------------------------------------------------------------------------*/

NFCSTATUS phDal4Nfc_Read( void *pContext, void *pHwRef,uint8_t *pBuffer, uint16_t length)
{
    NFCSTATUS result = NFCSTATUS_SUCCESS;

    if ((NULL != pContext) && (NULL != pHwRef)&&
        (NULL != pBuffer) && (0 != length))
    {
        if ( gDalContext.hw_valid== TRUE)
        {
            if((!gReadWriteContext.nReadBusy)&&
                (!gReadWriteContext.nWaitingOnRead))
            {
                DAL_DEBUG("*****DAl Read called  length : %d\n", length);

                /* Make a copy of the passed arguments */
                gReadWriteContext.pReadBuffer = pBuffer;
                gReadWriteContext.nNbOfBytesToRead  = length;
                /* Change the Read state so that thread can take over the read */
                gReadWriteContext.nReadBusy = TRUE;
                /* Just set variable here. This is the trigger for the Reader thread */
                gReadWriteContext.nWaitingOnRead = TRUE;
                /* Update the return state */
                result = NFCSTATUS_PENDING;
                /* unlock reader thread */
                sem_post(&nfc_read_sem);
            }
            else
            {
                /* Driver is BUSY with prev Read */
                DAL_PRINT("DAL BUSY\n");
                /* Make a copy of the passed arguments */
                gReadWriteContext.pReadBuffer = pBuffer;
                gReadWriteContext.nNbOfBytesToRead  = length;
                result = NFCSTATUS_PENDING;
            }
        }
        else
        {
            /* TBD :Additional error code : NOT_INITIALISED */
            result = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
        }
    }/*end if-Input parametrs valid-check*/
    else
    {
        result = NFCSTATUS_INVALID_PARAMETER;
    }
    DAL_DEBUG("*****DAl Read called  result : %x\n", result);
    return result;
}


/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_ReadWait

PURPOSE:  DAL Read wait function.

-----------------------------------------------------------------------------*/

NFCSTATUS phDal4Nfc_ReadWait(void *pContext, void *pHwRef,uint8_t *pBuffer, uint16_t length)
{
   /* not used */
   DAL_PRINT("phDal4Nfc_ReadWait"); 
   return 0;
}
/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_ReadWaitCancel

PURPOSE: Cancel the Read wait function.

-----------------------------------------------------------------------------*/

NFCSTATUS phDal4Nfc_ReadWaitCancel( void *pContext, void *pHwRef)
{
   DAL_PRINT("phDal4Nfc_ReadWaitCancel");

   /* unlock read semaphore */
   sem_post(&nfc_read_sem);

   return 0;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Config

PURPOSE: Configure the serial port.

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Config(pphDal4Nfc_sConfig_t config,void **phwref)
{
   NFCSTATUS                       retstatus = NFCSTATUS_SUCCESS;
   const hw_module_t* hw_module;
   nfc_pn544_device_t* pn544_dev;
   uint8_t num_eeprom_settings;
   uint8_t* eeprom_settings;
   int ret;

   /* Retrieve the hw module from the Android NFC HAL */
   ret = hw_get_module(NFC_HARDWARE_MODULE_ID, &hw_module);
   if (ret) {
       ALOGE("hw_get_module() failed");
       return NFCSTATUS_FAILED;
   }
   ret = nfc_pn544_open(hw_module, &pn544_dev);
   if (ret) {
       ALOGE("Could not open pn544 hw_module");
       return NFCSTATUS_FAILED;
   }
   config->deviceNode = pn544_dev->device_node;
   if (config->deviceNode == NULL) {
       ALOGE("deviceNode NULL");
       return NFCSTATUS_FAILED;
   }

   DAL_PRINT("phDal4Nfc_Config");

   if ((config == NULL) || (phwref == NULL))
      return NFCSTATUS_INVALID_PARAMETER;

   /* Register the link callbacks */
   memset(&gLinkFunc, 0, sizeof(phDal4Nfc_link_cbk_interface_t));
   switch(pn544_dev->linktype)
   {
      case PN544_LINK_TYPE_UART:
      case PN544_LINK_TYPE_USB:
      {
	 DAL_PRINT("UART link Config");
         /* Uart link interface */
         gLinkFunc.init               = phDal4Nfc_uart_initialize;
         gLinkFunc.open_from_handle   = phDal4Nfc_uart_set_open_from_handle;
         gLinkFunc.is_opened          = phDal4Nfc_uart_is_opened;
         gLinkFunc.flush              = phDal4Nfc_uart_flush;
         gLinkFunc.close              = phDal4Nfc_uart_close;
         gLinkFunc.open_and_configure = phDal4Nfc_uart_open_and_configure;
         gLinkFunc.read               = phDal4Nfc_uart_read;
         gLinkFunc.write              = phDal4Nfc_uart_write;
         gLinkFunc.reset              = phDal4Nfc_uart_reset;
      }
      break;

      case PN544_LINK_TYPE_I2C:
      {
	 DAL_PRINT("I2C link Config");
         /* i2c link interface */
         gLinkFunc.init               = phDal4Nfc_i2c_initialize;
         gLinkFunc.open_from_handle   = phDal4Nfc_i2c_set_open_from_handle;
         gLinkFunc.is_opened          = phDal4Nfc_i2c_is_opened;
         gLinkFunc.flush              = phDal4Nfc_i2c_flush;
         gLinkFunc.close              = phDal4Nfc_i2c_close;
         gLinkFunc.open_and_configure = phDal4Nfc_i2c_open_and_configure;
         gLinkFunc.read               = phDal4Nfc_i2c_read;
         gLinkFunc.write              = phDal4Nfc_i2c_write;
         gLinkFunc.reset              = phDal4Nfc_i2c_reset;
         break;
      }

      default:
      {
         /* Shound not happen : Bad parameter */
         return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_PARAMETER);
      }
   }

   gLinkFunc.init(); /* So that link interface can initialize its internal state */
   retstatus = gLinkFunc.open_and_configure(config, phwref);
   if (retstatus != NFCSTATUS_SUCCESS)
      return retstatus;

   /* Iniatilize the DAL context */
   (void)memset(&gDalContext,0,sizeof(phDal4Nfc_SContext_t));
   pgDalContext = &gDalContext;
   
   /* Reset the Reader Thread values to NULL */
   memset((void *)&gReadWriteContext,0,sizeof(gReadWriteContext));
   gReadWriteContext.nReadThreadAlive     = TRUE;
   gReadWriteContext.nWriteBusy = FALSE;
   gReadWriteContext.nWaitingOnWrite = FALSE;
   
   /* Prepare the message queue for the defered calls */
#ifdef USE_MQ_MESSAGE_QUEUE
   nDeferedCallMessageQueueId = mq_open(MQ_NAME_IDENTIFIER, O_CREAT|O_RDWR, 0666, &MQ_QUEUE_ATTRIBUTES);
#else
   nDeferedCallMessageQueueId = config->nClientId;
#endif

   gDalContext.pDev = pn544_dev;

   /* Start Read and Write Threads */
   if(NFCSTATUS_SUCCESS != phDal4Nfc_StartThreads())
   {
      return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_FAILED);
   }

   gDalContext.hw_valid = TRUE;
   phDal4Nfc_Reset(1);
   phDal4Nfc_Reset(0);
   phDal4Nfc_Reset(1);

   return NFCSTATUS_SUCCESS;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Reset

PURPOSE: Reset the PN544, using the VEN pin

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Reset(long level)
{
   NFCSTATUS	retstatus = NFCSTATUS_SUCCESS;

   DAL_DEBUG("phDal4Nfc_Reset: VEN to %ld",level);

   retstatus = gLinkFunc.reset(level);

   return retstatus;
}

/*-----------------------------------------------------------------------------

FUNCTION: phDal4Nfc_Download

PURPOSE: Put the PN544 in download mode, using the GPIO4 pin

-----------------------------------------------------------------------------*/
NFCSTATUS phDal4Nfc_Download()
{
   NFCSTATUS	retstatus = NFCSTATUS_SUCCESS;

   DAL_DEBUG("phDal4Nfc_Download: GPIO4 to %d",1);

   usleep(10000);
   retstatus = phDal4Nfc_Reset(2);

   return retstatus;
}



/*-----------------------------------------------------------------------------------
                                DAL INTERNAL IMPLEMENTATION
------------------------------------------------------------------------------------*/



/**
 * \ingroup grp_nfc_dal
 *
 * \brief DAL Reader thread handler
 * This function manages the reads from the link interface. The reads are done from
 * this thread to create the asynchronous mecanism. When calling the synchronous
 * function phDal4Nfc_Read, the nWaitingOnRead mutex is unlocked and the read
 * can be done. Then a client callback is called to send the result.
 *
 * \param[in]       pArg     A custom argument that can be passed to the thread (not used)
 *
 * \retval TRUE                                 Thread exiting.
 */

int phDal4Nfc_ReaderThread(void * pArg)
{
    char      retvalue;
    NFCSTATUS result = NFCSTATUS_SUCCESS;
    uint8_t   retry_cnt=0;
    void *    memsetRet;

    static int       MsgType= PHDAL4NFC_READ_MESSAGE;
    int *     pmsgType=&MsgType;

    phDal4Nfc_Message_t      sMsg;
    phOsalNfc_Message_t      OsalMsg ;
    int i;
    int i2c_error_count;
    int i2c_workaround;
    int i2c_device_address = 0x57;
    if (gDalContext.pDev != NULL) {
        i2c_workaround = gDalContext.pDev->enable_i2c_workaround;
        if (gDalContext.pDev->i2c_device_address) {
            i2c_device_address = gDalContext.pDev->i2c_device_address;
            if (i2c_workaround && i2c_device_address < 32)
            {
                ALOGE("i2c_device_address not set to valid value");
                return NFCSTATUS_FAILED;
            }
        }
    } else {
        ALOGE("gDalContext.pDev is not set");
        return NFCSTATUS_FAILED;
    }

    pthread_setname_np(pthread_self(), "reader");

    /* Create the overlapped event. Must be closed before exiting
    to avoid a handle leak. This event is used READ API and the Reader thread*/

    DAL_PRINT("RX Thread \n");
    DAL_DEBUG("\nRX Thread nReadThreadAlive = %d",gReadWriteContext.nReadThreadAlive);
    DAL_DEBUG("\nRX Thread nWaitingOnRead = %d",gReadWriteContext.nWaitingOnRead);
    while(gReadWriteContext.nReadThreadAlive) /* Thread Loop */
    {
        /* Check for the read request from user */
	DAL_PRINT("RX Thread Sem Lock\n");
        sem_wait(&nfc_read_sem);
        DAL_PRINT("RX Thread Sem UnLock\n");

        if (!gReadWriteContext.nReadThreadAlive)
        {
            /* got the signal that we should exit.  NOTE: we don't
               attempt to read below, since the read may block */
            break;
        }

        /* Issue read operation.*/

    i2c_error_count = 0;
retry:
	gReadWriteContext.nNbOfBytesRead=0;
	DAL_DEBUG("RX Thread *New *** *****Request Length = %d",gReadWriteContext.nNbOfBytesToRead);
	memsetRet=memset(gReadWriteContext.pReadBuffer,0,gReadWriteContext.nNbOfBytesToRead);

	/* Wait for IRQ !!!  */
    gReadWriteContext.nNbOfBytesRead = gLinkFunc.read(gReadWriteContext.pReadBuffer, gReadWriteContext.nNbOfBytesToRead);

    /* A read value equal to the i2c_device_address indicates a HW I2C error at I2C address i2c_device_address
     * (pn544). There should not be false positives because a read of length 1
     * must be a HCI length read, and a length of i2c_device_address is impossible (max is 33).
     */
    if (i2c_workaround && gReadWriteContext.nNbOfBytesToRead == 1 &&
            gReadWriteContext.pReadBuffer[0] == i2c_device_address)
    {
        i2c_error_count++;
        DAL_DEBUG("RX Thread Read 0x%02x  ", i2c_device_address);
        DAL_DEBUG("%d times\n", i2c_error_count);

        if (i2c_error_count < 5) {
            usleep(2000);
            goto retry;
        }
        DAL_PRINT("RX Thread NOTHING TO READ, RECOVER");
        phOsalNfc_RaiseException(phOsalNfc_e_UnrecovFirmwareErr,1);
    }
    else
    {
        i2c_error_count = 0;

        if (low_level_traces)
        {
             phOsalNfc_PrintData("RECV", (uint16_t)gReadWriteContext.nNbOfBytesRead,
                    gReadWriteContext.pReadBuffer, low_level_traces);
        }
        DAL_DEBUG("RX Thread Read ok. nbToRead=%d\n", gReadWriteContext.nNbOfBytesToRead);
        DAL_DEBUG("RX Thread NbReallyRead=%d\n", gReadWriteContext.nNbOfBytesRead);
/*      DAL_PRINT("RX Thread ReadBuff[]={ ");
        for (i = 0; i < gReadWriteContext.nNbOfBytesRead; i++)
        {
          DAL_DEBUG("RX Thread 0x%x ", gReadWriteContext.pReadBuffer[i]);
        }
        DAL_PRINT("RX Thread }\n"); */

        /* read completed immediately */
        sMsg.eMsgType= PHDAL4NFC_READ_MESSAGE;
        /* Update the state */
        phDal4Nfc_FillMsg(&sMsg,&OsalMsg);
        phDal4Nfc_DeferredCall((pphDal4Nfc_DeferFuncPointer_t)phDal4Nfc_DeferredCb,(void *)pmsgType);
        memsetRet=memset(&sMsg,0,sizeof(phDal4Nfc_Message_t));
        memsetRet=memset(&OsalMsg,0,sizeof(phOsalNfc_Message_t));
    }

    } /* End of thread Loop*/

    DAL_PRINT("RX Thread  exiting");

    return TRUE;
}



/**
 * \ingroup grp_nfc_dal
 *
 * \brief DAL Start threads function
 * This function is called from phDal4Nfc_Config and is responsible of creating the
 * reader thread.
 *
 * \retval NFCSTATUS_SUCCESS                    If success.
 * \retval NFCSTATUS_FAILED                     Can not create thread or retreive its attributes
 */
NFCSTATUS phDal4Nfc_StartThreads(void)
{
    pthread_attr_t nReadThreadAttributes;
    pthread_attr_t nWriteThreadAttributes;
    int ret;

    if(sem_init(&nfc_read_sem, 0, 0) == -1)
    {
      DAL_PRINT("NFC Init Semaphore creation Error");
      return -1;
    }

    ret = pthread_create(&gReadWriteContext.nReadThread, NULL,  (pphDal4Nfc_thread_handler_t)phDal4Nfc_ReaderThread,  (void*) "dal_read_thread");
    if(ret != 0)
        return(PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_FAILED));

    return NFCSTATUS_SUCCESS;
}

/**
 * \ingroup grp_nfc_dal
 *
 * \brief DAL fill message function
 * Internal messages management. This function fills message structure
 * depending on message types.
 *
 * \param[in, out]       pDalMsg     DAL message to fill
 * \param[in, out]       pOsalMsg    OSAL message to fill
 *
 */
void phDal4Nfc_FillMsg(phDal4Nfc_Message_t *pDalMsg,phOsalNfc_Message_t *pOsalMsg)
{
  if(NULL != pgDalHwContext)
  {
    if(pDalMsg->eMsgType == PHDAL4NFC_WRITE_MESSAGE)
    {
        pDalMsg->transactInfo.length  = (uint8_t)gReadWriteContext.nNbOfBytesWritten;
        pDalMsg->transactInfo.buffer = NULL;
        pDalMsg->transactInfo.status  = NFCSTATUS_SUCCESS;
        pDalMsg->pHwRef  = pgDalHwContext;
        pDalMsg->writeCbPtr = pgDalContext->cb_if.send_complete;
        pOsalMsg->eMsgType = PH_DAL4NFC_MESSAGE_BASE;
        pOsalMsg->pMsgData = pDalMsg;
        return;
    }
    else if(pDalMsg->eMsgType == PHDAL4NFC_READ_MESSAGE)
    {
        pDalMsg->transactInfo.length  = (uint8_t)gReadWriteContext.nNbOfBytesRead;
        pDalMsg->transactInfo.buffer = gReadWriteContext.pReadBuffer;
        pDalMsg->pContext= pgDalContext->cb_if.pif_ctxt;
    }
    else
    {
        pDalMsg->transactInfo.length  = (uint8_t)gReadWriteContext.nNbOfBytesReadWait;
        pDalMsg->transactInfo.buffer = gReadWriteContext.pReadWaitBuffer;
        pDalMsg->pContext= pgDalContext;
    }
    pDalMsg->transactInfo.status  = NFCSTATUS_SUCCESS;
    pDalMsg->pHwRef  = pgDalHwContext;
    pDalMsg->readCbPtr = pgDalContext->cb_if.receive_complete;
    /*map to OSAL msg format*/
    pOsalMsg->eMsgType = PH_DAL4NFC_MESSAGE_BASE;
    pOsalMsg->pMsgData = pDalMsg;
  }

}

/**
 * \ingroup grp_nfc_dal
 *
 * \brief DAL deferred callback function
 * Generic handler function called by a client thread when reading a message from the queue.
 * Will function will directly call the client function (same context). See phDal4Nfc_DeferredCall
 *
  * \param[in]       params    Parameter that will be passed to the client function.
 *
 */
void phDal4Nfc_DeferredCb (void  *params)
{
    int*    pParam=NULL;
    int     i;
    phNfc_sTransactionInfo_t TransactionInfo;

    pParam=(int*)params;

    switch(*pParam)
    {
        case PHDAL4NFC_READ_MESSAGE:
            DAL_PRINT(" Dal deferred read called \n");
            TransactionInfo.buffer=gReadWriteContext.pReadBuffer;
            TransactionInfo.length=(uint16_t)gReadWriteContext.nNbOfBytesRead;
            if (gReadWriteContext.nNbOfBytesRead == gReadWriteContext.nNbOfBytesToRead) {
                TransactionInfo.status=NFCSTATUS_SUCCESS;
            } else {
                TransactionInfo.status=NFCSTATUS_READ_FAILED;
            }
            gReadWriteContext.nReadBusy = FALSE;


            /*  Reset flag so that another opertion can be issued.*/
            gReadWriteContext.nWaitingOnRead = FALSE;
            if ((NULL != pgDalContext) && (NULL != pgDalContext->cb_if.receive_complete))
            {
                pgDalContext->cb_if.receive_complete(pgDalContext->cb_if.pif_ctxt,
                                                        pgDalHwContext,&TransactionInfo);
            }
            
            break;
        case PHDAL4NFC_WRITE_MESSAGE:
            DAL_PRINT(" Dal deferred write called \n");

            if(low_level_traces)
            {
                phOsalNfc_PrintData("SEND", (uint16_t)gReadWriteContext.nNbOfBytesToWrite,
                        gReadWriteContext.pWriteBuffer, low_level_traces);
            }

            /* DAL_DEBUG("dalMsg->transactInfo.length : %d\n", dalMsg->transactInfo.length); */
            /* Make a Physical WRITE */
            /* NOTE: need to usleep(3000) here if the write is for SWP */
            usleep(500);  /* NXP advise 500us sleep required between I2C writes */
            gReadWriteContext.nNbOfBytesWritten = gLinkFunc.write(gReadWriteContext.pWriteBuffer, gReadWriteContext.nNbOfBytesToWrite);
            if (gReadWriteContext.nNbOfBytesWritten != gReadWriteContext.nNbOfBytesToWrite)
            {
                /* controller may be in standby. do it again! */ 
                usleep(10000); /* wait 10 ms */
                gReadWriteContext.nNbOfBytesWritten = gLinkFunc.write(gReadWriteContext.pWriteBuffer, gReadWriteContext.nNbOfBytesToWrite);
            }
            if (gReadWriteContext.nNbOfBytesWritten != gReadWriteContext.nNbOfBytesToWrite)
            {
                /* Report write failure or timeout */
                DAL_PRINT(" Physical Write Error !!! \n");
                TransactionInfo.length=(uint16_t)gReadWriteContext.nNbOfBytesWritten;
                TransactionInfo.status = PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_BOARD_COMMUNICATION_ERROR);
            }
            else
            {
                DAL_PRINT(" Physical Write Success \n"); 
	        TransactionInfo.length=(uint16_t)gReadWriteContext.nNbOfBytesWritten;
	        TransactionInfo.status=NFCSTATUS_SUCCESS;
/*              DAL_PRINT("WriteBuff[]={ ");
                for (i = 0; i < gReadWriteContext.nNbOfBytesWritten; i++)
                {
                  DAL_DEBUG("0x%x ", gReadWriteContext.pWriteBuffer[i]);
                }
                DAL_PRINT("}\n"); */

		// Free TempWriteBuffer 
		if(gReadWriteContext.pTempWriteBuffer != NULL)
		{
		    free(gReadWriteContext.pTempWriteBuffer);
		}
            }
            /* Reset Write context */
            gReadWriteContext.nWriteBusy = FALSE;
            gReadWriteContext.nWaitingOnWrite = FALSE;
            
            /* call LLC callback */
            if ((NULL != pgDalContext) && (NULL != pgDalContext->cb_if.send_complete))
            {
                pgDalContext->cb_if.send_complete(pgDalContext->cb_if.pif_ctxt,
                                                    pgDalHwContext,&TransactionInfo);
            }
            break;
        default:
            break;
    }
}

/**
 * \ingroup grp_nfc_dal
 *
 * \brief DAL deferred call function
 * This function will enable to call the callback client asyncronously and in the client context.
 * It will post a message in a queue that will be processed by a client thread.
 *
 * \param[in]       func     The function to call when message is read from the queue
 * \param[in]       param    Parameter that will be passed to the 'func' function.
 *
 */
void phDal4Nfc_DeferredCall(pphDal4Nfc_DeferFuncPointer_t func, void *param)
{
    int                retvalue = 0;
    phDal4Nfc_Message_Wrapper_t nDeferedMessageWrapper;
    phDal4Nfc_DeferredCall_Msg_t *pDeferedMessage;
    static phDal4Nfc_DeferredCall_Msg_t nDeferedMessageRead;
    static phDal4Nfc_DeferredCall_Msg_t nDeferedMessageWrite;

#ifdef USE_MQ_MESSAGE_QUEUE
    nDeferedMessage.eMsgType = PH_DAL4NFC_MESSAGE_BASE;
    nDeferedMessage.def_call = func;
    nDeferedMessage.params   = param;
    retvalue = (int)mq_send(nDeferedCallMessageQueueId, (char *)&nDeferedMessage, sizeof(phDal4Nfc_DeferredCall_Msg_t), 0);
#else
    if(PHDAL4NFC_READ_MESSAGE==(* (int*)param))
    {
        pDeferedMessage = &nDeferedMessageRead;
    }
    else
    {
        pDeferedMessage = &nDeferedMessageWrite;
    }
    nDeferedMessageWrapper.mtype = 1;
    nDeferedMessageWrapper.msg.eMsgType = PH_DAL4NFC_MESSAGE_BASE;
    pDeferedMessage->pCallback = func;
    pDeferedMessage->pParameter = param;
    nDeferedMessageWrapper.msg.pMsgData = pDeferedMessage;
    nDeferedMessageWrapper.msg.Size = sizeof(phDal4Nfc_DeferredCall_Msg_t);
    retvalue = phDal4Nfc_msgsnd(nDeferedCallMessageQueueId, (struct msgbuf *)&nDeferedMessageWrapper, sizeof(phLibNfc_Message_t), 0);
#endif

}

#undef _DAL_4_NFC_C