/******************************************************************************
 *
 *  Copyright 2018 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
 *
 */
#define LOG_TAG "NxpEseHal"
#include <log/log.h>

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <ese_config.h>
#include <hardware/nfc.h>
#include <phEseStatus.h>
#include <phNxpEsePal.h>
#include <phNxpEsePal_spi.h>
#include <string.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 RF_ON 1

extern int omapi_status;
extern bool ese_debug_enabled;

static int rf_status;
unsigned long int configNum1, configNum2;
// Default max retry count for SPI CLT write blocked in secs
static const uint8_t DEFAULT_MAX_SPI_WRITE_RETRY_COUNT_RF_ON = 10;
static const uint8_t MAX_SPI_WRITE_RETRY_COUNT_HW_ERR = 3;
/*******************************************************************************
**
** Function         phPalEse_spi_close
**
** Description      Closes PN547 device
**
** Parameters       pDevHandle - device handle
**
** Returns          None
**
*******************************************************************************/
void phPalEse_spi_close(void* pDevHandle) {
  ese_nxp_IoctlInOutData_t inpOutData;
  static uint8_t cmd_omapi_concurrent[] = {0x2F, 0x01, 0x01, 0x00};
  int retval;
  ALOGD_IF(ese_debug_enabled, "halimpl close enter.");

  NfcAdaptation& pNfcAdapt = NfcAdaptation::GetInstance();
  pNfcAdapt.Initialize();
  // nxpesehal_ctrl.p_ese_stack_cback = p_cback;
  // nxpesehal_ctrl.p_ese_stack_data_cback = p_data_cback;
  memset(&inpOutData, 0x00, sizeof(ese_nxp_IoctlInOutData_t));
  inpOutData.inp.data.nxpCmd.cmd_len = sizeof(cmd_omapi_concurrent);
  inpOutData.inp.data_source = 1;
  memcpy(inpOutData.inp.data.nxpCmd.p_cmd, cmd_omapi_concurrent,
         sizeof(cmd_omapi_concurrent));
  retval = pNfcAdapt.HalIoctl(HAL_NFC_SPI_DWP_SYNC, &inpOutData);
  ALOGD_IF(ese_debug_enabled, "_spi_close() status %x", retval);

  if (NULL != pDevHandle) {
    close((intptr_t)pDevHandle);
  }
  ALOGD_IF(ese_debug_enabled, "halimpl close exit.");
  return;
}
ESESTATUS phNxpEse_spiIoctl(uint64_t ioctlType, void* p_data) {
  ese_nxp_IoctlInOutData_t* inpOutData = (ese_nxp_IoctlInOutData_t*)p_data;
  rf_status = inpOutData->inp.data.nxpCmd.p_cmd[0];
  if (rf_status == 1) {
    ALOGD_IF(ese_debug_enabled,
             "******************RF IS ON*************************************");
  } else {
    ALOGD_IF(
        ese_debug_enabled,
        "******************RF IS OFF*************************************");
  }
  if (p_data != NULL) {
    ALOGD_IF(ese_debug_enabled,
             "halimpl phNxpEse_spiIoctl p_data is not null ioctltyp: %ld",
             (long)ioctlType);
  }
  return ESESTATUS_SUCCESS;
}

/*******************************************************************************
**
** Function         phPalEse_spi_open_and_configure
**
** 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 phPalEse_spi_open_and_configure(pphPalEse_Config_t pConfig) {
  int nHandle;
  int retryCnt = 0, nfc_access_retryCnt = 0;
  int retval;
  ese_nxp_IoctlInOutData_t inpOutData;
  NfcAdaptation& pNfcAdapt = NfcAdaptation::GetInstance();
  pNfcAdapt.Initialize();
  static uint8_t cmd_omapi_concurrent[] = {0x2F, 0x01, 0x01, 0x01};

  if (EseConfig::hasKey(NAME_NXP_SOF_WRITE)) {
    configNum1 = EseConfig::getUnsigned(NAME_NXP_SOF_WRITE);
    ALOGD_IF(ese_debug_enabled, "NXP_SOF_WRITE value from config file = %ld",
             configNum1);
  }

  if (EseConfig::hasKey(NAME_NXP_SPI_WRITE_TIMEOUT)) {
    configNum2 = EseConfig::getUnsigned(NAME_NXP_SPI_WRITE_TIMEOUT);
    ALOGD_IF(ese_debug_enabled,
             "NXP_SPI_WRITE_TIMEOUT value from config file = %ld", configNum2);
  }
  ALOGD_IF(ese_debug_enabled, "halimpl open enter.");
  memset(&inpOutData, 0x00, sizeof(ese_nxp_IoctlInOutData_t));
  inpOutData.inp.data.nxpCmd.cmd_len = sizeof(cmd_omapi_concurrent);
  inpOutData.inp.data_source = 1;
  memcpy(inpOutData.inp.data.nxpCmd.p_cmd, cmd_omapi_concurrent,
         sizeof(cmd_omapi_concurrent));

retry_nfc_access:
  omapi_status = ESESTATUS_FAILED;
  retval = pNfcAdapt.HalIoctl(HAL_NFC_SPI_DWP_SYNC, &inpOutData);
  if (omapi_status != 0) {
    ALOGD_IF(ese_debug_enabled, "omapi_status return failed.");
    nfc_access_retryCnt++;
    phPalEse_sleep(2000000);
    if (nfc_access_retryCnt < 5) goto retry_nfc_access;
    return ESESTATUS_FAILED;
  }

  ALOGD_IF(ese_debug_enabled, "Opening port=%s\n", pConfig->pDevName);
/* open port */

retry:
  nHandle = open((char const*)pConfig->pDevName, O_RDWR);
  if (nHandle < 0) {
    ALOGE("%s : failed errno = 0x%x", __FUNCTION__, errno);
    if (errno == -EBUSY || errno == EBUSY) {
      retryCnt++;
      ALOGE("Retry open eSE driver, retry cnt : %d", retryCnt);
      if (retryCnt < MAX_RETRY_CNT) {
        phPalEse_sleep(1000000);
        goto retry;
      }
    }
    ALOGE("_spi_open() Failed: retval %x", nHandle);
    pConfig->pDevHandle = NULL;
    return ESESTATUS_INVALID_DEVICE;
  }
  ALOGD_IF(ese_debug_enabled, "eSE driver opened :: fd = [%d]", nHandle);
  pConfig->pDevHandle = (void*)((intptr_t)nHandle);
  return ESESTATUS_SUCCESS;
}

/*******************************************************************************
**
** Function         phPalEse_spi_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 phPalEse_spi_read(void* pDevHandle, uint8_t* pBuffer, int nNbBytesToRead) {
  int ret = -1;
  ALOGD_IF(ese_debug_enabled, "%s Read Requested %d bytes", __FUNCTION__,
           nNbBytesToRead);
  ret = read((intptr_t)pDevHandle, (void*)pBuffer, (nNbBytesToRead));
  ALOGD_IF(ese_debug_enabled, "Read Returned = %d", ret);
  return ret;
}

/*******************************************************************************
**
** Function         phPalEse_spi_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 phPalEse_spi_write(void* pDevHandle, uint8_t* pBuffer,
                       int nNbBytesToWrite) {
  int ret = -1;
  int numWrote = 0;
  unsigned long int retryCount = 0;
  if (NULL == pDevHandle) {
    return -1;
  }

  if (configNum1 == 1) {
    /* Appending SOF for SPI write */
    pBuffer[0] = SEND_PACKET_SOF;
  } else {
    /* Do Nothing */
  }

  unsigned int maxRetryCount = 0, retryDelay = 0;
  while (numWrote < nNbBytesToWrite) {
    // usleep(5000);
    if (rf_status != RF_ON) {
      ret = write((intptr_t)pDevHandle, pBuffer + numWrote,
                  nNbBytesToWrite - numWrote);
    } else {
      ret = -1;
    }
    if (ret > 0) {
      numWrote += ret;
    } else if (ret == 0) {
      ALOGE("_spi_write() EOF");
      return -1;
    } else {
      ALOGE("_spi_write() errno : %x", errno);

      if (rf_status == RF_ON) {
        maxRetryCount = (configNum2 > 0)
                            ? configNum2
                            : DEFAULT_MAX_SPI_WRITE_RETRY_COUNT_RF_ON;
        retryDelay = 1000 * WRITE_WAKE_UP_DELAY;
        ALOGD_IF(ese_debug_enabled, "spi_Write failed as RF is ON.");
      } else {
        maxRetryCount = MAX_SPI_WRITE_RETRY_COUNT_HW_ERR;
        retryDelay = WRITE_WAKE_UP_DELAY;
        ALOGD_IF(ese_debug_enabled, "spi_write failed");
      }

      if (retryCount < maxRetryCount) {
        retryCount++;
        /*wait for eSE wake up*/
        phPalEse_sleep(retryDelay);
        ALOGE("_spi_write() failed. Going to retry, counter:%ld !", retryCount);
        continue;
      }
      return -1;
    }
  }
  return numWrote;
}

/*******************************************************************************
**
** Function         phPalEse_spi_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 phPalEse_spi_ioctl(phPalEse_ControlCode_t eControlCode,
                             void* pDevHandle, long level) {
  ESESTATUS ret = ESESTATUS_IOCTL_FAILED;
  ALOGD_IF(ese_debug_enabled, "phPalEse_spi_ioctl(), ioctl %x , level %lx",
           eControlCode, level);
  ese_nxp_IoctlInOutData_t inpOutData;
  inpOutData.inp.level = level;
  NfcAdaptation& pNfcAdapt = NfcAdaptation::GetInstance();
  if (NULL == pDevHandle) {
    return ESESTATUS_IOCTL_FAILED;
  }
  switch (eControlCode) {
    // Nfc Driver communication part
    case phPalEse_e_ChipRst:
      ret = pNfcAdapt.HalIoctl(HAL_NFC_SET_SPM_PWR, &inpOutData);
      break;

    case phPalEse_e_SetPowerScheme:
      // ret = sendIoctlData(p, HAL_NFC_SET_POWER_SCHEME, &inpOutData);
      ret = ESESTATUS_SUCCESS;
      break;

    case phPalEse_e_GetSPMStatus:
      // ret = sendIoctlData(p, HAL_NFC_GET_SPM_STATUS, &inpOutData);
      ret = ESESTATUS_SUCCESS;
      break;

    case phPalEse_e_GetEseAccess:
      // ret = sendIoctlData(p, HAL_NFC_GET_ESE_ACCESS, &inpOutData);
      ret = ESESTATUS_SUCCESS;
      break;
#ifdef NXP_ESE_JCOP_DWNLD_PROTECTION
    case phPalEse_e_SetJcopDwnldState:
      // ret = sendIoctlData(p, HAL_NFC_SET_DWNLD_STATUS, &inpOutData);
      ret = ESESTATUS_SUCCESS;
      break;
#endif
    case phPalEse_e_DisablePwrCntrl:
      ret = pNfcAdapt.HalIoctl(HAL_NFC_INHIBIT_PWR_CNTRL, &inpOutData);
      break;
    default:
      ret = ESESTATUS_IOCTL_FAILED;
      break;
  }
  return ret;
}