/****************************************************************************/
/* Copyright 1990 to 1994 MBARI                                             */
/****************************************************************************/
/* Summary  : Microcontroller serial communications protcol layer           */
/* Filename : proto.c                                                       */
/* Author   : Andrew Pearce                                                 */
/* Project  : ROV 1.5 Microcontroller Board Rev 2.0                         */
/* Version  : 2.0                                                           */
/* Created  : 04/06/92                                                      */
/* Modified : 08/23/94                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/lib/proto.c,v 1.1.1.1 1997/05/02 17:16:00 pean Exp $
 * $Log: proto.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:31  09:14:31  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 "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 "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 Int16 sendSRQPacket( Void );
MLocal Void dequeueSRQPacket( Byte ackSeqNo );
MLocal Void resetSerialLink( Void );

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   */

MLocal serialFrame dataFrame;           /* Last Data Frame sent for re-txmit*/
MLocal LstHead SRQPacketHead;           /* Head of SRQ packet Queue         */
MLocal Boolean sendLinkReset;           /* Force next frame to be reset link*/
MLocal Boolean dataPktHoldOff;          /* Hold of data pkt Tx if SRQ sent  */

#define SRQ_ACK_PKT_COUNT 2             /* No of recvd packets before resend*/

/****************************************************************************/
/* Function    : initProtocolLayer                                          */
/* Purpose     : Initializes Protocol Layer data structures                 */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initProtocolLayer( 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  */

    list_init(&SRQPacketHead);             /* Initialize SRQ Packet list    */
} /* initProtocolLayer() */

/****************************************************************************/
/* Function    : resetProtocolLayer                                         */
/* Purpose     : Resets Protocol Layer data structures                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
resetProtocolLayer( Void )
{
    Reg SRQFrame *srqFrame;

    nextRxSeqNo  = 0;
    nextTxSeqNo  = 0;
    nextSrqSeqNo = 0;

    sendLinkReset = FALSE;                 /* Don't send Link Reset frame   */
    dataPktHoldOff = FALSE;                /* Clear Data Pkt hold off flag  */

    while ( (srqFrame = (SRQFrame *) list_head(&SRQPacketHead)) != NULLNODE)
    {                                      /* Get pointer to first packet   */
        free (srqFrame->frame.start);      /* Free SRQ data buffer          */
        free ((Byte*) srqFrame);           /* Free SRQ frame resources      */
    } /* if */

    list_init(&SRQPacketHead);             /* Initialize SRQ Packet list    */
} /* resetProtocolLayer() */

/****************************************************************************/
/* Function    : readRecvdPacket                                            */
/* 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
readRecvdPacket(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)
            resetProtocolLayer();   /* Reset sequence numbers               */

        else if (sendLinkReset)     /* We need to reset link seq nos        */
            resetSerialLink();      /* 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
                    transmitFrame(&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;
                transmitFrame(&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;
                transmitFrame(&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    : enqueueSRQPacket                                           */
/* Purpose     : Adds a Service Request packet to SRQ packet queue          */
/* Inputs      : Buffer containing packet and packet length                 */
/* Outputs     : Returns ERROR or OK                                        */
/****************************************************************************/
    Int16
enqueueSRQPacket(Byte *packet, Int16 packetLen)
{
    Reg SRQFrame *srqFrame;

    if ( (srqFrame = (SRQFrame *) malloc(sizeof(SRQFrame) ) ) == NULL )
        return(ERROR);

    if ( (srqFrame->frame.start = (Byte *) malloc(packetLen) )
        == NULL )
    {
        free((Byte *) srqFrame);
        return(ERROR);
    } /* if */

    srqFrame->frame.hdr = SET_FRAME_HEADER(SRQ_FRAME, nextSrqSeqNo);
    nextSrqSeqNo = (nextSrqSeqNo == 0 ? 1 : 0);

    srqFrame->frame.length = packetLen;
    srqFrame->frame.buffer = srqFrame->frame.start;
    srqFrame->ackCount     = SRQ_ACK_PKT_COUNT;

    while (packetLen--)                  /* Copy data into malloced buffer */
    {
        *(srqFrame->frame.buffer) = *packet++;
        srqFrame->frame.buffer++;
    } /* while */
                                         /* Reset pointer to start of frame*/
    srqFrame->frame.buffer = srqFrame->frame.start;

                                         /* Add SRQ packet to SRQ packet Q */
    list_add(&SRQPacketHead, (Node *) srqFrame);
} /* enqueueSRQPacket() */

/****************************************************************************/
/* Function    : sendSRQPacket                                              */
/* Purpose     : Sends any SRQ packets on the SRQ packet work Q             */
/* Inputs      : None                                                       */
/* Outputs     : Returns TRUE if SRQ Packet sent. Work Q is manipulated     */
/****************************************************************************/
    MLocal Int16
sendSRQPacket( Void )
{
    Reg SRQFrame *srqFrame;

    if (SRQPacketHead.lst_head != NULLNODE) /* If SRQ packets in the Q     */
    {                                   /* Get pointer to first packet     */
        srqFrame = (SRQFrame *) SRQPacketHead.lst_head;
                                        /* Wait for up to 2 recvd packets  */
                                        /* before resending SRQ packet     */
        if (srqFrame->ackCount++ >= SRQ_ACK_PKT_COUNT)
        {                               /* Send SRQ Packet at head of Q    */
            transmitFrame(&srqFrame->frame);
            srqFrame->ackCount = 0;     /* Reset recvd frame counter       */
            return (TRUE);
        } /* if */
    } /* if */

    return (FALSE);
} /* sendSRQPacket() */

/****************************************************************************/
/* Function    : dequeueSRQPacket                                           */
/* Purpose     : Remove Service Request packet from Head of SRQ packet queue*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
dequeueSRQPacket( Byte ackSeqNo )
{
    Reg SRQFrame *srqFrame;

    if (SRQPacketHead.lst_head != NULLNODE) /* If SRQ packets in the Q     */
    {                                   /* Get pointer to first packet     */
        srqFrame = (SRQFrame *) SRQPacketHead.lst_head;

                                        /* If seq no recvd = seq no sent   */
                                        /* then remove packet from Q       */
        if ( (FRAME_SEQ_NO(srqFrame->frame.hdr) == ackSeqNo) &&
           ( (srqFrame = (SRQFrame *) list_head(&SRQPacketHead)) != NULLNODE))
        {                               /* Get packet from SRQ work Q      */
           free (srqFrame->frame.start);/* Free SRQ data buffer            */
           free ((Byte*) srqFrame);     /* Free SRQ frame resources        */
        } /* if */
    } /* if */
} /* dequeueSRQPacket() */

/****************************************************************************/
/* 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(transmitFrame(&dataFrame));
} /* transmitDataPacket */


    MLocal Void
resetSerialLink( Void )
{
    serialFrame resetFrame;             /* Link Reset Frame                 */

    resetFrame.hdr = SET_FRAME_HEADER(LINK_RESET_FRAME, 0);

    resetFrame.length = 0;
    resetFrame.buffer = NULL;
    resetFrame.start  = NULL;

    if (transmitFrame(&resetFrame) == OK)
        sendLinkReset = FALSE;
} /* resetSerialLink() */
