/****************************************************************************/
/* Copyright 1990 to 1994 MBARI                                             */
/****************************************************************************/
/* Summary  : C Serial communication support for Microcontroller Board      */
/* Filename : serial.c                                                      */
/* Author   : Andrew Pearce                                                 */
/* Project  : ROV 1.5 Microcontroller Board Rev 2.0                         */
/* Version  : 2.0                                                           */
/* Created  : 02/24/92                                                      */
/* Modified : 08/23/94                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/lib/serial.c,v 1.1.1.1 1997/05/02 17:16:00 pean Exp $
 * $Log: serial.c,v $
 * Revision 1.1.1.1  1997/05/02 17:16:00  pean
 * Initial release of the microcontroller software after Tiburon
 * Moolpool Dive to test IView, Lapboxes, modified Power can
 * GF/5V using bus capacitance mode.
 *
 * Revision 1.1  92/05/14  09:14:32  09:14:32  pean (Andrew Pearce 408-647-3746)
 * 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 "proto.h"                      /* protocol layer functions         */

                                        /* Data Link Layer data structures  */
struct
{
    enum txState state;                 /* Txmit Finite State Machine State */
    Boolean DLESent;                    /* True if DLE sent by transmitter  */
    Byte    checksum;                   /* Transmitted frame checksum       */
} frameTxState;                         /* Transmitter control structure    */

struct
{
    enum txState 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       */
} frameRxSync;                          /* Receiver control structure       */

MLocal struct
{
    Byte *bufInPtr;                     /* Buffer input pointer             */
    Boolean startFound;                 /* Start of frame detected flag     */
    Boolean DLEFound;                   /* DLE detected flag                */
} rxFrame;

MLocal struct
{
    Nat16   rxTimeout;                  /* Frame Rx Timeout Period          */
    timerId rxTimer;                    /* Frame received timer             */
    Void (*disconnectHook) ();          /* Func. to call if link disconnects*/
} linkState;

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( serialFrame *frame );
MLocal Void  receiverFSM( Byte rxByte );
MLocal Void  waitForRxIdle( Void );
MLocal Void  linkDisconnect( Void );

/****************************************************************************/
/* Function    : initDataLinkLayer                                          */
/* Purpose     : Initializes data link layer data structures                */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initDataLinkLayer( Void )
{
    frameTxState.state  =  IDLE;        /* Transmitter state machine state  */
    frameTxState.DLESent = FALSE;       /* DLE sent (byte stuffing flag)    */

    frameRxSync.state = IDLE;           /* Set recvd FSM state to IDLE      */
    frameRxSync.DLERecvd = FALSE;       /* Clear frame delimeter received   */
    frameRxSync.startRecvd = FALSE;     /* Clear start of frame flag        */

    rxFrame.bufInPtr = frameBuffer;     /* Rx frame in ptr = buffer start   */
    rxFrame.startFound = FALSE;         /* Clear start of frame detect flag */
    rxFrame.DLEFound = FALSE;           /* Clear DLE detected flag          */

    linkState.rxTimeout = FRAME_RX_TIME;
    linkState.rxTimer = (timerId) NULL; /* Initalize frame received timer   */
    linkState.disconnectHook = NULL;    /* Clear disconnect function pointer*/
} /* initDataLinkLayer() */

/****************************************************************************/
/* Function    : dataLinkDisconnectHook                                     */
/* Purpose     : set applic layer function to call when link disconnects    */
/* Inputs      : pointer to function to be called                           */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
dataLinkDisconnectHook( Void (*disconnectFunc) () )
{
                                        /* Save function pointer to call if */
                                        /* the link disconnects             */
    linkState.disconnectHook = disconnectFunc;

    if (disconnectFunc == NULL)
        return;
                                        /* Create link status timer         */
    if (linkState.rxTimer == (timerId) (NULL))
        linkState.rxTimer = timerCreate();

    linkState.rxTimeout = FRAME_RX_TIME;
} /* dataLinkDisconnectHook() */

/****************************************************************************/
/* Function    : setLinkRxTimeout                                           */
/* Purpose     : Sets link receive timeout period in 10 msec ticks          */
/* Inputs      : new Receive Timeout Value                                  */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
setLinkRxTimeout( Nat16 rxTimeout )
{
    linkState.rxTimeout = rxTimeout;
} /* setLinkRxTimeout() */

/****************************************************************************/
/* Function    : getLinkRxTimeout                                           */
/* Purpose     : Gets link receive timeout period in 10 msec ticks          */
/* Inputs      : None                                                       */
/* Outputs     : Link Receive Timeout                                       */
/****************************************************************************/
    Nat16
getLinkRxTimeout( Void )
{
    return (linkState.rxTimeout);
} /*getLinkRxTimeout() */

/****************************************************************************/
/* Function    : linkDisconnect                                             */
/* Purpose     : Disconnect serial communications link                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
     MLocal Void
linkDisconnect( Void )
{
    linkState.disconnectHook();        /* Call disconnect function         */
} /* linkDisconnect() */

/****************************************************************************/
/* Function    : initSerialPort                                             */
/* Purpose     : Initializes serial port hardware                           */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initSerialPort( Void )
{
    int1Disable(TI_INT_MASK);           /* Disable Txmit Interrupt          */
    int1Disable(RI_INT_MASK);           /* Disable Receive Interrupt        */

                                        /* Initialize receive ring buffer   */
    ring_init(&serial_rx_ring, serial_rx_buf, SERIAL_BUF_SIZE);

    transmitControl(OFF);               /* Disable RS485 Transmitter        */

    ioc1 = IOC1_SELECT_TXD;             /* Set P2.0 to Transmit Data mode   */

    setBaudRate((Nat16) BAUD_RATE);     /* Set Baud Rate                    */
                                        /* Mode 1, No Parity, Received On   */
    sp_con = SP_CON_MODE1 | SP_CON_RX_ENABLE;

    int1Enable(RI_INT_MASK);            /* Enable Receive Interrupt         */
    waitForRxIdle();                    /* Wait for RX line to go idle      */

                                        /* Flush receive ring buffer        */
    ring_init(&serial_rx_ring, serial_rx_buf, SERIAL_BUF_SIZE);

#ifdef RS232_MODE
    int1Enable(TI_INT_MASK);            /* Enable Transmit Interrupt        */
#endif
} /* initSerialPort() */

/****************************************************************************/
/* 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 */
        frameTxState.DLESent = TRUE;            /* stuff it                 */
    frameTxState.checksum += txByte;            /* Add to checksum          */
} /* transmitByte() */

/****************************************************************************/
/* Function    : transmitFrame                                              */
/* Purpose     : Transmit interface between Protcol and Data Link Layers    */
/* Inputs      : frame to send                                              */
/* Outputs     : Returns OK if frame sent and acknowledged, otherwise ERROR */
/****************************************************************************/
    Int16
transmitFrame(serialFrame *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);
} /* transmitFrame() */

/****************************************************************************/
/* Function    : receiveByte  - 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
receiveByte( 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 */
} /* receiveByte */

/****************************************************************************/
/* Function    : getFrameFromRing                                           */
/* Purpose     : Extracts a frame from the Receiver Ring Buffer             */
/* Inputs      : pointer to frame length                                    */
/* Outputs     : Returns pointer to frame when received, otherwise NULL     */
/****************************************************************************/
    Byte*
getFrameFromRing(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 (rxFrame.startFound)         /* If start of frame detected then  */
                                        /* Save received byte in frame buf  */
           *rxFrame.bufInPtr++ = rxByte;

        if (rxFrame.DLEFound == TRUE)   /* Last byte recvd was a DLE so     */
        {                               /* Check for valid DLE sequences    */
            rxFrame.DLEFound = FALSE;   /* Set DLE state flag FALSE         */

            if (rxByte == STX)          /* Check for start of frame sequence*/
            {                           /* Reset pointer to start of buffer */
                rxFrame.bufInPtr = frameBuffer;
                rxFrame.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 (rxFrame.startFound == TRUE)
               {                        /* Clear frame start found flag     */
                   rxFrame.startFound = FALSE;
                                        /* calculate length of frame        */
                   *frameLen = (rxFrame.bufInPtr - (frameBuffer + 2));

                   return(frameBuffer); /* Return pointer to start of frame */
               } /* if */
                                        /* Clear frame start found flag     */
               rxFrame.startFound = FALSE;
            } /* else */

            else if (rxByte != DLE)     /* Check for stuffed DLE sequence   */
                rxFrame.startFound = FALSE;
                                        /* Invalid sequence so discard frame*/
        } /* if */

        else if (rxByte == DLE)         /* Last byte was not DLE so test for*/
            rxFrame.DLEFound = TRUE;    /* DLE and set DEL found flag       */

    } /* forever */
} /* getFrameFromRing() */

/****************************************************************************/
/* 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( serialFrame *frame )
{
    if (frameTxState.DLESent)           /* Last byte sent was a DLE so send */
    {
        transmitByte(DLE);              /* A additonal stuffed DLE          */
        frameTxState.DLESent = FALSE;   /* Set DLE state flag FALSE         */
    } /* if */

    else
    {
        switch ((Int16) (++frameTxState.state))
        {
            case IDLE:
                break;

            case SOF_DLE:
                sbuf = DLE;
                break;

            case SOF_STX:
                sbuf = STX;
                frameTxState.checksum = 0;     /* Start checksum calcs     */
                break;

            case F_TYPE:
                transmitByte(frame->hdr);
                                        /* Don't send data field if len = 0 */
                if (frame->length == 0)
                    frameTxState.state = DATA;

                frame->buffer = frame->start; /* Reset pointer to start     */
                break;

            case DATA:
                transmitByte( *(frame->buffer++) );
                if (frame->buffer <
                    (frame->start + frame->length))
                    frameTxState.state = F_TYPE;
                break;

            case CKSUM:
                transmitByte(frameTxState.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:
                frameTxState.state = IDLE;
                break;

        } /* switch */
    } /* else */

    return (frameTxState.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 (frameRxSync.DLERecvd)            /* Last byte recvd was a DLE so    */
    {
        frameRxSync.DLERecvd = FALSE;    /* Set DLE state flag FALSE        */
        if (rxByte == STX)               /* Start of frame received         */
        {
            frameRxSync.startRecvd = TRUE;
            frameRxSync.checksum = 0;     /* Start checksum calcs           */
        } /* if */

        else if (rxByte == ETX)          /* Check for end of frame          */
        {                                /* If Start and End received then  */
            if (frameRxSync.startRecvd == TRUE)
            {
                frameRxSync.checksum -= DLE;
                if (frameRxSync.lastByte2 == DLE)
                    frameRxSync.checksum -= DLE;
            } /* if */

            if (linkState.rxTimer)     /* Re-start link timer, if enabled  */
            {
                timerCancel(linkState.rxTimer);
                timerStart(linkState.rxTimer, linkState.rxTimeout,
                         linkDisconnect, 0);
            } /* if */

            frameRxSync.startRecvd = FALSE;
            frameRxSync.state = IDLE;
        } /* if */

        else if (rxByte == DLE)
            frameRxSync.checksum += rxByte;
        else
        {
            frameRxSync.startRecvd = FALSE;
            frameRxSync.state = IDLE;
        } /* else */

    } /* if */

    else if (frameRxSync.startRecvd)
    {
        if (frameRxSync.state == DATA)
            frameRxSync.checksum += rxByte;

        if (rxByte == DLE) frameRxSync.DLERecvd = TRUE;

        frameRxSync.lastByte2 = frameRxSync.lastByte1;
        frameRxSync.lastByte1 = rxByte;
    } /* else */

    else if (rxByte == DLE) frameRxSync.DLERecvd = TRUE;
} /* receiverFSM() */











