/****************************************************************************/
/* Copyright 1995 MBARI                                                     */
/****************************************************************************/
/* Summary  : Microcontroller serial communications protcol layer           */
/* Filename : qs_proto.c                                                    */
/* Author   : Douglas Au                                                    */
/* Project  : ROV 1.0                                                       */
/* Version  : 1.0                                                           */
/* Created  : 10/23/95                                                      */
/* Modified : 10/23/95                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header:
 * $Log:
 * Revision 1.0
 * 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 "microasm.h"                   /* Assembly language functions      */
#include "malloc.h"                     /* Malloc support definitions       */
#include "list.h"                       /* Linked list library routines     */
#include "microdef.h"                   /* Microcontroller Definitions      */
#include "microlib.h"                   /* Microcontroller Board decls      */
#include "serial.h"                     /* Data LInk Layer Functions        */
#include "qsserial.h"                   /* data link layer functions        */
#include "qs_proto.h"                   /* protocol layer functions         */

MLocal Byte  calcChecksum(Reg Byte *bytes, Reg Int16 len);
MLocal Byte* unStuffByte(Reg Byte *buffer, Reg Byte *value);
MLocal Int16 copyFrameToPacket(Byte *frame, Int16 frameLen, Byte *packet);
MLocal Byte  getRxChecksum(Byte *frame, Int16 *frameLen);
MLocal Byte* getRxFrameType(Byte *frame, Byte *frameType);



MLocal Byte nextRxSeqNo;                /* Next Rx Data frame sequence no   */
MLocal Byte nextTxSeqNo;                /* Next Tx Data frame sequence no   */
MLocal Byte nextSrqSeqNo;               /* Next SRQ frame sequence number   */

enum txState qsTxState;
MLocal qsSerialFrame dataFrame;         /* Last Data Frame sent for re-txmit*/
MLocal Boolean sendLinkReset;           /* Force next frame to be reset link*/
MLocal Boolean dataPktHoldOff;          /* Hold of data pkt Tx if SRQ sent  */


/****************************************************************************/
/* Function    : initQsProtocolLayer                                        */
/* Purpose     : Initializes Protocol Layer data structures                 */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initQsProtocolLayer( Void )
{
    nextRxSeqNo  = 0;
    nextTxSeqNo  = 0;
    nextSrqSeqNo = 0;

    dataFrame.length = 0;
    dataFrame.start  = NULL;
    dataFrame.buffer = NULL;

    sendLinkReset = TRUE;                  /* Send Link Reset frame         */
    dataPktHoldOff = FALSE;                /* Clear Data Pkt hold off flag  */

} /* initQsProtocolLayer() */

/****************************************************************************/
/* Function    : resetQsProtocolLayer                                         */
/* Purpose     : Resets Protocol Layer data structures                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
resetQsProtocolLayer( Void )
{

    nextRxSeqNo  = 0;
    nextTxSeqNo  = 0;
    nextSrqSeqNo = 0;

    sendLinkReset = FALSE;                 /* Don't send Link Reset frame   */
    dataPktHoldOff = FALSE;                /* Clear Data Pkt hold off flag  */

} /* resetQsProtocolLayer() */

/****************************************************************************/
/* Function    : qsReadRecvdPacket                                            */
/* Purpose     : Extract packet from protocol layer                         */
/* Inputs      : Pointers to packet buffer and packet length                */
/* Outputs     : Returns pointer to malloced packet buffer & buffer length  */
/*               Return status is ERROR or OK if packet placed in buffer    */
/****************************************************************************/
    Int16
qsReadRecvdPacket(Byte **packet, Int16 *packetLen)
{
    struct
    {
        Int16 length;               /* Received frame length                */
        Byte  checksum;             /* Received frame checksum              */
        Byte  hdr;                  /* Received frame type header           */
        Byte  *data;                /* Pointer to data portion of frame     */
        Byte  *start;               /* Pointer to start of frame buffer     */
    } rxFrame;                      /* Information on received frame        */

    Byte    checksum;               /* Checksum calcuated from Rx bytes     */
    Boolean srqSent;
                                    /* Get frame & length from ring buffer  */
    while ((rxFrame.data = getFrameFromRing(&rxFrame.length)) != NULL)
    {                               /* Save pointer to start for length calc*/
        rxFrame.start = rxFrame.data;
                                    /* Extract checksum from frame and      */
                                    /* adjust frame length                  */
        rxFrame.checksum =  getRxChecksum(rxFrame.data, &rxFrame.length);

                                    /* Calculate recevied checksum          */
        checksum = calcChecksum(rxFrame.data, rxFrame.length);

                                    /* Compare calculated and rx checksums  */
        if (rxFrame.checksum != checksum)
            return(ERROR);          /* Discard frame and return error       */

                                    /* Extract frame type from Rx frame     */
                                    /* and adjust frame buffer pointer      */
        rxFrame.data = getRxFrameType(rxFrame.data, &rxFrame.hdr);

        if (FRAME_TYPE(rxFrame.hdr) == LINK_RESET_FRAME)
            resetQsProtocolLayer(); /* Reset sequence numbers               */

        else if (sendLinkReset)     /* We need to reset link seq nos        */
            resetQsSerialLink();    /* Send link reset frame                */

        if (FRAME_TYPE(rxFrame.hdr) == DATA_FRAME)
        {                           /* Data frame received                  */
                                    /* Check work Q and send any SRQ Packets*/
            srqSent = sendSRQPacket();

                                    /* Check seq nos for a duplicate packet */
            if (FRAME_SEQ_NO(rxFrame.hdr) == nextRxSeqNo)
            {                       /* Increment frame sequence number      */
                nextRxSeqNo = (nextRxSeqNo ? 0 : 1);
                                    /* calc length of data portion of frame */
                rxFrame.length -= rxFrame.data - rxFrame.start;

                if (rxFrame.length > 0) /* check for zero length frame      */
                {                       /* Allocate a buffer for the packet */
                    if ((*packet = (Byte *) malloc(rxFrame.length)) == NULL)
                        return(ERROR);
                                    /* Now extract data portion of frame    */
                    *packetLen = copyFrameToPacket(rxFrame.data,
                            rxFrame.length, *packet);

                    if (srqSent)    /* We sent SRQ pkt so wait until SRQ ACK*/
                                    /* is received before sending Data Pkt  */
                        dataPktHoldOff = TRUE;
                    return (OK);
                } /* if */
            } /* if */

            else                    /* Duplicate packet so resend last data*/
                if (srqSent)
                    dataPktHoldOff = TRUE;
                else
                    qsTransmitFrame(&dataFrame);
        } /* if */

        else if (FRAME_TYPE(rxFrame.hdr) == SRQ_ACK_FRAME)
        {                           /* SRQ Frame Acknowledge received       */
            dequeueSRQPacket(FRAME_SEQ_NO(rxFrame.hdr));
                                    /* Delete SRQ Packet from work Q        */

                                    /* Check work Q and send any SRQ Packets*/
            srqSent = sendSRQPacket();
            if ((!srqSent) && (dataPktHoldOff))
            {
                dataPktHoldOff = FALSE;
                qsTransmitFrame(&dataFrame);
            } /* if */

        } /* else */                /* Drop SRQ Frame Acknowledge frame     */

        else if (FRAME_TYPE(rxFrame.hdr) == KEEP_ALIVE_FRAME)
        {                           /* Keep alive frame received            */
                                    /* Check work Q and send any SRQ Packets*/
            srqSent = sendSRQPacket();
            if ((!srqSent) && (dataPktHoldOff))
            {
                dataPktHoldOff = FALSE;
                qsTransmitFrame(&dataFrame);
            } /* if */
        }  /* else */               /* Drop Keep Alive frame                */
    } /* while */

    return(ERROR);
} /* readRecvdPacket() */

/****************************************************************************/
/* Function    : calcChecksum                                               */
/* Purpose     : Calculates a checksum. Value is 8 bit sum of all bytes     */
/* Inputs      : buffer with bytes and length of buffer                     */
/* Outputs     : Returns checksum                                           */
/****************************************************************************/
    MLocal Byte
calcChecksum(
    Reg Byte    *bytes,                 /* Pointer to the data bytes        */
    Reg Int16   len)                    /* number of bytes to checksum      */
{
    Reg Nat16   i;                      /* loop counter                     */
    Reg Int16   checksum;               /* calculated checksum              */

    checksum = 0;
    for (i = 0; i < len; i++)           /* checksum is the sum of all bytes */
        checksum += *bytes++;

    return((Byte) (checksum & 0xff));   /* truncate to 8 bit value          */
} /* calcChecksum */

/****************************************************************************/
/* Function    : unStuffByte                                                */
/* Purpose     : Remove a byte from a buffer, checking for DLE stuffing     */
/* Inputs      : buffer and pointer to return byte                          */
/* Outputs     : Returns updated pointer                                    */
/****************************************************************************/
    MLocal Byte*
unStuffByte(Reg Byte *buffer, Reg Byte *value)
{
    if (*buffer == DLE)             /* Check for DLE character in buffer    */
        buffer++;                   /* bump pointer to second DLE           */

    *value = *buffer++;             /* Extract value of byte                */
    return(buffer);                 /* and return updated pointer           */
} /* unStuffByte() */

/****************************************************************************/
/* Function    : copyFrameToPacket                                          */
/* Purpose     : Copies buffer from a frame, checking for DLE stuffing      */
/* Inputs      : frame buffer, frame length and destination buffer          */
/* Outputs     : Returns length of destination buffer                       */
/****************************************************************************/
    MLocal Int16
copyFrameToPacket(Reg Byte *frame, Reg Int16 frameLen, Reg Byte *packet)
{
    Byte    *packetStart;           /* Copy of packet pointer for length    */

    packetStart = packet;           /* copy packet pointer for length calc  */

    while (frameLen--)
    {
        if ((*frame) == DLE)        /* Check for DLE in data stream         */
          if ((*(++frame)) == DLE)  /* Check for Double DLE in data stream  */
            frameLen--;             /* Decrement frame length counter       */

        *packet++ = *frame++;       /* Copy characters into packet buf      */
    } /* while */

    return(packet - packetStart);   /* return length of packet              */
} /* copyFrameToPacket() */

/****************************************************************************/
/* Function    : getRxChecksum                                              */
/* Purpose     : Extract received checksum from end of frame buffer         */
/* Inputs      : frame buffer, frame length                                 */
/* Outputs     : Returns checksum                                           */
/****************************************************************************/
    MLocal Byte
getRxChecksum(Reg Byte *frame, Reg Int16 *frameLen)
{
    frame+= (*(frameLen) - 1);      /* point to end of frame                */
                                    /* Check if checksum is stuffed DLE     */
    if ((*frame == DLE) && (*(frame - 1) == DLE))
    {
        (*frameLen)--;              /* Checksum is stuffed DLE so decrement */
        frame--;                    /* frame length to and point to 1st DLE */
    } /* if */

    (*frameLen)--;                  /* Side effect - Decrement length of    */
                                    /* frame after we remove the checksum   */
    return(*frame);                 /* Return value of checksum field and   */
} /* getRxChecksum() */

/****************************************************************************/
/* Function    : getRxFrameType                                             */
/* Purpose     : Extract frame type byte from frame buffer                  */
/* Inputs      : frame buffer, pointer to returned frame type               */
/* Outputs     : Returns adjusted frame buffer pointer                      */
/****************************************************************************/
    MLocal Byte*
getRxFrameType(Byte *frame, Byte *frameType)
{                                   /* Extract frame type from frame        */
                                    /* and return updated pointer           */
    return (unStuffByte(frame, frameType));
} /* getRxFrameType() */


/****************************************************************************/
/* Function    : transmitDataPacket                                         */
/* Purpose     : Sends a Data Packet to the serial port. Packet is copied to*/
/*               a buffer for possible re-transmission                      */
/* Inputs      : Packet data and packet length                              */
/* Outputs     : None but data is sent to serial port                       */
/****************************************************************************/
    Int16
transmitDataPacket(Byte *packet, Int16 packetLen)
{
    if (packetLen > dataFrame.length)
    {
        if (dataFrame.start != NULL)
            free(dataFrame.start);

        if ( (dataFrame.start = (Byte*) malloc(packetLen) ) == NULL)
            return(ERROR);
    } /* if */

    dataFrame.hdr = SET_FRAME_HEADER(DATA_FRAME, nextTxSeqNo);
    nextTxSeqNo = (nextTxSeqNo ? 0 : 1);

    dataFrame.length = packetLen;
    dataFrame.buffer = dataFrame.start;

    while (packetLen--)                  /* Copy data into buffer          */
    {
        *(dataFrame.buffer) = *packet++;
        dataFrame.buffer++;
    } /* while */

    if (dataPktHoldOff)
        return (OK);

    return(qsTransmitFrame(&dataFrame));
} /* transmitDataPacket */


     Void
resetQsSerialLink( Void )
{
    qsSerialFrame resetFrame;            /* Link Reset Frame                 */

    resetFrame.hdr = SET_FRAME_HEADER(LINK_RESET_FRAME, 0);

    resetFrame.length = 0;
    resetFrame.buffer = NULL;
    resetFrame.start  = NULL;

    if (qsTransmitFrame(&resetFrame) == OK)
        sendLinkReset = FALSE;
} /* resetQsSerialLink*/

