/**
* @file znp_interface_spi.c
*
* @brief Interface to the Zigbee Network Processor (e.g. CC2530ZNP) over the Serial Peripheral Interface (SPI) port.
*
* $Rev: 590 $
* $Author: dsmith $
* $Date: 2010-06-11 17:04:51 -0700 (Fri, 11 Jun 2010) $
*
* YOU ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT WARRANTY 
* OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, 
* TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL TEXAS INSTRUMENTS 
* OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, 
* BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
* INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, 
* LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY 
* CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
*/
#include "hal.h"
#include "znp_interface_spi.h"
#include "znp_interface.h"
#include "znp_commands.h"
#include "simple_api.h"
#include "mt.h"

/** This buffer will hold the transmitted messages and 
*   received SRSP Payload after sendMessage() was called. */
unsigned char znpBuf[100];
uint16 ZNPDataCmd, ZNPDataSrc, ZNPDataLen;
uint8 ZNPDataBuf[ZNP_BUF_LEN];

static void  getSRDY1(void);
static uint8 getSRDY2(void);
uint8 halDelayDone(void);

/**
Sends a CC2530ZNP Synchronous Request (SREQ) message and retrieves the response. 
A SREQ is a message to the ZNP that is immediately followed by a Synchronous 
Response (SRSP) message  from the ZNP. 
As opposed to an Asynchronous Request (AREQ) message, which does not have a SRSP.
This is a private method that gets wrapped by sendMessage().
@pre CC2530ZNP has been initialized
@pre znpBuf contains a properly formatted message
@post received data is written to znpBuf
@return 0 if success, -1 if timeout waiting for SRDY to go low after chip select; -2 if timeout waiting for SRSP.
@TODO: timeout on SRDY_IS_HIGH() and SRDY_IS_LOW(), or separate this method and return control to the application
*/
signed int sendSreq()
{

  do {
    getSRDY1();
    /* Send the host SREQ-message across SPI.*/
    spiWrite(znpBuf, (*znpBuf+3));
    /* Now setup the POLL command in the buffer which has just been transmitted and which will now
     * be used to receive the SREQ in response.
     */
    *znpBuf = 0;                    /* POLL command has zero data bytes.*/
    *(znpBuf+1) = 0;                /* POLL command MSB.*/
    *(znpBuf+2) = 0;                /* POLL command LSB.*/
  } while (!getSRDY2());

  spiWrite( znpBuf, 3);  /* Send the POLL across the SPI.*/

  /* Receive the rest of the slave's message if it is longer than the POLL.*/
  if (*znpBuf != 0)
  {
    spiWrite( znpBuf+3, *znpBuf);   
  }

     SPI_SS_CLEAR();  /* MRDY must be cleared before setting it again to talk again.*/
     return 0;  
  
}

/**
Polls the ZNP for data. This is used to receive data from the ZNP, for example when a message has arrived. 
This will be initiated by detecting SRDY going low. 
@pre CC2530ZNP has been initialized.
@pre SRDY has gone low
@pre CC2530ZNP has been initialized
@post received data is written to znpBuf
@return result of sendSreq()
@note this method not required to be implemented when using UART interface.
*/
signed int spiPoll()
{
    *znpBuf = 0; *(znpBuf+1) = 0; *(znpBuf+2) = 0;  /*poll message is 0,0,0 */
    return(sendSreq());
}


/** Public method to send messages to the ZNP. This will send one message and then receive the 
Synchronous Response (SRSP) message from the ZNP to indicate the command was received.
@pre znpBuf contains a properly formatted message
@pre ZNP has been initialized
@post buffer znpBuf contains the response (if any) from the ZNP.
@return 0 if the SRSP received was the expected SRSP, -11 if it was NOT the expected SRSP, 
-12 if sendSreq timeout, or -13 if too many bytes received. 
*/
signed int sendMessage()
{
    unsigned char expectedSrspCmdMsb = znpBuf[1] + 0x40;
    unsigned char expectedSrspCmdLsb = znpBuf[2];

    signed int result = sendSreq(); /*send message, buffer now holds received data*/
    
    if (result != 0)                            /*ERROR - spiSreq() timeout*/
    {
        return -12;
    }
    if (znpBuf[0] > SRSP_BUFFER_SIZE)           /*Error - SRSP length exceeds buffer size*/
    {
        return -13;
    }

    /*The correct SRSP will always be 0x4000 + cmd, or simpler 0x4000 | cmd
      For example, if the SREQ is 0x2605 then the corresponding SRSP is 0x6605 */
    if ((znpBuf[1] == expectedSrspCmdMsb) && (znpBuf[2] == expectedSrspCmdLsb))    /*verify the correct SRSP was received*/
    {
      #if HOST_MT 
       #ifndef ZTOOL
            /*If sys_ping_response_message */
            /* Format of data to be changed - SOF (1) + Length (1)+ Cmd 0 (1) + Cmd 1 (1) + Profile (2) + FCS (1)  (This is required for sensor monitor demo GUI)*/
            if (znpBuf[1] == 0x61 && znpBuf[2] == 0x01)
            {
            znpBuf[0] = 2;
            znpBuf[1] = 0x61;
            znpBuf[2] = 0x01;
            znpBuf[3] = 0x42;  /*Profile ID for Sensor Demo GUI using CC2530ZNP-MK is 0x0042 */
            znpBuf[4] = 0x00;
            }
        #endif
     /* The response from the znp will be sent to the host controller via the MT interface if enabled*/      
     mtTx(znpBuf);
      #endif
      return 0;
    }
    else
    {
         return -11;   /*Wrong SRSP received*/
        
    }
}

/* znpPoll Poll the znp for asynchronous command and drive the ZNP transaction.
input parameters None. output parameters None. return None.
*/
void znpPoll(void)
{  
   if (SRDY_IS_HIGH() == 0) /*wait until SRDY goes low indicating a message has been received.*/
   {
     spiPoll();
#ifdef HOST_MT
       
    mtTx(znpBuf);
#endif
   /* checks the type of received msg and set appropriate event flag
     Two types of messgaes that we are intending to recieve in this application are system messages (system 
    reset). Or Simple API messages 
   */
    znpRsp();
   }     
}

/* znpRsp This function acts on an ZNP response message. Checks the command subsystem of response message and calls appropriate function
input parameters znpBuf - Pointer to the ZNP response buffer.
output parameters None. return      None.
*/
static void znpRsp()
{
  uint16 event;
  switch (znpBuf[1] & MT_SUBSYSTEM_MASK)
  {
  case MT_SYS:
    event = sysRsp();
    break;

  case MT_SAPI:
    event = sapiRsp();
    break;

  default:
    event = ZNP_EVT_NONE;
    break;
  }
  ZNPEvtFlags |= event;
}

/* sysRsp function checks the system message from ZNP and takes appropriate action
input parameters znpBuf - Pointer to the response buffer.output parameters None.
return The corresponding Host event flag, if a recognized response is parsed.
*/
static uint16 sysRsp()
{
  uint16 event = ZNP_EVT_NONE;

  switch (znpBuf[2])
  {
  case LSB(SYS_RESET_IND):
    event = ZNP_SYS_RESET_IND;
    break;
    
  default:
    break;
  }
  return event;
}

/* sapiRsp This function checks the type of Simple API Message from ZNP and takes action.
input parameters znpBuf - Pointer to the response buffer.
output parameters None. return The corresponding Host event flag, if a recognized response is parsed.
*/
static uint16 sapiRsp()
{
  uint16 event = ZNP_EVT_NONE;

  switch (znpBuf[2])
  {
  case LSB(ZB_GET_DEVICE_INFO):
    /*Not being used in Sensor Monitor Network Application**/
    event = ZNP_DEV_INFO_RSP;
    break;

  case LSB(ZB_START_CONFIRM):
     event = ZNP_START_CNF;
    break;

  case LSB(ZB_BIND_CONFIRM):
      BindConfirm();
      break;

  case LSB(ZB_ALLOW_BIND_CONFIRM):
    event = znpBuf[2]; 
    event += (uint16)(znpBuf[1]);
    AllowBindConfirm(event);
    event = ZNP_ALLOW_BIND_CNF;
    break;

  case LSB(ZB_SEND_DATA_CONFIRM):
    SendDataConfirm(znpBuf[3], znpBuf[4]);
    event = ZNP_SEND_DATA_CNF;
    break;

  case LSB(ZB_FIND_DEVICE_CONFIRM):
    /*Not being used in Sensor Monitor Network Application**/
    FindDeviceConfirm(znpBuf[2], znpBuf[3],znpBuf[5]);
    event = ZNP_FIND_DEV_CNF;
    break;

  case LSB(ZB_RECEIVE_DATA_INDICATION):
    if (!(ZNPEvtFlags & ZNP_RCV_DATA_IND))  /* Currently limited by 1 incoming data buffer.*/
    {
      /*Not Being Handled/Used in the Sensor Monitor Network Application*/
      /*Developers should refer to the ZNP Interface Specification document 
      to understand the response message Byte formate*/
      event = ZNP_RCV_DATA_IND;
      ZNPDataSrc = znpBuf[3];
      ZNPDataSrc += (uint16)(znpBuf[4]) << 8;
      ZNPDataCmd = znpBuf[5];
      ZNPDataCmd += (uint16)(znpBuf[6]) << 8;
      ZNPDataLen = znpBuf[7];
      ZNPDataLen += (uint16)(znpBuf[8]) << 8;
      ReceiveDataIndication(ZNPDataSrc, ZNPDataCmd, ZNPDataLen);
    }
    break;

  default:
    break;
  }

  return event;
}

/**************************************************************************************************
 * @fn          getSRDY1
 *
 * @brief       This function cycles setting MRDY and timing out waiting for SRDY until success.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
#define SRDY_WAIT_MSECS 10
#define SRDY()                  ((P2IN & BV(6)) == 0)  //if SRDY is low return true
#define SRDY_RSP()  (!SRDY())  /*if slave ready is low return false*/
static void getSRDY1(void)
{
  SPI_SS_SET();   /* MRDY must be set before talking to the slave.*/

  halDelay(SRDY_WAIT_MSECS, FALSE);
  while (!SRDY() && !halDelayDone()) /*we come out if SRDY becomes low or timer expires....*/
  {
     HAL_SLEEP();
  }
  if (!(SRDY_IS_HIGH() == 0))  
  {
     SPI_SS_CLEAR();  /* MRDY must be cleared before setting it again to talk again.*/
     znpReset();
     SPI_SS_SET();   /* MRDY must be set before talking to the slave.*/
  }
}

/**************************************************************************************************
 * @fn          getSRDY2
 *
 * @brief       This function times out waiting for the SRDY_RSP.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      TRUE if the slave signals SRDY_RSP.
 **************************************************************************************************
 */
static uint8 getSRDY2(void)
{
  halConfigureSrdyInterrrupt(SRDY_INTERRUPT_RISING_EDGE);
  halDelay(SRDY_WAIT_MSECS, FALSE);
  while (!SRDY_RSP() && !halDelayDone())  //we exit when either srdy is high or 10ms timer expires
  {
    HAL_SLEEP();
  }
  halConfigureSrdyInterrrupt(SRDY_INTERRUPT_FALLING_EDGE);  //configure SRDY interrupt on falling  edge

  return !(SRDY_IS_LOW());
}

/**************************************************************************************************
 * @fn          halDelayDone
 *
 * @brief       Check to determine if a requested HAL delay is done.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
uint8 halDelayDone(void)
{
  return (TACCR0 == TACCR0_INIT);
}
