/**********************************************************************
 * Copyright (c) 1998 - 2000 by Borst Automation, Fachingen
 * All rights reserved
 *
 * Filename:  HartLoL.c
 * Project:   HART Slave
 * Date:      Sep-10-99
 * Author(s): W. Borst
 *
 * Revision:
 * Date:      Name:       Version:  Description:
 * Dec-03-98  W. Borst    -----     Initial Draft
 * Jan-20-99  W. Borst    1.0       Driver did not return error codes
 *                                  Change of NoComErr()
 *                                  Changes in BuildDefaultResponse
 *                                  Short frame only accepted for 
 *                                  command 0
 *                                  FrameIsForMe()
 * Sep-10-99  W. Borst    1.1       Changes for Project be001/99
 * Jul-25-00  W. Borst    1.2       Changes for Project li001/00
 * 
 *
 * Description: Hart receive and send state machine.
 *              Receiver based on gap time out.
 *
 **********************************************************************/

/**********************************************************************
 * The Standard Include                                               *
 **********************************************************************/
#define __HARTLOL_H__
#include "..\Protocol\HartLoL.h"

/**********************************************************************
 * Public data                                                        *
 **********************************************************************/
T_APP_INTF   strHrtAppIf;     /* Interface buffer for application */

/**********************************************************************
 * Local data                                                         *
 **********************************************************************/
static m_ucResponse2 = HRT_COLD_START;

/* IO state machine */
static T_COM_SM     strHrtComSM = 
{ 0,0,0,0,0,0,0,0,0,0,
  {0xff,0xff,0xff,0xff,0xff} //Number of preambles
};
static T_APP_INTF   astrHrtSrvBuf[2];  /* Buffer to store services */
static BYTE         byHrtToState = HRT_TO_STATE_NONE;
                                       /* Time out status */
static BYTE         byHrtToCounter = 0;    
                                       /* Counter as multiple of
                                          5 ms */
static BYTE         byInChar = 0;      /* The received character */
static BYTE         byInErr = 0;       /* The receiver error */                             

/**********************************************************************
 * Public Functions                                                   *
 **********************************************************************/
    /*******/
VOID hrtInit()
    /*******/
{ 
  /* IO state machine structure */
  strHrtComSM.byState = HRT_IO_IDLE; 
  /* Application interface buffers */
  strHrtAppIf.byStatus = HRT_APP_IDLE;
  astrHrtSrvBuf[0].byStatus = HRT_APP_IDLE;
  astrHrtSrvBuf[1].byStatus = HRT_APP_IDLE;
  M_HRT_INIT_COM;
}

   /*###########*/
void hrt5msCycle(void)
   /*###########*/
{ /*
   * Call the data link layer on time out
   */
  if(byHrtToCounter)
  { if (!(--byHrtToCounter))
      DataLinkLayerFunction();
  }
}

   /*##########*/
void hrtGetChar()
   /*##########*/
{ StartTimeOut(HRT_TO_GAP,HRT_TO_STATE_GAP);
  byInChar = M_HRT_GET_RCV_CHAR;
  byInErr = M_HRT_GET_RCV_ERROR;
  switch (strHrtComSM.byState)
  { case HRT_IO_IDLE:
      strHrtComSM.byCount = 0;
       if (byInChar == HRT_PREAMBLE)
         strHrtComSM.byState = HRT_WAIT_NEXT_PREAMBLE;
       break;
     case HRT_WAIT_NEXT_PREAMBLE:
       if (byInChar == HRT_PREAMBLE)
         strHrtComSM.byState = HRT_RCV_WAIT_DEL;
       else
         strHrtComSM.byState = HRT_IO_IDLE;
       break;
     case HRT_RCV_WAIT_DEL:
       if (IsValidDel())
       { GetFirstChar();
         strHrtComSM.byState = HRT_RCV_READING;
       }
       else
       { if (byInChar != HRT_PREAMBLE)
           strHrtComSM.byState = HRT_IO_IDLE;
       }  
       break;
     case HRT_RCV_READING:
       if (!(byInErr & 0x20))
         GetNextChar();
       break;
     case HRT_IO_LOCKED:
       break;      
  }
}

   /*###############*/
VOID hrtTransmitDone(VOID)
   /*###############*/
{
}

          /*############*/
static VOID StartTimeOut(BYTE byTimeOutValue,BYTE byTimeOutState)
          /*############*/
{ /*
   * Set down counter with required timer value
   */
  M_HRT_DISABLE_INTERRUPT;
  byHrtToCounter = byTimeOutValue;
  byHrtToState = byTimeOutState;
  M_HRT_ENABLE_INTERRUPT;
}

          /*###########*/
static VOID StopTimeOut(VOID)
          /*###########*/
{
  M_HRT_DISABLE_INTERRUPT;
  byHrtToCounter = 0;
  byHrtToState = HRT_TO_STATE_NONE;
  M_HRT_ENABLE_INTERRUPT;
}

          /*############*/
static BOOL FrameIsForMe(VOID)
          /*############*/
 /*
  * The function is evaluating the received frame and is
  * trying to find out why not to process the frame as early
  * as possible.
  * However, if the function is returning 'TRUE' this frame
  * has to be worked out by the application.
  */
{ BYTE e;
  BOOL bAddrMatch;
  BYTE byCheck;
  BYTE byLenPos;
  BYTE byFrameLen;    
      
  if (strHrtComSM.byCount < HRT_MIN_FRAME_ABS) 
                            /* DEL,ADR,CMD,LEN,CHK is minimum */
    /* frame too short */
    return FALSE; /* NOT ME!!! */
  /* set master type */
  if (strHrtComSM.byData[HRT_ADDR_POS] & HRT_MASTER_ADDR_BIT)
    strHrtComSM.byMasterType = HRT_PRIM_MASTER;
  else
    strHrtComSM.byMasterType = HRT_SEC_MASTER;  
  if (strHrtComSM.bAddrErr) /* note: error was evaluated by GetNextChar() */
    /* address error */
    return FALSE; /* NOT ME!!! */
  if ((strHrtComSM.byData[HRT_DELIMITER_POS] & HRT_DELIMITER_TYPE_MASK) != HRT_STX)
    /* not a request */
    return FALSE; /* NOT ME!!! */
  bAddrMatch = FALSE; /* being pessimistic at this point */
  if (strHrtComSM.byData[HRT_DELIMITER_POS] & HRT_LONG_ADDR)
  { /*
     * long frame
     */
    if (strHrtComSM.byCount < HRT_MIN_FRAME_LONG) 
                              /* DEL,UN0,UN1,UN2,UN3,UN4,CMD,LEN,CHK
                                 is minimum */
      /* frame too short */
      return FALSE; /* NOT ME!!! */
    /* test if unique id is correct address */
    bAddrMatch = TRUE; /* assume it is me */ 
    /* mask out host address bits */
    if ((strHrtComSM.byData[HRT_ADDR_POS] & HRT_MASK_OUT_HI_ADDR_BITS)
        !=
        (rs_osFbHartUniqueID[0] & HRT_MASK_OUT_HI_ADDR_BITS)
       )
      bAddrMatch = FALSE; /* not me */
    /* Check the remaining part of the unique identifier */
    for (e=1;e<5;e++)
    { if (strHrtComSM.byData[(HRT_ADDR_POS)+e] != rs_osFbHartUniqueID[e])
        bAddrMatch = FALSE; /* not me */
    }
    /* set command */
    strHrtComSM.byCmd = strHrtComSM.byData[HRT_LONGF_CMD_POS];
    if (strHrtComSM.byData[HRT_LONGF_CMD_POS] == HRT_CMD_GET_UNIQUE_ID_BY_TAG)
    { /* addressed by tag name */
      if (strHrtComSM.byCount < HRT_MIN_FRAME_BY_TAG) 
                                /* DEL,UN0,UN1,UN2,UN3,UN4,CMD,LEN
                                   TG0,TG1,TG2,TG3,TG4,TG5,CHK
                                   is minimum */ 
        /* frame too short */
        return FALSE; /* NOT ME!!! */
      if (strHrtComSM.byData[HRT_LONGF_LEN_POS] < HRT_TAG_NAME_SIZE)
        /* data length error */
        return FALSE; /* NOT ME!!! */
      if (bAddrMatch == FALSE)
      { /* device was not addressed by unique id
           check for broadcast address */
        bAddrMatch = TRUE; /* assume it is me */
        /* mask out host address bits */
        if ((strHrtComSM.byData[HRT_ADDR_POS] & HRT_MASK_OUT_HI_ADDR_BITS) != 0)
          return FALSE; /* NOT ME!!! */
        /* Check for the broadcast address */
        for (e=HRT_ADDR_POS+1;e<(HRT_LONG_ADDR_SIZE);e++)
        { if (strHrtComSM.byData[e] != HRT_BROADCAST_ADDR)
          { return FALSE; /* NOT ME!!! */
          }
        }
      }
      if (bAddrMatch)
      { /* now check for correct tag name */
        for (e=0;e<HRT_TAG_NAME_SIZE;e++)
        { if (re_paFbHartTagName[e] != strHrtComSM.byData[HRT_LONGF_REQDATA_POS + e])
            return FALSE; /* NOT ME!!! */
        }
      }
    }
    else
    { if (bAddrMatch == FALSE)
        return FALSE; /* NOT ME!!! */
    }
    byLenPos = HRT_LONGF_LEN_POS;
    bAddrMatch = TRUE; /* **** it is me, but is chk correct ? ****/
  }
  else
  { /*
     * short frame  
     */
    if (strHrtComSM.byData[HRT_SHORTF_CMD_POS] != 
        HRT_CMD_GET_UNIQUE_ID_BY_SHORT
       )
      return FALSE;  /* This is not command 0, 20.1.99 */
    if ((strHrtComSM.byData[HRT_ADDR_POS] & HRT_MASK_OUT_HI_ADDR_BITS) != re_ui8FbHartAddress)
      return FALSE; /* NOT ME!!! */
    byLenPos = HRT_SHORTF_LEN_POS;
    /* set command */
    strHrtComSM.byCmd = strHrtComSM.byData[HRT_SHORTF_CMD_POS];
    bAddrMatch = TRUE; // **** it is me, but is chk correct ? ****
  }
 /***********************************
  * Check if byte count was correct *
  ***********************************/
  if (bAddrMatch)  
  { if (strHrtComSM.byCount < (byLenPos + strHrtComSM.byData[byLenPos] + 2)) 
      return FALSE; /* not enough data bytes received */
  }
 /***********************************/
  if (bAddrMatch)
  { /* checksum check done only if no overflow */
    if (!(strHrtComSM.byErr & HRT_ERR_BUFFER_OVERFLOW))
    { byCheck = 0;
      byFrameLen = (BYTE)(byLenPos + 1 + strHrtComSM.byData[byLenPos]);
      //checksum check
      for (e=0;e<byFrameLen;e++)
      { byCheck ^= strHrtComSM.byData[e];
      }
      if (byCheck != strHrtComSM.byData[byFrameLen])
        /* flag longitudinal address error */
        strHrtComSM.byErr |= HRT_ERR_LONG_PARITY;
    }
    return TRUE;    
  }
  else
  { return FALSE; /* No address match */
  }      
}               

          /*###########*/
static VOID GetNextChar(VOID)
          /*###########*/
{ if (strHrtComSM.byCount >= HRT_MAX_IO_BUFFER)
  { byInErr |= HRT_ERR_BUFFER_OVERFLOW;
  }
  else
  { strHrtComSM.byData[strHrtComSM.byCount++] = byInChar;
  }
 /*******************
  * Check for error *
  *******************/
  if (byInErr)
  { strHrtComSM.byErr |= byInErr;
    /* check for error within addressing range */
    if (strHrtComSM.byCount < HRT_SHORTF_LEN_POS)
      /* shortest header has 2 char */
      strHrtComSM.bAddrErr = TRUE;
    if (strHrtComSM.byCount == (HRT_SHORTF_LEN_POS + 1))
      strHrtComSM.bAddrErr = TRUE;
    else
    { if (strHrtComSM.byData[HRT_DELIMITER_POS] == (HRT_LONG_ADDR | HRT_STX))
      { /* long stx frame */
        if (strHrtComSM.byCount < HRT_LONGF_LEN_POS)
          /* shortest header 6 characters */
          strHrtComSM.bAddrErr = TRUE;
        else
        { 
          if (strHrtComSM.byCount == (HRT_LONGF_LEN_POS + 1))
            strHrtComSM.bAddrErr = TRUE;
          if (strHrtComSM.byData[HRT_LONGF_CMD_POS] == HRT_CMD_GET_UNIQUE_ID_BY_TAG)
          { /* addressed by tag name */
            if (strHrtComSM.byCount < HRT_MIN_FRAME_BY_TAG)
              strHrtComSM.bAddrErr = TRUE;
          }
        }  
      }
    }                
  }      
}

          /*############*/
static VOID GetFirstChar(VOID)
          /*############*/
{ strHrtComSM.byCount = 0;
  strHrtComSM.bAddrErr = FALSE;
  strHrtComSM.byErr = HRT_ERR_NONE;
  GetNextChar();
}

          /*####################*/
static VOID BuildDefaultResponse(VOID)
          /*####################*/
{ BYTE byIndex;
  BYTE byCheck;
  BYTE e;

  /* change stx into ack */
  strHrtComSM.byData[HRT_DELIMITER_POS] |= HRT_ACK;
  /* initialize checksum */
  byCheck = strHrtComSM.byData[HRT_DELIMITER_POS];
  /* clear burst mode bit */
  strHrtComSM.byData[HRT_ADDR_POS] &= (UCHR)(~HRT_DEV_IN_BURST_BIT);
  /* assume short address */
  byIndex = HRT_SHORTF_CMD_POS;
  /* build checksum */
  byCheck ^= strHrtComSM.byData[HRT_ADDR_POS];
  if (strHrtComSM.byData[HRT_DELIMITER_POS] & HRT_LONG_ADDR)
  { /* change to long address */
    byIndex = HRT_LONGF_CMD_POS;
    /* build checksum */
    for (e=(HRT_ADDR_POS+1);e<HRT_LONGF_CMD_POS;e++)
      byCheck ^= strHrtComSM.byData[e];
  }
  /* build checksum, command */
  byCheck ^= strHrtComSM.byData[byIndex++];
  /* Set default data len to responses only */
  strHrtComSM.byData[byIndex++] = 2;
  /* build checksum */
  byCheck ^= 2;
  /* set position of response 1 */
  strHrtComSM.byPosRsp1 = byIndex;
  /* report eventual errors in response 1 */
  if (strHrtComSM.byErr)
    strHrtComSM.byData[byIndex] = strHrtComSM.byErr;
  else   
    /* use device busy as the default response */
    strHrtComSM.byData[byIndex] = HRT_RSPCODE_DEV_BUSY;
  /* check for special commands / (Uoppss!) only if no error */
  if (!(strHrtComSM.byErr))
  { if (strHrtAppIf.byStatus == HRT_APP_IDLE)
    { switch (strHrtComSM.byData[byIndex-2])
      { case HRT_CMD_DO_RESET:
          /* do not report busy if reset is requested */
          strHrtComSM.byData[byIndex] = HRT_RSPCODE_OK;
          break;
        default:
          break;
      }
    }
  }
  byCheck ^= strHrtComSM.byData[byIndex++];
  /* response 2 */
  strHrtComSM.byData[byIndex++] = (BYTE)(m_ucResponse2);
  byCheck ^= m_ucResponse2;
  /* place checksum at end of frame */
  strHrtComSM.byData[byIndex++] = byCheck;
  /* set the length of the frame to be sent */
  strHrtComSM.byCount = byIndex;
}

          /*##########*/
static BOOL IsValidDel(VOID)
          /*##########*/
{ switch (byInChar)
  { case 0x02:
    case 0x82:
    case 0x01:
    case 0x81:
    case 0x06:
    case 0x86:
      return TRUE;
    default:
      return FALSE;  
  }
}

          /*##############*/
static VOID CopyFrameToApp(VOID)
          /*##############*/
{ BYTE byIndex;
  /* Set master type */
  if (strHrtComSM.byData[HRT_ADDR_POS] & HRT_MASTER_ADDR_BIT)
    strHrtAppIf.byMasterType = HRT_PRIM_MASTER;
  else
    strHrtAppIf.byMasterType = HRT_SEC_MASTER;
  /* Set index pointer to command position */
  if (strHrtComSM.byData[HRT_DELIMITER_POS] & HRT_LONG_ADDR)
    /* long frame */
    byIndex = HRT_LONGF_CMD_POS;
  else
    /* short frame */
    byIndex = HRT_SHORTF_CMD_POS;
  /* set the command */
  strHrtAppIf.bySrvID = strHrtComSM.byData[byIndex++];
  /* set the data length */
  strHrtAppIf.byDataLen = strHrtComSM.byData[byIndex];
  if (strHrtAppIf.byDataLen > HRT_MAX_DATA_BUFFER)
  { strHrtAppIf.byDataLen = HRT_MAX_DATA_BUFFER;
  }
  if (strHrtAppIf.byDataLen)
  { /* there is data to copy */
    memcpy(&strHrtAppIf.byData[0],
           &strHrtComSM.byData[byIndex+1],
           strHrtAppIf.byDataLen);
  }
}   

          /*##################*/
static VOID BuildValidResponse(T_APP_INTF* pstrAppIntf)
          /*##################*/
{ PBYTE pbyBuf;
  BYTE  byCheck;
  BYTE  byDatLen;
  BYTE  e;    
      
  /* Set correct byte count in frame */
  strHrtComSM.byData[strHrtComSM.byPosRsp1 - 1] = (BYTE)(2 + pstrAppIntf->byDataLen);
  pbyBuf = &strHrtComSM.byData[strHrtComSM.byPosRsp1];
  /* insert response 1 */
  *pbyBuf++ = pstrAppIntf->byResponse1;
  /* save response 2 */
  m_ucResponse2 = (BYTE)(pstrAppIntf->byResponse2);
  /* insert response 2 */
  *pbyBuf++ = (BYTE)(pstrAppIntf->byResponse2);
  /* copy the data into the frame */
  if (pstrAppIntf->byDataLen)
  { memcpy(pbyBuf,&pstrAppIntf->byData[0],pstrAppIntf->byDataLen);
  }
  byCheck = 0;
  byDatLen = (BYTE)(strHrtComSM.byPosRsp1 + pstrAppIntf->byDataLen + 2);
  /* build the checksum */
  for (e=0;e<byDatLen;e++)
  { byCheck ^= strHrtComSM.byData[e];
  }
  /* insert checksum in frame*/
  strHrtComSM.byData[byDatLen] = byCheck;
  /* set length of frame to be sent */
  strHrtComSM.byCount = (BYTE)(byDatLen+1);
  /* reset cold start flag for requesting master */
  pstrAppIntf->byColdStartFlag = FALSE;
}

          /*##########*/
static VOID CopyStrApp(VOID)
          /*##########*/
{ BYTE byMasterType;
  /*
   * Copy the result of the application into a standby buffer
   */  
  byMasterType = strHrtAppIf.byMasterType;
  astrHrtSrvBuf[byMasterType].byStatus = strHrtAppIf.byStatus;
  astrHrtSrvBuf[byMasterType].bySrvID = strHrtAppIf.bySrvID;
  astrHrtSrvBuf[byMasterType].bWriteProtected = strHrtAppIf.bWriteProtected;
  astrHrtSrvBuf[byMasterType].byResponse1 = strHrtAppIf.byResponse1;
  astrHrtSrvBuf[byMasterType].byResponse2 = strHrtAppIf.byResponse2;
  astrHrtSrvBuf[byMasterType].byDataLen = strHrtAppIf.byDataLen;
  if (strHrtAppIf.byDataLen)
    memcpy(&astrHrtSrvBuf[byMasterType].byData[0],
           &strHrtAppIf.byData[0],
           strHrtAppIf.byDataLen);
}

          /*#####################*/
static VOID DataLinkLayerFunction(VOID)
          /*#####################*/
{ switch(byHrtToState)
  { case HRT_TO_STATE_GAP:
      FbHartVerify();
      /* GAP time out indicates receive ready */
      strHrtComSM.byState = HRT_APP_IDLE;
      if (FrameIsForMe())
      { M_HRT_DISABLE_RCV;
        if (!strHrtComSM.byErr)
        { /* there is no communication error, valid frame! */
      	  if (strHrtAppIf.byStatus == HRT_APP_DONE)
      	  { /* application hands over a service to the communication */
            CopyStrApp();
            /* Free the application interface */
            strHrtAppIf.byStatus = HRT_APP_IDLE;
          }
          //check if new command
          if (astrHrtSrvBuf[strHrtComSM.byMasterType].bySrvID != strHrtComSM.byCmd)
	        { /* reset service buffer status if new command */
            /* old command no longer of interest */
            astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus = HRT_APP_IDLE;
	        } 
          if (astrHrtSrvBuf[strHrtComSM.byMasterType].byResponse1)
	        { /* reset service buffer status if there was a negative
               response before */
            if (astrHrtSrvBuf[strHrtComSM.byMasterType].byResponse1 != HRT_RSPCODE_DEV_BUSY)
              astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus = HRT_APP_IDLE;
	        } 
          /* check if app should do next service */
          if ((strHrtAppIf.byStatus == HRT_APP_IDLE) &&
              (astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus == HRT_APP_IDLE)
             )
          {
            CopyFrameToApp(); //copy data before building default response
            //work to do for the application
            strHrtAppIf.byStatus = HRT_APP_BUSY;
          }
          BuildDefaultResponse();
          /* check if immediate response can be sent */
          if (astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus == HRT_APP_DONE)
	        { BuildValidResponse(&astrHrtSrvBuf[strHrtComSM.byMasterType]);
            if (strHrtAppIf.byStatus != HRT_APP_LOCKED) 
              FbHartSendStream(&strHrtComSM.byData[0],strHrtComSM.byCount);
            astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus = HRT_APP_IDLE;
          }
          else
          { /* wait to send response */
            strHrtComSM.byPollCount = HRT_NO_APP_POLLS;
            StartTimeOut(HRT_TO_APP_CYCLE,HRT_TO_STATE_WAIT_APP);
          }
        }
        else
        { /* Send out the default response immediately if there was a
             communication error */
          BuildDefaultResponse();
          if (strHrtAppIf.byStatus != HRT_APP_LOCKED)
          { FbHartSendStream(&strHrtComSM.byData[0],strHrtComSM.byCount);
          }
        }
      }
      else
      { /* Reset com state state machine if this was not a frame
           for me */
        strHrtComSM.byState = HRT_APP_IDLE;
      }
      break;
    case HRT_TO_STATE_WAIT_APP:
      if (strHrtAppIf.byStatus == HRT_APP_DONE)
      { /* Get the application result */
        CopyStrApp();
        /* Reset application interface */
        strHrtAppIf.byStatus = HRT_APP_IDLE;
      }
      if (astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus == HRT_APP_DONE)
      { /* Send out the response together with the applications data */
        BuildValidResponse(&astrHrtSrvBuf[strHrtComSM.byMasterType]);
        /* Reset the time out */
        StopTimeOut();
        /* Send out data stream */
        if (strHrtAppIf.byStatus != HRT_APP_LOCKED)
          FbHartSendStream(&strHrtComSM.byData[0],strHrtComSM.byCount);
        astrHrtSrvBuf[strHrtComSM.byMasterType].byStatus = HRT_APP_IDLE;
      }
      else
      { if (!(strHrtComSM.byPollCount--))
        { /* Send the default response anyhow if application locked
             comm process */
          StopTimeOut();
          if (strHrtAppIf.byStatus != HRT_APP_LOCKED)
            FbHartSendStream(&strHrtComSM.byData[0],strHrtComSM.byCount);
          /* Reset the time out */
        }
        else
        { /* Wait another 50 ms for the application */
          StartTimeOut(HRT_TO_APP_CYCLE,HRT_TO_STATE_WAIT_APP);
        }
      } 
      break;
  }
}