/****************************************************************************/
/* Copyright 1995 MBARI                                                     */
/****************************************************************************/
/* Summary  : C Serial communication support for Microcontroller Board      */
/*            Via a Quad Serial Board                                       */
/* Filename : qsserial.c                                                    */
/* Author   : Douglas Au                                                    */
/* Project  : ROV 1.5 Microcontroller Board Rev 2.0                         */
/* Version  : 2.0                                                           */
/* Created  : 10/23/95                                                      */
/* Modified : 10/23/95                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header:
 * $Log:
 * Revision 1
 * Initial revision
 *
*/
/****************************************************************************/
                                        /* Generate code for the 80C196KB   */
#pragma model(196)
#pragma nosignedchar                    /* Use unsigned char (Char)         */

#include "C:/C96/include/80C196.h"      /* 80196 Register mapping           */
#include "C:/C96/include/stddef.h"      /* C Standard Definitions           */
#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "ring.h"                       /* Ring buffer support routines     */
#include "timer.h"                      /* Timer library definitions        */
#include "microasm.h"                   /* Assembly language functions      */
#include "microdef.h"                   /* Microcontroller definitions      */
#include "microlib.h"                   /* Microcontroller Board decls      */
#include "/hardware/sio32/syslib.h"     /* system library hardware funcs    */
#include "serial.h"                     /* Data Link Layer Functions        */
#include "qsserial.h"                   /* data link layer functions        */
#include "qs_proto.h"                   /* protocol layer functions         */

                                        /* Data Link Layer data structures  */
struct
{
    enum qsTxState state;               /* Txmit Finite State Machine State */
    Boolean DLESent;                    /* True if DLE sent by transmitter  */
    Byte    checksum;                   /* Transmitted frame checksum       */
} qsFrameTxState;                       /* Transmitter control structure    */

struct
{
    enum qsTxState state;                 /* Recvd Finite State Machine State */
    Boolean DLERecvd;                   /* True if frame delimeter received */
    Boolean startRecvd;                 /* True if frame start received     */
    Byte checksum;                      /* Calculated frame checksum        */
    Byte lastByte1;                     /* Last byte recevied               */
    Byte lastByte2;                     /* Last but one byte received       */
} qsFrameRxSync;                          /* Receiver control structure       */

MLocal struct
{
    Byte *bufInPtr;                     /* Buffer input pointer             */
    Boolean startFound;                 /* Start of frame detected flag     */
    Boolean DLEFound;                   /* DLE detected flag                */
} qsRxFrame;

MLocal struct
{
    Nat16   rxTimeout;                  /* Frame Rx Timeout Period          */
    timerId rxTimer;                    /* Frame received timer             */
    Void (*disconnectHook) ();          /* Func. to call if link disconnects*/
} qsLinkState;

MLocal Boolean receiverBusy;            /* Receiver activity detected       */
MLocal Ring serial_rx_ring;             /* Serial IO receive data structure */
MLocal Byte serial_rx_buf[SERIAL_BUF_SIZE];

                                        /* Data Link Layer frame buffer     */
MLocal Byte frameBuffer[MAX_PACKET_LEN + FRAME_HDR_SIZE];

MLocal Int16 transmitterFSM( qsSerialFrame *frame );
MLocal Void  receiverFSM( Byte rxByte );
MLocal Void  waitForRxIdle( Void );
MLocal Void  linkDisconnect( Void );

/****************************************************************************/
/* Function    : initQsDataLinkLayer                                        */
/* Purpose     : Initializes data link layer data structures                */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initQsDataLinkLayer( Void )
{
    qsFrameTxState.state  =  IDLE;        /* Transmitter state machine state  */
    qsFrameTxState.DLESent = FALSE;       /* DLE sent (byte stuffing flag)    */

    qsFrameRxSync.state = IDLE;           /* Set recvd FSM state to IDLE      */
    qsFrameRxSync.DLERecvd = FALSE;       /* Clear frame delimeter received   */
    qsFrameRxSync.startRecvd = FALSE;     /* Clear start of frame flag        */

    qsRxFrame.bufInPtr = frameBuffer;     /* Rx frame in ptr = buffer start   */
    qsRxFrame.startFound = FALSE;         /* Clear start of frame detect flag */
    qsRxFrame.DLEFound = FALSE;           /* Clear DLE detected flag          */

    qsLinkState.rxTimeout = FRAME_RX_TIME;
    qsLinkState.rxTimer = (timerId) NULL; /* Initalize frame received timer   */
    qsLinkState.disconnectHook = NULL;    /* Clear disconnect function pointer*/
} /* initQsDataLinkLayer() */

/****************************************************************************/
/* Function    : qsDataLinkDisconnectHook                                     */
/* Purpose     : set applic layer function to call when link disconnects    */
/* Inputs      : pointer to function to be called                           */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
qsDataLinkDisconnectHook( Void (*disconnectFunc) () )
{
                                        /* Save function pointer to call if */
                                        /* the link disconnects             */
    qsLinkState.disconnectHook = disconnectFunc;

    if (disconnectFunc == NULL)
        return;
                                        /* Create link status timer         */
    if (qsLinkState.rxTimer == (timerId) (NULL))
        qsLinkState.rxTimer = timerCreate();

    qsLinkState.rxTimeout = FRAME_RX_TIME;
} /* qsDataLinkDisconnectHook() */

/****************************************************************************/
/* Function    : setQsLinkRxTimeout                                           */
/* Purpose     : Sets link receive timeout period in 10 msec ticks          */
/* Inputs      : new Receive Timeout Value                                  */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
setQsLinkRxTimeout( Nat16 rxTimeout )
{
    qsLinkState.rxTimeout = rxTimeout;
} /* setQsLinkRxTimeout() */

/****************************************************************************/
/* Function    : getQsLinkRxTimeout                                           */
/* Purpose     : Gets link receive timeout period in 10 msec ticks          */
/* Inputs      : None                                                       */
/* Outputs     : Link Receive Timeout                                       */
/****************************************************************************/
    Nat16
getQsLinkRxTimeout( Void )
{
    return (qsLinkState.rxTimeout);
} /*getQsLinkRxTimeout() */

/****************************************************************************/
/* Function    : qsLinkDisconnect                                             */
/* Purpose     : Disconnect serial communications link                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
     MLocal Void
qsLinkDisconnect( Void )
{
    qsLinkState.disconnectHook();        /* Call disconnect function         */
} /* qsLinkDisconnect() */

/****************************************************************************/
/* Function    : txStartUp                                                  */
/* Purpose     : Enables RS485 serial transmission                          */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
txStartUp( Void )
{
#ifndef RS232_MODE
    waitForRxIdle();                        /* Wait for receiver to go idle*/
    int1Disable(RI_INT_MASK);               /* Disable Receiver Interrupt  */
    transmitControl(ON);                    /* enable 485 transmitter      */
#endif
} /* txStartUp */

/****************************************************************************/
/* Function    : txShutDown                                                 */
/* Purpose     : Disables RS485 serial transmission                         */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
txShutDown( Void )
{
    Byte temp;

#ifndef RS232_MODE
    transmitControl(OFF);               /* Disable 485 transmitter        */
    temp = sbuf;                        /* Discard data in serial rx buf  */
    ipend1 &= ~RI_INT_MASK;             /* Clear any pending RI interrupt */
    int1Enable(RI_INT_MASK);            /* Enable Receiver Interrupt      */
#endif
} /* txShutDown */

/****************************************************************************/
/* Function    : transmitByte                                               */
/* Purpose     : Sends a byte to the serial port and does DLE stuffing      */
/* Inputs      : Byte to send                                               */
/* Outputs     : None, but frameTxState is modified                         */
/****************************************************************************/
    MLocal Void
transmitByte(Byte txByte)
{
    sbuf = txByte;
    if (txByte == DLE)                          /* If byte sent is DLE then */
        qsFrameTxState.DLESent = TRUE;            /* stuff it                 */
    qsFrameTxState.checksum += txByte;            /* Add to checksum          */
} /* transmitByte() */

/****************************************************************************/
/* Function    : qsTransmitFrame                                              */
/* Purpose     : Transmit interface between Protcol and Data Link Layers    */
/* Inputs      : frame to send                                              */
/* Outputs     : Returns OK if frame sent and acknowledged, otherwise ERROR */
/****************************************************************************/
    Int16
qsTransmitFrame(qsSerialFrame *frame)
{
    txStartUp();                        /* Enable RS485 transmitter         */
    do
    {                                   /* Wait for transmitter to empty    */
          while ((sp_stat & TXE_BIT) == 0);
    } while (transmitterFSM(frame) != (Int16) IDLE);

    while ((sp_stat & TXE_BIT) == 0);   /* Wait for last bit to be sent     */
    txShutDown();                       /* Disable RS485 transmitter        */

    return(OK);
} /* qsTransmitFrame() */

/****************************************************************************/
/* Function    : qsReceiveByte  - Receiver Interrupt Service Routine          */
/* Purpose     : Reads byte from serial port and saves it in ring buffer    */
/* Inputs      : None                                                       */
/* Outputs     : None, but ring buffer is updated                           */
/****************************************************************************/
    Void
qsReceiveByte( Void )
{
    Byte    rxByte;                     /* received byte                    */

    rxByte = sbuf;                      /* Read received serial port byte   */
    receiverBusy = TRUE;                /* Set flag for waitForRxIdle       */

    if ((sp_stat & 0x54) == 0x40)       /* Check for Byte Received, Overrun */
    {                                   /* and framing error. No Errors so  */
                                        /* process received byte            */
        ring_put(&serial_rx_ring, rxByte);
        receiverFSM(rxByte);
    } /* if */
} /* qsReceiveByte */

/****************************************************************************/
/* Function    : qsGetFrameFromRing                                          */
/* Purpose     : Extracts a frame from the Receiver Ring Buffer             */
/* Inputs      : pointer to frame length                                    */
/* Outputs     : Returns pointer to frame when received, otherwise NULL     */
/****************************************************************************/
    Byte*
qsGetFrameFromRing(Int16 *frameLen)
{
    Int16    rxByte;                    /* received byte                    */

    FOREVER
    {                                   /* read next byte from receive buf  */
        if ((rxByte = ring_get(&serial_rx_ring)) == ERROR)
            return(NULL);               /* Buffer is empty so return NULL   */

        if (qsRxFrame.startFound)         /* If start of frame detected then  */
                                        /* Save received byte in frame buf  */
           *qsRxFrame.bufInPtr++ = rxByte;

        if (qsRxFrame.DLEFound == TRUE)   /* Last byte recvd was a DLE so     */
        {                               /* Check for valid DLE sequences    */
            qsRxFrame.DLEFound = FALSE;   /* Set DLE state flag FALSE         */

            if (rxByte == STX)          /* Check for start of frame sequence*/
            {                           /* Reset pointer to start of buffer */
                qsRxFrame.bufInPtr = frameBuffer;
                qsRxFrame.startFound = TRUE;  /* Set frame start found flag   */
            } /* if */

            else if (rxByte == ETX)     /* Check for end of frame sequence  */
            {                           /* If Start and End received then   */
               if (qsRxFrame.startFound == TRUE)
               {                        /* Clear frame start found flag     */
                   qsRxFrame.startFound = FALSE;
                                        /* calculate length of frame        */
                   *frameLen = (qsRxFrame.bufInPtr - (frameBuffer + 2));

                   return(frameBuffer); /* Return pointer to start of frame */
               } /* if */
                                        /* Clear frame start found flag     */
               qsRxFrame.startFound = FALSE;
            } /* else */

            else if (rxByte != DLE)     /* Check for stuffed DLE sequence   */
                qsRxFrame.startFound = FALSE;
                                        /* Invalid sequence so discard frame*/
        } /* if */

        else if (rxByte == DLE)         /* Last byte was not DLE so test for*/
            qsRxFrame.DLEFound = TRUE;    /* DLE and set DEL found flag       */

    } /* forever */
} /* qsGetFrameFromRing() */

/****************************************************************************/
/* Function    : TransmitterFSM                                           */
/* Purpose     : Transmitter finite state machine - generates frame format  */
/* Inputs      : Frame to send                                              */
/* Outputs     : Returns current FSM state and frameTxState is modified     */
/****************************************************************************/
    MLocal Int16
transmitterFSM( qsSerialFrame *frame )
{
    if (qsFrameTxState.DLESent)         /* Last byte sent was a DLE so send */
    {
        transmitByte(DLE);              /* A additonal stuffed DLE          */
        qsFrameTxState.DLESent = FALSE; /* Set DLE state flag FALSE         */
    } /* if */

    else
    {
        switch ((Int16) (++qsFrameTxState.state))
        {
            case IDLE:
                break;

            case SOF_DLE:
                sbuf = DLE;
                break;

            case SOF_STX:
                sbuf = STX;
                qsFrameTxState.checksum = 0;     /* Start checksum calcs     */
                break;

            case F_TYPE:
                transmitByte(frame->hdr);
                                        /* Don't send data field if len = 0 */
                if (frame->length == 0)
                    qsFrameTxState.state = DATA;

                frame->buffer = frame->start; /* Reset pointer to start     */
                break;

            case DATA:
                transmitByte( *(frame->buffer++) );
                if (frame->buffer <
                    (frame->start + frame->length))
                    qsFrameTxState.state = F_TYPE;
                break;

            case CKSUM:
                transmitByte(qsFrameTxState.checksum);
                break;

            case EOF_DLE:
                sbuf = DLE;             /* End of Frame Sequence DLE ETX    */
                break;

            case EOF_ETX:               /* End of Frame Sequence DLE ETX    */
                sbuf = ETX;
                break;

            default:
                qsFrameTxState.state = IDLE;
                break;

        } /* switch */
    } /* else */

    return (qsFrameTxState.state);
} /* transmitFSM() */

/****************************************************************************/
/* Function    : waitForRxIdle                                              */
/* Purpose     : Carrier sense detector - waits for receiver to go idle     */
/* Inputs      : None                                                       */
/* Outputs     : None, but global receiverBusy flag is modified             */
/****************************************************************************/
    MLocal Void
waitForRxIdle( Void )
{
    Nat16 ticks;

    do
    {                                     /* Wait RX_IDLE_TIME             */
        receiverBusy = FALSE;             /* Clear receiver busy flag      */
                                          /* Short Delay                   */
        for (ticks = 0; ticks < RX_IDLE_TIME; ticks++);
    } while (receiverBusy != FALSE);
} /* waitForRxIdle() */

/****************************************************************************/
/* Function    : receiverFSM                                                */
/* Purpose     : Receiver finite state machine - process recevied frame fmt */
/* Inputs      : Byte recevied                                              */
/* Outputs     : None, but frameRxSync structure is modified                */
/****************************************************************************/
    MLocal Void
receiverFSM(Byte rxByte)
{
    if (qsFrameRxSync.DLERecvd)            /* Last byte recvd was a DLE so    */
    {
        qsFrameRxSync.DLERecvd = FALSE;    /* Set DLE state flag FALSE        */
        if (rxByte == STX)               /* Start of frame received         */
        {
            qsFrameRxSync.startRecvd = TRUE;
            qsFrameRxSync.checksum = 0;     /* Start checksum calcs           */
        } /* if */

        else if (rxByte == ETX)          /* Check for end of frame          */
        {                                /* If Start and End received then  */
            if (qsFrameRxSync.startRecvd == TRUE)
            {
                qsFrameRxSync.checksum -= DLE;
                if (qsFrameRxSync.lastByte2 == DLE)
                    qsFrameRxSync.checksum -= DLE;
            } /* if */

            if (qsLinkState.rxTimer)     /* Re-start link timer, if enabled  */
            {
                timerCancel(qsLinkState.rxTimer);
                timerStart(qsLinkState.rxTimer, qsLinkState.rxTimeout,
                         linkDisconnect, 0);
            } /* if */

            qsFrameRxSync.startRecvd = FALSE;
            qsFrameRxSync.state = IDLE;
        } /* if */

        else if (rxByte == DLE)
            qsFrameRxSync.checksum += rxByte;
        else
        {
            qsFrameRxSync.startRecvd = FALSE;
            qsFrameRxSync.state = IDLE;
        } /* else */

    } /* if */

    else if (qsFrameRxSync.startRecvd)
    {
        if (qsFrameRxSync.state == DATA)
            qsFrameRxSync.checksum += rxByte;

        if (rxByte == DLE) qsFrameRxSync.DLERecvd = TRUE;

        qsFrameRxSync.lastByte2 = qsFrameRxSync.lastByte1;
        qsFrameRxSync.lastByte1 = rxByte;
    } /* else */

    else if (rxByte == DLE) qsFrameRxSync.DLERecvd = TRUE;
} /* receiverFSM() */




















