
/******************************************************************************
  * \attention
  *
  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
  *
  * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        www.st.com/myliberty
  *
  * 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,
  * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
******************************************************************************/


/*
 *      PROJECT:   ST25R3911 firmware
 *      Revision:
 *      LANGUAGE:  ISO C99
 */

/*! \file
 *
 *  \author Ulrich Herrmann
 *
 *  \brief Implementation of ST25R3911 communication.
 *
 */

/*
******************************************************************************
* INCLUDES
******************************************************************************
*/
#include "st25r3911_com.h"
#include "st25r3911.h"
#include "utils.h"


/*
******************************************************************************
* LOCAL DEFINES
******************************************************************************
*/

#define ST25R3911_WRITE_MODE  (0U)                           /*!< ST25R3911 SPI Operation Mode: Write                            */
#define ST25R3911_READ_MODE   (1U << 6)                      /*!< ST25R3911 SPI Operation Mode: Read                             */
#define ST25R3911_FIFO_LOAD   (2U << 6)                      /*!< ST25R3911 SPI Operation Mode: FIFO Load                        */
#define ST25R3911_FIFO_READ   (0xBFU)                        /*!< ST25R3911 SPI Operation Mode: FIFO Read                        */
#define ST25R3911_CMD_MODE    (3U << 6)                      /*!< ST25R3911 SPI Operation Mode: Direct Command                   */

#define ST25R3911_CMD_LEN     (1U)                           /*!< ST25R3911 CMD length                                           */
#define ST25R3911_BUF_LEN     (ST25R3911_CMD_LEN+ST25R3911_FIFO_DEPTH)  /*!< ST25R3911 communication buffer: CMD + FIFO length   */

/*
******************************************************************************
* LOCAL VARIABLES
******************************************************************************
*/

#ifdef ST25R391X_COM_SINGLETXRX
static uint8_t comBuf[ST25R3911_BUF_LEN];    /*!< ST25R3911 communication buffer            */
#endif /* ST25R391X_COM_SINGLETXRX */

/*
******************************************************************************
* LOCAL FUNCTION PROTOTYPES
******************************************************************************
*/

static inline void st25r3911CheckFieldSetLED(uint8_t value)
{
    if ((ST25R3911_REG_OP_CONTROL_tx_en & value) != 0U)
    {
#ifdef PLATFORM_LED_FIELD_PIN
        platformLedOn( PLATFORM_LED_FIELD_PORT, PLATFORM_LED_FIELD_PIN );
    }
    else
    {
        platformLedOff( PLATFORM_LED_FIELD_PORT, PLATFORM_LED_FIELD_PIN );
#endif /* PLATFORM_LED_FIELD_PIN */
    }
}


/*
******************************************************************************
* GLOBAL FUNCTIONS
******************************************************************************
*/
void st25r3911ReadRegister(uint8_t reg, uint8_t* value)
{ 
#ifdef ST25R391X_COM_SINGLETXRX
    uint8_t* buf = comBuf;
#else  /* ST25R391X_COM_SINGLETXRX */
    uint8_t  buf[2];
#endif  /* ST25R391X_COM_SINGLETXRX */
  
    platformProtectST25R391xComm();
    platformSpiSelect();
  
    buf[0] = (reg | ST25R3911_READ_MODE);
    buf[1] = 0;
  
    platformSpiTxRx(buf, buf, 2);
  
    if(value != NULL)
    {
      *value = buf[1];
    }
    
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}


void st25r3911ReadMultipleRegisters(uint8_t reg, uint8_t* values, uint8_t length)
{
#if !defined(ST25R391X_COM_SINGLETXRX)
    uint8_t cmd = (reg | ST25R3911_READ_MODE);
#endif  /* !ST25R391X_COM_SINGLETXRX */
  
    if (length > 0U)
    {
        platformProtectST25R391xComm();
        platformSpiSelect();
  
#ifdef ST25R391X_COM_SINGLETXRX
  
        ST_MEMSET( comBuf, 0x00, MIN( (ST25R3911_CMD_LEN + (uint32_t)length), ST25R3911_BUF_LEN ) );
        comBuf[0] = (reg | ST25R3911_READ_MODE);
        
        platformSpiTxRx(comBuf, comBuf, MIN( (ST25R3911_CMD_LEN + length), ST25R3911_BUF_LEN ) );               /* Transceive as a single SPI call                        */
        ST_MEMCPY( values, &comBuf[ST25R3911_CMD_LEN], MIN( length, ST25R3911_BUF_LEN - ST25R3911_CMD_LEN ) );  /* Copy from local buf to output buffer and skip cmd byte */
  
#else  /* ST25R391X_COM_SINGLETXRX */
  
        /* Since the result comes one byte later, let's first transmit the adddress with discarding the result */
        platformSpiTxRx(&cmd, NULL, ST25R3911_CMD_LEN);
        platformSpiTxRx(NULL, values, length);  
  
#endif  /* ST25R391X_COM_SINGLETXRX */

        platformSpiDeselect();
        platformUnprotectST25R391xComm();
    }
    
    return;
}

void st25r3911ReadTestRegister(uint8_t reg, uint8_t* value)
{
  
#ifdef ST25R391X_COM_SINGLETXRX
    uint8_t* buf = comBuf;
#else  /* ST25R391X_COM_SINGLETXRX */
    uint8_t  buf[3];
#endif  /* ST25R391X_COM_SINGLETXRX */

    platformProtectST25R391xComm();
    platformSpiSelect();

    buf[0] = ST25R3911_CMD_TEST_ACCESS;
    buf[1] = (reg | ST25R3911_READ_MODE);
    buf[2] = 0x00;
  
    platformSpiTxRx(buf, buf, 3);
    
    if(value != NULL)
    {
      *value = buf[2];
    }
    
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}

void st25r3911WriteTestRegister(uint8_t reg, uint8_t value)
{
#ifdef ST25R391X_COM_SINGLETXRX
    uint8_t* buf = comBuf;
#else  /* ST25R391X_COM_SINGLETXRX */
    uint8_t  buf[3];
#endif  /* ST25R391X_COM_SINGLETXRX */
    
    platformProtectST25R391xComm();
    platformSpiSelect();

    buf[0] = ST25R3911_CMD_TEST_ACCESS;
    buf[1] = (reg | ST25R3911_WRITE_MODE);
    buf[2] = value;
  
    platformSpiTxRx(buf, NULL, 3);
  
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}

void st25r3911WriteRegister(uint8_t reg, uint8_t value)
{
#ifdef ST25R391X_COM_SINGLETXRX
    uint8_t* buf = comBuf;
#else  /* ST25R391X_COM_SINGLETXRX */
    uint8_t buf[2];
#endif  /* ST25R391X_COM_SINGLETXRX */
  
    if (ST25R3911_REG_OP_CONTROL == reg)
    {
        st25r3911CheckFieldSetLED(value);
    }    
    
    platformProtectST25R391xComm();
    platformSpiSelect();

    buf[0] = reg | ST25R3911_WRITE_MODE;
    buf[1] = value;
    
    platformSpiTxRx(buf, NULL, 2);
    
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}

void st25r3911ClrRegisterBits( uint8_t reg, uint8_t clr_mask )
{
    uint8_t tmp;

    st25r3911ReadRegister(reg, &tmp);
    tmp &= ~clr_mask;
    st25r3911WriteRegister(reg, tmp);
    
    return;
}


void st25r3911SetRegisterBits( uint8_t reg, uint8_t set_mask )
{
    uint8_t tmp;

    st25r3911ReadRegister(reg, &tmp);
    tmp |= set_mask;
    st25r3911WriteRegister(reg, tmp);
    
    return;
}

void st25r3911ChangeRegisterBits(uint8_t reg, uint8_t valueMask, uint8_t value)
{
    st25r3911ModifyRegister(reg, valueMask, (valueMask & value) );
}

void st25r3911ModifyRegister(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
{
    uint8_t tmp;

    st25r3911ReadRegister(reg, &tmp);

    /* mask out the bits we don't want to change */
    tmp &= ~clr_mask;
    /* set the new value */
    tmp |= set_mask;
    st25r3911WriteRegister(reg, tmp);

    return;
}

void st25r3911ChangeTestRegisterBits( uint8_t reg, uint8_t valueMask, uint8_t value )
{
    uint8_t    rdVal;
    uint8_t    wrVal;
    
    /* Read current reg value */
    st25r3911ReadTestRegister(reg, &rdVal);
    
    /* Compute new value */
    wrVal  = (rdVal & ~valueMask);
    wrVal |= (value & valueMask);
    
    /* Write new reg value */
    st25r3911WriteTestRegister(reg, wrVal );
    
    return;
}

void st25r3911WriteMultipleRegisters(uint8_t reg, const uint8_t* values, uint8_t length)
{ 
#if !defined(ST25R391X_COM_SINGLETXRX)
    uint8_t cmd = (reg | ST25R3911_WRITE_MODE);
#endif  /* !ST25R391X_COM_SINGLETXRX */

    if ((reg <= ST25R3911_REG_OP_CONTROL) && ((reg+length) >= ST25R3911_REG_OP_CONTROL))
    {
        st25r3911CheckFieldSetLED(values[ST25R3911_REG_OP_CONTROL-reg]);
    }
    
    if (length > 0U)
    {
        /* make this operation atomic */
        platformProtectST25R391xComm();
        platformSpiSelect();
    
#ifdef ST25R391X_COM_SINGLETXRX
      
        comBuf[0] = (reg | ST25R3911_WRITE_MODE);
        ST_MEMCPY( &comBuf[ST25R3911_CMD_LEN], values, MIN( length, ST25R3911_BUF_LEN - ST25R3911_CMD_LEN ) );

        platformSpiTxRx( comBuf, NULL, MIN( (ST25R3911_CMD_LEN + length), ST25R3911_BUF_LEN ) );
      
#else  /*ST25R391X_COM_SINGLETXRX*/    
    
        platformSpiTxRx( &cmd, NULL, ST25R3911_CMD_LEN );
        platformSpiTxRx( values, NULL, length );
    
#endif  /*ST25R391X_COM_SINGLETXRX*/    
    
        platformSpiDeselect();
        platformUnprotectST25R391xComm();
    }
    
    return;
}


void st25r3911WriteFifo(const uint8_t* values, uint8_t length)
{
#if !defined(ST25R391X_COM_SINGLETXRX)
    uint8_t cmd = ST25R3911_FIFO_LOAD;
#endif  /* !ST25R391X_COM_SINGLETXRX */

    if (length > 0U)
    {  
        platformProtectST25R391xComm();
        platformSpiSelect();
  
#ifdef ST25R391X_COM_SINGLETXRX
  
        comBuf[0] = ST25R3911_FIFO_LOAD;
        ST_MEMCPY( &comBuf[ST25R3911_CMD_LEN], values, MIN( length, ST25R3911_BUF_LEN - ST25R3911_CMD_LEN ) );

        platformSpiTxRx( comBuf, NULL, MIN( (ST25R3911_CMD_LEN + length), ST25R3911_BUF_LEN ) );
  
#else  /*ST25R391X_COM_SINGLETXRX*/
  
        platformSpiTxRx( &cmd, NULL, ST25R3911_CMD_LEN );
        platformSpiTxRx( values, NULL, length );
  
#endif  /*ST25R391X_COM_SINGLETXRX*/
  
        platformSpiDeselect();
        platformUnprotectST25R391xComm();
    }

    return;
}

void st25r3911ReadFifo(uint8_t* buf, uint8_t length)
{
#if !defined(ST25R391X_COM_SINGLETXRX)
    uint8_t cmd = ST25R3911_FIFO_READ;
#endif  /* !ST25R391X_COM_SINGLETXRX */
    
    if(length > 0U)
    {
        platformProtectST25R391xComm();
        platformSpiSelect();

#ifdef ST25R391X_COM_SINGLETXRX
      
        ST_MEMSET( comBuf, 0x00, MIN( (ST25R3911_CMD_LEN + (uint32_t)length), ST25R3911_BUF_LEN ) );
        comBuf[0] = ST25R3911_FIFO_READ;
      
        platformSpiTxRx( comBuf, comBuf, MIN( (ST25R3911_CMD_LEN + length), ST25R3911_BUF_LEN ) );          /* Transceive as a single SPI call                        */
        ST_MEMCPY( buf, &comBuf[ST25R3911_CMD_LEN], MIN( length, ST25R3911_BUF_LEN - ST25R3911_CMD_LEN ) ); /* Copy from local buf to output buffer and skip cmd byte */
  
#else  /*ST25R391X_COM_SINGLETXRX*/
  
        platformSpiTxRx( &cmd, NULL, ST25R3911_CMD_LEN );
        platformSpiTxRx( NULL, buf, length );
  
#endif  /*ST25R391X_COM_SINGLETXRX*/
      
        platformSpiDeselect();
        platformUnprotectST25R391xComm();
    }

    return;
}

void st25r3911ExecuteCommand( uint8_t cmd )
{
    uint8_t tmpCmd;                                    /* MISRA 17.8 */
    
#ifdef PLATFORM_LED_FIELD_PIN
    if ( (cmd >= ST25R3911_CMD_TRANSMIT_WITH_CRC) && (cmd <= ST25R3911_CMD_RESPONSE_RF_COLLISION_0))
    {
        platformLedOff(PLATFORM_LED_FIELD_PORT, PLATFORM_LED_FIELD_PIN);
    }
#endif /* PLATFORM_LED_FIELD_PIN */
    
    tmpCmd = (cmd | ST25R3911_CMD_MODE);

    platformProtectST25R391xComm();
    platformSpiSelect();
    
    platformSpiTxRx( &tmpCmd, NULL, ST25R3911_CMD_LEN );
    
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}


void st25r3911ExecuteCommands(const uint8_t *cmds, uint8_t length)
{
    platformProtectST25R391xComm();
    platformSpiSelect();

    platformSpiTxRx(cmds, NULL, length);
    
    platformSpiDeselect();
    platformUnprotectST25R391xComm();

    return;
}

bool st25r3911IsRegValid( uint8_t reg )
{
    if( !(( (int16_t)reg >= (int16_t)ST25R3911_REG_IO_CONF1) && (reg <= ST25R3911_REG_CAPACITANCE_MEASURE_RESULT)) &&  (reg != ST25R3911_REG_IC_IDENTITY)  )
    {
        return false;
    }
    return true;
}

/*
******************************************************************************
* LOCAL FUNCTIONS
******************************************************************************
*/

