/******************************************************************************
 *
 *  Copyright 2018-2020, 2023 NXP
 *
 *  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.
 *
 ******************************************************************************/

/*
 * DAL spi port implementation for linux
 *
 * Project: Trusted ESE Linux
 *
 */

#include "EseSpiTransport.h"

#define LOG_TAG "NxpEseHal"
#include <errno.h>
#include <ese_config.h>
#include <ese_logs.h>
#include <fcntl.h>
#include <hardware/nfc.h>
#include <log/log.h>
#include <phEseStatus.h>
#include <phNxpEsePal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "NfcAdaptation.h"
#include "hal_nxpese.h"
#include "phNxpEse_Api.h"

#define MAX_RETRY_CNT 10
#define HAL_NFC_SPI_DWP_SYNC 21
#define USE_COLD_RESET 0x00

extern int omapi_status;

static int rf_status;
#if (NFC_NXP_ESE_VER == JCOP_VER_5_x)
eseIoctlData_t eseioctldata;
#endif
// Default max retry count for SPI CLT write blocked in secs
static unsigned long int gsMaxSpiWriteRetryCnt = 10;
#if (NFC_NXP_ESE_VER == JCOP_VER_4_0)
static ESESTATUS phNxpEse_spiIoctl_legacy(uint64_t ioctlType, void* p_data);
#endif

/*******************************************************************************
**
** Function         phPalEse_spi_close
**
** Description      Closes PN547 device
**
** Parameters       pDevHandle - device handle
**
** Returns          None
**
*******************************************************************************/
void EseSpiTransport::Close(void* pDevHandle) {
  if (NULL != pDevHandle) {
    close((intptr_t)pDevHandle);
  }
  return;
}

#ifdef NXP_BOOTTIME_UPDATE
/*******************************************************************************
**
** Function         phNxpEse_spiIoctl
**
** Description      Perform cross HAL IOCTL functionality
**
** Parameters       ioctlType, input data
**
** Returns          SUCCESS/FAIL
**
*******************************************************************************/
ESESTATUS phNxpEse_spiIoctl(uint64_t ioctlType, void* p_data) {
  ESESTATUS status = ESESTATUS_SUCCESS;
  if (!p_data) {
    NXP_LOG_ESE_E("halimpl phNxpEse_spiIoctl p_data is null ioctltyp: %ld",
                  (long)ioctlType);
    return ESESTATUS_FAILED;
  }
#if (NFC_NXP_ESE_VER == JCOP_VER_5_x)
  ese_nxp_IoctlInOutData_t* inpOutData = (ese_nxp_IoctlInOutData_t*)p_data;
  switch (ioctlType) {
    case HAL_ESE_IOCTL_RF_STATUS_UPDATE:
      rf_status = inpOutData->inp.data.nxpCmd.p_cmd[0];
      if (rf_status == 1) {
        NXP_LOG_ESE_D(
            "******************RF IS ON*************************************");
      } else {
        NXP_LOG_ESE_D(
            "******************RF IS OFF*************************************");
      }
      break;
    default:
      NXP_LOG_ESE_D("Invalid IOCTL type");
      break;
  }
#endif
#if (NFC_NXP_ESE_VER == JCOP_VER_4_0)
  status = phNxpEse_spiIoctl_legacy(ioctlType, p_data);
#endif
  return status;
}
#endif

#if (NFC_NXP_ESE_VER == JCOP_VER_4_0)
/*******************************************************************************
**
** Function         phNxpEse_spiIoctl_legacy
**
** Description      Perform cross HAL IOCTL functionality
**
** Parameters       ioctlType, input data
**
** Returns          SUCCESS/FAIL
**
*******************************************************************************/
static ESESTATUS phNxpEse_spiIoctl_legacy(uint64_t ioctlType, void* p_data) {
  ese_nxp_IoctlInOutData_t* inpOutData = (ese_nxp_IoctlInOutData_t*)p_data;
  switch (ioctlType) {
    case HAL_ESE_IOCTL_RF_STATUS_UPDATE:

      rf_status = inpOutData->inp.data.nxpCmd.p_cmd[0];
      if (rf_status == 1) {
        NXP_LOG_ESE_D(
            "******************RF IS ON*************************************");
      } else {
        NXP_LOG_ESE_D(
            "******************RF IS OFF*************************************");
      }
      break;
    default:
      NXP_LOG_ESE_D("Invalid IOCTL type");
      break;
  }
  return ESESTATUS_SUCCESS;
}
#endif

/*******************************************************************************
**
** Function         OpenAndConfigure
**
** Description      Open and configure pn547 device
**
** Parameters       pConfig     - hardware information
**                  pLinkHandle - device handle
**
** Returns          ESE status:
**                  ESESTATUS_SUCCESS            - open_and_configure operation
*success
**                  ESESTATUS_INVALID_DEVICE     - device open operation failure
**
*******************************************************************************/
ESESTATUS EseSpiTransport::OpenAndConfigure(pphPalEse_Config_t pConfig) {
  int nHandle;
  int retryCnt = 0;
  ALOGD("NxpEse EseSpiTransport::OpenAndConfigure 1");
  if (EseConfig::hasKey(NAME_NXP_SOF_WRITE)) {
    mConfigSofWrite = EseConfig::getUnsigned(NAME_NXP_SOF_WRITE);
    NXP_LOG_ESE_D("NXP_SOF_WRITE value from config file = %ld",
                  mConfigSofWrite);
  }
  if (EseConfig::hasKey(NAME_NXP_SPI_WRITE_TIMEOUT)) {
    mConfigSpiWriteTimeout = EseConfig::getUnsigned(NAME_NXP_SPI_WRITE_TIMEOUT);
    NXP_LOG_ESE_D("NXP_SPI_WRITE_TIMEOUT value from config file = %ld",
                  mConfigSpiWriteTimeout);
  }
  /* Read eSE cold reset interface from ese config file */
  if (EseConfig::hasKey(NAME_NXP_P61_COLD_RESET_INTERFACE)) {
    mConfigColdResetIntf =
        EseConfig::getUnsigned(NAME_NXP_P61_COLD_RESET_INTERFACE);
    NXP_LOG_ESE_D("mConfigColdResetIntf value from config file = %ld",
                  mConfigColdResetIntf);
  } else {
    mConfigColdResetIntf = 0x01; /* Default interface is NFC HAL */
    NXP_LOG_ESE_D("mConfigColdResetIntf: Default value ");
  }
  /* Read eSE GPIO reset config */
  if (EseConfig::hasKey(NAME_NXP_ESE_GPIO_RESET)) {
    mConfigGpioReset = EseConfig::getUnsigned(NAME_NXP_ESE_GPIO_RESET);
    NXP_LOG_ESE_D("mConfigGpioReset value from config file = %ld",
                  mConfigGpioReset);
  } else {
    mConfigGpioReset = USE_COLD_RESET;
    NXP_LOG_ESE_D("mConfigGpioReset: Default value ");
  }
  NXP_LOG_ESE_D("Opening port=%s\n", pConfig->pDevName);
/* open port */
retry:
  nHandle = open((char const*)pConfig->pDevName, O_RDWR);
  if (nHandle < 0) {
    NXP_LOG_ESE_E("%s : failed errno = 0x%x, retval %x", __FUNCTION__, errno,
                  nHandle);

    if ((errno == -EBUSY) || (errno == EBUSY)) {
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        phPalEse_sleep(100 * 1000);  // 100ms delay
        return ESESTATUS_DRIVER_BUSY;
      } else {
        retryCnt++;
        NXP_LOG_ESE_E("Retry open eSE driver, retry cnt : %d", retryCnt);
        if (retryCnt < MAX_RETRY_CNT) {
          phPalEse_sleep(1000000);
          goto retry;
        }
      }
    }
    NXP_LOG_ESE_E("_spi_open() Failed: retval %x", nHandle);
    pConfig->pDevHandle = NULL;
    return ESESTATUS_INVALID_DEVICE;
  }
  NXP_LOG_ESE_D("eSE driver opened :: fd = [%d]", nHandle);
  pConfig->pDevHandle = (void*)((intptr_t)nHandle);
  return ESESTATUS_SUCCESS;
}

/*******************************************************************************
**
** Function         Read
**
** Description      Reads requested number of bytes from pn547 device into given
*buffer
**
** Parameters       pDevHandle       - valid device handle
**                  pBuffer          - buffer for read data
**                  nNbBytesToRead   - number of bytes requested to be read
**
** Returns          numRead   - number of successfully read bytes
**                  -1        - read operation failure
**
*******************************************************************************/
int EseSpiTransport::Read(void* pDevHandle, uint8_t* pBuffer,
                          int nNbBytesToRead) {
  int ret = -1;
  ret = read((intptr_t)pDevHandle, (void*)pBuffer, (nNbBytesToRead));
  return ret;
}

/*******************************************************************************
**
** Function         Write
**
** Description      Writes requested number of bytes from given buffer into
*pn547 device
**
** Parameters       pDevHandle       - valid device handle
**                  pBuffer          - buffer for read data
**                  nNbBytesToWrite  - number of bytes requested to be written
**
** Returns          numWrote   - number of successfully written bytes
**                  -1         - write operation failure
**
*******************************************************************************/
int EseSpiTransport::Write(void* pDevHandle, uint8_t* pBuffer,
                           int nNbBytesToWrite) {
  int ret = -1;
  int numWrote = 0;
  unsigned long int retryCount = 0;
  if (NULL == pDevHandle) {
    NXP_LOG_ESE_E("phPalEse_spi_write: received pDevHandle=NULL");
    return -1;
  }
  if (GET_CHIP_OS_VERSION() == OS_VERSION_4_0) {
    if (mConfigSofWrite == 1) {
      /* Appending SOF for SPI write */
      pBuffer[0] = SEND_PACKET_SOF;
    } else {
      /* Do Nothing */
    }
  }
  NXP_LOG_ESE_D("NXP_SPI_WRITE_TIMEOUT value is... : %ld secs",
                mConfigSpiWriteTimeout);
  if (mConfigSpiWriteTimeout > 0) {
    gsMaxSpiWriteRetryCnt = mConfigSpiWriteTimeout;
  } else {
    /* Do Nothing */
  }

  while (numWrote < nNbBytesToWrite) {
    // usleep(5000);
    if (rf_status == 0) {
      ret = write((intptr_t)pDevHandle, pBuffer + numWrote,
                  nNbBytesToWrite - numWrote);
    } else {
      ret = -1;
    }
    if (ret > 0) {
      numWrote += ret;
    } else if (ret == 0) {
      NXP_LOG_ESE_E("_spi_write() EOF");
      return -1;
    } else {
      NXP_LOG_ESE_E("_spi_write() errno : %x", errno);
      NXP_LOG_ESE_D("rf_status value is %d", rf_status);
      if ((errno == EINTR || errno == EAGAIN || rf_status == 1) &&
          (retryCount < gsMaxSpiWriteRetryCnt)) {
        /*Configure retry count or timeout here,now its configured for 2*10
         * secs*/
        if (retryCount > gsMaxSpiWriteRetryCnt) {
          ret = -1;
          break;
        }

        retryCount++;
        /* 5ms delay to give ESE wake up delay */
        phPalEse_sleep(1000 * (GET_WAKE_UP_DELAY()));
        NXP_LOG_ESE_E("_spi_write() failed. Going to retry, counter:%ld !",
                      retryCount);
        continue;
      }
      return -1;
    }
  }
  return numWrote;
}

/*******************************************************************************
**
** Function         Ioctl
**
** Description      Exposed ioctl by p61 spi driver
**
** Parameters       pDevHandle     - valid device handle
**                  level          - reset level
**
** Returns           0   - ioctl operation success
**                  -1   - ioctl operation failure
**
*******************************************************************************/
ESESTATUS EseSpiTransport::Ioctl(phPalEse_ControlCode_t eControlCode,
                                 void* pDevHandle, long level) {
  ESESTATUS ret = ESESTATUS_IOCTL_FAILED;
  int retioctl = 0x00;
#if (NFC_NXP_ESE_VER == JCOP_VER_5_x)
  ese_nxp_IoctlInOutData_t inpOutData;
  inpOutData.inp.level = level;
  NfcAdaptation& pNfcAdapt = NfcAdaptation::GetInstance();
#endif
  NXP_LOG_ESE_D("phPalEse_spi_ioctl(), ioctl %x , level %lx", eControlCode,
                level);
  if (NULL == pDevHandle) {
    if (GET_CHIP_OS_VERSION() == OS_VERSION_4_0) {
      return ESESTATUS_IOCTL_FAILED;
    }
  }
  switch (eControlCode) {
    case phPalEse_e_ResetDevice:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_PWR, level);
      }
      break;

    case phPalEse_e_EnableLog:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_DBG, level);
      }
      break;

    case phPalEse_e_EnablePollMode:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_POLL, level);
      }
      break;
    case phPalEse_e_SetSecureMode:
      ret =
          (ESESTATUS)ioctl((intptr_t)pDevHandle, ESE_SET_TRUSTED_ACCESS, level);
      if (0x00 <= ret) {
        ret = ESESTATUS_SUCCESS;
      }
      break;
    case phPalEse_e_ChipRst:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        if (level == 5) {               // SPI driver communication part
          if (!mConfigColdResetIntf) {  // Call the driver IOCTL
            unsigned int cmd = ESE_PERFORM_COLD_RESET;
            if ((mConfigGpioReset == 0x01) &&
                ((GET_CHIP_OS_VERSION() == OS_VERSION_8_9))) {
              cmd = P61_SET_PWR;
            }
            retioctl = ioctl((intptr_t)pDevHandle, cmd, level);
            if (0x00 <= retioctl) {
              ret = ESESTATUS_SUCCESS;
            }
          } else {
            if ((NFC_NXP_ESE_VER == JCOP_VER_5_x) &&
                (GET_CHIP_OS_VERSION() != OS_VERSION_8_9)) {
              // Nfc Driver communication part
              pNfcAdapt.Initialize();
              ret = pNfcAdapt.resetEse(level);
            } else {
              NXP_LOG_ESE_E("%s: Not supported", __func__);
              ret = ESESTATUS_SUCCESS;
            }
          }
        } else {
          ret = ESESTATUS_SUCCESS;
        }
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_SPM_PWR, level);
      }
      break;
    case phPalEse_e_ResetProtection:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        retioctl = ioctl((intptr_t)pDevHandle, PERFORM_RESET_PROTECTION, level);
        if (0x00 <= retioctl) {
          ret = ESESTATUS_SUCCESS;
        } else {
          NXP_LOG_ESE_E("phPalEse_e_ResetProtection ioctl failed status :%x !",
                        retioctl);
        }
      }
      break;
    case phPalEse_e_EnableThroughputMeasurement:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_THROUGHPUT, level);
      }
      break;

    case phPalEse_e_SetPowerScheme:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret =
            (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_POWER_SCHEME, level);
      }
      break;

    case phPalEse_e_GetSPMStatus:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_GET_SPM_STATUS, level);
      }
      break;

    case phPalEse_e_GetEseAccess:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret = (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_GET_ESE_ACCESS, level);
      }
      break;
    case phPalEse_e_SetJcopDwnldState:
      if (GET_CHIP_OS_VERSION() != OS_VERSION_4_0) {
        ret = ESESTATUS_SUCCESS;
      } else {
        ret =
            (ESESTATUS)ioctl((intptr_t)pDevHandle, P61_SET_DWNLD_STATUS, level);
      }
      break;
    case phPalEse_e_DisablePwrCntrl:
      ret = ESESTATUS_SUCCESS;
      break;
    default:
      ret = ESESTATUS_IOCTL_FAILED;
      break;
  }
  NXP_LOG_ESE_D("Exit  phPalEse_spi_ioctl : ret = %d errno = %d", ret, errno);
  return (ESESTATUS)ret;
}
