/******************************************************************************/
/* Copyright 1991 to 1996 MBARI                                               */
/******************************************************************************/
/* Summary  : Server for RS485 microcontroller serial protocol                */
/* Filename : sio32Server.c                                                   */
/* Author   : Andrew Pearce                                                   */
/* Project  : Tiburon ROV VME to Microcontroller Communications Interface     */
/* Version  : 2.1                                                             */
/* Created  : 11/01/91                                                        */
/* Modified : 03/08/96                                                        */
/* Archived :                                                                 */
/******************************************************************************/
/* Modification History:                                                      */
/* $Header: sio32Server.c,v 1.2 96/03/08 17:17:11 pean Exp $
 * $Log:        sio32Server.c,v $
 * Revision 1.2  96/03/08  17:17:11  17:17:11  pean (Andrew Pearce)
 * Added keep alive mode control. Cleaned up comments, fixed CHAN_CLOSE etc
 *
 *
 * Revision 1.1  92/06/29  13:45:36  13:45:36  pean (Andrew Pearce 408-647-3746)
 * Initial revision
 *
 */
/******************************************************************************/

#include "vxWorks.h"                /* vxWorks system declarations            */
#include "iosLib.h"                 /* vxWorks IO system declarations         */
#include "memLib.h"                 /* vxWorks Memory allocation declarations */
#include "taskLib.h"                /* vxWorks Task Libarary support decls    */
#include "msgQLib.h"                /* vxWorks Message Queue Library          */
#include "msgQSmLib.h"              /* vxWorks Sharded Memory Message Queues  */
#include "smNameLib.h"              /* vxWorks Shared Memory Name Database    */

#include "mbariTypes.h"             /* MBARI style guide type declarations    */
#include "sio32Drv.h"               /* SIO32 Driver information               */
#include "sio32_card.h"             /* SIO32 hardware information             */
#include "sio32Server.h"            /* Application specific declarations      */
#include "sio32Client.h"            /* Client side declarations               */

#define DLE 0x10                    /* Delete Character for Framing           */
#define STX 0x02                    /* Start of text character                */
#define ETX 0x03                    /* End of text character                  */

#define PROTO_HDR_SIZE       8      /* Protocol layer adds 8 bytes of overhead*/

#define DATA_FRAME        0x7e      /* Data Frame Type                        */
#define SRQ_FRAME         0x7d      /* Service Request Frame Type             */
#define SRQ_ACK_FRAME     0x7c      /* SRQ Frame Acknowledge                  */
#define KEEP_ALIVE_FRAME  0x7b      /* Keep Alive Frame                       */
#define LINK_RESET_FRAME  0x7a      /* Link Reset Frame                       */

#define FRAME_TYPE_P         0      /* Frame type field in frame              */
#define FRAME_DATA_P         1      /* Start of data field in frame           */
                                    /* Options for mutual exclusion semaphores*/
#define SEM_OPTIONS SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE

#define FRAME_TYPE(hdr)     (hdr & 0x7f)
#define FRAME_SEQ_NO(hdr)  ((hdr & 0x80) >> 7)

#define SET_FRAME_HEADER(type, seq) (Byte) (((seq << 7) & 0x80) | (type & 0x7f))

                                    /* Array of channel data structures       */
MLocal chanProto protoHdr[PORTS_PER_SIO32];

/************************** Forward Declarations ******************************/

Void sio32RxTask (char *serverName, chanProto *protoHdr );
Void sio32TxTask (char *serverName, chanProto *protoHdr,
     Nat16 channel, MSG_Q_ID replyQId, sio32InitReq *initReq);

MLocal  STATUS transmitPacket(MSG_Q_ID txQId, chanProto *protoHdr);

MLocal  Int32 initSio32Chan(Nat16 channel, sio32InitReq *initRequest);

MLocal  Byte  calcChecksum(FAST Byte *bytes, FAST Int16 len);
MLocal  Byte* unStuffByte(FAST Byte *buffer, FAST Byte *value);
MLocal  Void  resetSeqNos( FAST chanProto  *protoHdr );

MLocal  Byte  getRxChecksum(FAST Byte *frame, FAST Int16 *frameLen);
MLocal  Byte* getRxFrameType(FAST Byte *frame, FAST Byte *frameType);

MLocal  Int16 copyFrameToPacket(FAST Byte *frame, FAST Int16 frameLen,
                 FAST Byte *packet);

MLocal  STATUS readPacketFromMicro(FAST chanProto *protoHdr, FAST Char *buffer,
                 FAST Int32 maxBytes, Byte *pktType);

MLocal STATUS transmitFrame(chanProto *protoHdr, Byte frameType,
                 Byte *packet, Int32 packetLen);

MLocal Void sio32SetKeepAliveMode(sio32Request *sio32Req);
MLocal Void checkPSUStatus(sio32Request *sio32Req);
MLocal Void sio32GetStatistics(sio32Request *sio32Req);
MLocal Void sio32ClearStatistics(Nat16 channel);

/******************************************************************************/
/* Function  : sio32DeamonTask                                                */
/* Purpose   : Initializes serial protocol services for GMS & telemetry boards*/
/*             Waits on shared memory message Q for requests from sio32Cclient*/
/*             This task runs on the GMS board or on a CPU board if its       */
/*             controlling the telemetry board uart. It is spawned during     */
/*             initialization and runs forever. When a request arrives, two   */
/*             tasks are spawned (TxTask & RxTask) which handle all subsequent*/
/*             requests for communication with that SIO32 channel.            */
/*                                                                            */
/* Inputs    : server name for Message Q Name                                 */
/* Outputs   : None - runs forever                                            */
/******************************************************************************/
    STATUS
sio32DeamonTask( const char* serverName )
{
    sio32Request sio32Req;          /* Requests from client tasks             */
    sio32InitReq *initReq;          /* Parameters for Channel Initialization  */

    MSG_Q_ID  smRequestQId;         /* Server Request Message Queue           */
    MSG_Q_ID  smStatusQId;          /* Server Status Message Queue            */
    char      name[MAX_CHAN_NAME];  /* Name string for tasks and msg Q's      */
    Nat16     channel;              /* Sio32 Channel number                   */

                                    /* Clear channel data structure           */
    bzero(protoHdr, sizeof(protoHdr));

                                    /* Initialize SIO32 serial interface drv  */
    if (sio32Drv((Nat16) INT_VEC_SIO32, (Nat16) INT_LVL_SIO32) != OK)
    {
        logMsg("SIO32 Driver could not be initialized");
        return(ERROR);
    } /* if */

                                    /* Create mesage queues for request from  */
                                    /* and status messages to sio32Clients    */
    if ((smRequestQId = msgQSmCreate(MAX_REQ_MSG, sizeof(sio32Request),
        MSG_Q_FIFO)) == NULL)
    {
        logMsg("Error Creating SIO32 Request Queue\n");
        return (ERROR);
    } /* if */

    if ((smStatusQId = msgQSmCreate(MAX_REQ_MSG, MAX_REQ_MSG_LEN,
        MSG_Q_FIFO)) == NULL)
    {
        logMsg("Error Creating SIO32 Status Queue\n");
        return (ERROR);
    } /* if */
                                    /* Assign names to message Q's            */
    sprintf(name, "/%s/%s", serverName, SIO32_REQUEST_Q);
    smNameAdd (name, smRequestQId, T_SM_MSG_Q);

    sprintf(name, "/%s/%s", serverName, SIO32_STATUS_Q);
    smNameAdd (name, smStatusQId, T_SM_MSG_Q);

    FOREVER
    {                               /* Wait for requests from sio32Client     */
        if (msgQReceive(smRequestQId, (char *) &sio32Req, sizeof(sio32Req),
            WAIT_FOREVER) != ERROR)
        {
            switch (sio32Req.cmd)   /* Request received so decode & process it*/
            {
              case SIO32_INIT_CHAN: /* SIO32 Channel Initialization Request   */
                channel = sio32Req.channel;

                                    /* Range check channel number             */
                if (channel >= PORTS_PER_SIO32)
                    break;
                                    /* Create child task name from channel no */
                sprintf(name, "sio32Tx%02d", channel);

                                    /* Create channel initialize structure    */
                if ((initReq = malloc(sizeof(sio32InitReq))) != NULL)
                {
                    protoHdr[channel].channel = channel;
                    bcopy(sio32Req.args, initReq, sizeof(sio32InitReq));

                                    /* Spawn transmit task for this channel   */
                    taskSpawn(name, SIO32_TX_PRI, 0, SIO32_TX_STACK,
                       (FUNCPTR) sio32TxTask,    (int) serverName,
                       (int) (protoHdr + channel), channel,
                       (int) sio32Req.replyQId,  (int) initReq, 0, 0, 0, 0, 0);
                } /* if */

                break;

              case SIO32_CLOSE_CHAN:/* SIO32 Channel Close Request            */
                channel = sio32Req.channel;

                                    /* Range check channel number             */
                if (channel >= PORTS_PER_SIO32)
                    break;
                                    /* Delete Receive Task                    */
                sprintf(name, "sio32Rx%02d", channel);
                taskDelete(taskNameToId(name));

                                    /* Delete Tranmsit Task                   */
                sprintf(name, "sio32Tx%02d", channel);
                taskDelete(taskNameToId(name));

                if (protoHdr[channel].sio32Fd != 0)
                    close (protoHdr[channel].sio32Fd);
                break;

              case SIO32_SET_KA_MODE:   /* Set Keep Alive Packet Mode         */
                sio32SetKeepAliveMode(&sio32Req);
                break;

              case SIO32_GET_STATS: /* Read data transmission statistics      */
                sio32GetStatistics(&sio32Req);
                break;

              case SIO32_CLR_STATS: /* Clear data transmission statistics     */
                sio32ClearStatistics(sio32Req.channel);
                break;

              case SIO32_PS_STATUS: /* Check SIO32 Power Supply Status        */
                checkPSUStatus(&sio32Req);
                break;

            } /* switch */
        } /* if */

        else
            logMsg("SIO32 Deamon: Request Error\n");
    } /* forever */

    return(OK);                     /* We never get here but just to be tidy  */
} /* sio32Deamon() */

/******************************************************************************/
/* Function    : sio32TxTask                                                  */
/* Purpose     : Processes packet transmit requests from the client to sio32  */
/* Inputs      : Client network address, sio32 channel No and channel name    */
/* Outputs     : None - runs forever                                          */
/******************************************************************************/
    Void
sio32TxTask (char *serverName, chanProto *protoHdr, Nat16 channel,
    MSG_Q_ID replyQId, sio32InitReq *initReq)
{
    MSG_Q_ID sio32TxQId;            /* Transmit Data Message Queue            */
    Int32    status;                /* Return status from initSio32Chan()     */
    Int32    sio32Fd;               /* Sio32 serial cahnnel file descriptor   */
    char     txName[MAX_CHAN_NAME]; /* Channel Tx Message Q Name              */

                                    /* Initialize SIO32 serial channel        */
    if ((sio32Fd = initSio32Chan(channel, initReq))  == ERROR)
        status = errnoGet();        /* An error occured so return error code  */
    else
        status = OK;                /* Channel initialized OK                 */

    if (status != OK)
    {                               /* Send SIO32 init status to client task */
        if (msgQSend (replyQId, (char *) &status, sizeof(status),
            WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)
        {
            logMsg("SIO32 server: error sending init status to client\n");
            if (status == OK)
                close (sio32Fd);
            return;
        } /* if */

        if (status != OK)           /* Could not initialize SIO32 serial chan */
            return;                 /* So exit                                */
    } /* if */

                                    /* Create Transmit Data Message Q Name    */
    sprintf(txName, "/%s/sio32Tx%02d", serverName, channel);

    if ((sio32TxQId = msgQSmCreate(MAX_PKT_MSG, sizeof(sio32Packet),
        MSG_Q_FIFO)) == NULL)
    {
        logMsg("Error Creating SIO32 Tx Queue for channel %d\n", channel);
        return;
    } /* if */
                                    /* Initialize channel data structure      */
    protoHdr->sio32Fd  = sio32Fd;   /* Save sio32 serial chan file descriptor */
    protoHdr->channel  = channel;   /* Copy channel number to proto header    */
    protoHdr->protocol = initReq->protocol;

                                    /* Copy keep alive packet config params   */
    if (initReq->keepAlive.ticks == 0) /* Use default if 0 is specified       */
        protoHdr->keepAliveMode.ticks = KEEP_ALIVE_TIME;
    else
        protoHdr->keepAliveMode.ticks = initReq->keepAlive.ticks;
    protoHdr->keepAliveMode.count = initReq->keepAlive.count;

    resetSeqNos(protoHdr);          /* Reset frame sequence numbers           */

    sio32ClearStatistics(channel);  /* Clear Statistics                       */

                                    /* Create frame received semaphore        */
    protoHdr->frameRecvdSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);

                                    /* Create Write mutual exclustion sem     */
    protoHdr->writeMutexSem = semMCreate(SEM_OPTIONS);

                                    /* Add transmit Q name for client         */
    smNameAdd (txName, sio32TxQId, T_SM_MSG_Q);

    free(initReq);                  /* Finished with init params malloced by  */
                                    /* sio32DeamonTask                        */

                                    /* Create child task name from channel no */
    sprintf(txName, "sio32Rx%02d", protoHdr->channel);

                                    /* Spawn frame receive task for channel   */
    taskSpawn(txName, SIO32_RX_PRI, 0, SIO32_RX_STACK, (FUNCPTR) sio32RxTask,
        (int) serverName, (int) protoHdr, 0, 0, 0, 0, 0, 0, 0, 0);

    /**************************************************************************/
    /* End of initialization section. Task enters an infinite loop listening  */
    /* packets from the client and sending them to the microcontroller via    */
    /* sio32 driver.                                                          */
    /**************************************************************************/

    if (protoHdr->protocol == RS485_MODE)
    {
      if (protoHdr->debugLevel)
        logMsg("Chan %d: Sending Link Reset\n", protoHdr->channel);

                                   /* Send Link reset to microcontroller      */
      if (transmitFrame(protoHdr, SET_FRAME_HEADER(LINK_RESET_FRAME, 0),
		NULL, 0) != OK)
        if (protoHdr->debugLevel)
	  logMsg("Chan %d: Link reset failed\n", protoHdr->channel);

    } /* if */

    if (msgQSend (replyQId, (char *) &status, sizeof(status),
                  WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)
    {
        logMsg("SIO32 server: error sending init status to client\n");
        if (status == OK)
            close (protoHdr->sio32Fd);
            return;
    } /* if */
                                   /* Infinite loop sends packets to micro    */
    transmitPacket(sio32TxQId, protoHdr);

} /* sio32TxTask() */

/******************************************************************************/
/* Function    : sio32RxTask                                                  */
/* Purpose     : Processes packets received from the microcontroller          */
/* Inputs      : Client data & SRQ sockets, channel protocol data structure   */
/* Outputs     : None - runs forever                                          */
/******************************************************************************/
    Void
sio32RxTask( char *serverName, chanProto *protoHdr)
{
    MSG_Q_ID sio32RxQId;            /* Receive Packet Message Queue           */
    MSG_Q_ID sio32SrqQId;           /* SRQ Packet Message Queue               */
    char     rxQName[MAX_CHAN_NAME];/* Channel Receive Message Q Name         */
    Byte     pktType;               /* Type of packet received                */
    Int32    status;                /* Status of msgQSend                     */
    Int32    nBytes;                /* Return status from sio32Read()         */

    Byte packet[MAX_PACKET_LEN *2]; /* buffer to hold the packet data & header*/

                                    /* Create Receive Data Message Q Name     */
    sprintf(rxQName, "/%s/sio32Rx%02d", serverName, protoHdr->channel);

    if ((sio32RxQId = msgQSmCreate(MAX_PKT_MSG, sizeof(sio32Packet),
        MSG_Q_FIFO)) == NULL)
    {
        logMsg("Error Creating SIO32 Rx Data Packet Queue for channel %d\n",
               protoHdr->channel);
        return;
    } /* if */

    smNameAdd (rxQName, sio32RxQId, T_SM_MSG_Q);

                                    /* Create SRQ Packet Message Q Name       */
    sprintf(rxQName, "/%s/sio32SRQ%02d", serverName, protoHdr->channel);

    if ((sio32SrqQId = msgQSmCreate(MAX_PKT_MSG, sizeof(sio32Packet),
        MSG_Q_FIFO)) == NULL)
    {
        logMsg("Error Creating SIO32 SRQ Packet Queue for channel %d\n",
               protoHdr->channel);
        return;
    } /* if */

    smNameAdd (rxQName, sio32SrqQId, T_SM_MSG_Q);

    FOREVER
    {                               /* Wait for packets from microcontroller  */
        if (protoHdr->protocol != RS485_MODE)
        {
            if ( (nBytes = read(protoHdr->sio32Fd, (char*) packet,
                MAX_PACKET_LEN) ) != ERROR)
            {                       /* Increment packet recevied count        */
                protoHdr->statistics.totalRecvd++;
                if (protoHdr->debugLevel)
                    logMsg("Pkt Rx len %d\n", nBytes);

                                    /* Send packet to client task             */
                status = msgQSend(sio32RxQId, (char *) packet, nBytes,
                    SIO32_MSG_Q_TIMEOUT, MSG_PRI_NORMAL);

                if ((protoHdr->debugLevel) && (status != OK))
                        logMsg("Client RS232 write failed\n");
            } /* if */
        } /* if */

        else
        {                           /* Wait for packets from microcontroller  */
            if ((nBytes = readPacketFromMicro(protoHdr, packet,
                         MAX_PACKET_LEN + PROTO_HDR_SIZE, &pktType)) == ERROR)
                                    /* Increment received error count         */
                protoHdr->statistics.rxError++;

            if ((nBytes != ERROR) && (nBytes <= MAX_PACKET_LEN+PROTO_HDR_SIZE))
            {                       /* If a frame was received then           */
                                    /* Increment packet recevied count        */
                protoHdr->statistics.totalRecvd++;

                if (pktType == SRQ_FRAME)
                {                   /* SRQ Packet so send it to the client    */

                    if (protoHdr->debugLevel > 1)
                        logMsg("Pkt SRQ Rx len %d\n", nBytes);

                                    /* Send packet to client task             */
                    status = msgQSend(sio32SrqQId, (char *) packet,
                        nBytes, SIO32_MSG_Q_TIMEOUT, MSG_PRI_NORMAL);

                    if ((protoHdr->debugLevel) && (status != OK))
                        logMsg("Client SRQ Packet write failed\n");
                } /* else */

                else if (pktType == DATA_FRAME)
                {                   /* Data packet received so send client    */

                    if (protoHdr->debugLevel > 1)
                        logMsg("Pkt Rx len %d\n", nBytes);

                                    /* Send packet to client task             */
                    status = msgQSend(sio32RxQId, (char *) packet,
                        nBytes, SIO32_MSG_Q_TIMEOUT, MSG_PRI_NORMAL);

                    if ((protoHdr->debugLevel) && (status != OK))
                        logMsg("Client Data Packet write failed\n");
                } /* else */
            } /* if */
        } /* else */

   } /* FOREVER */

} /* sio32RxTask() */

/******************************************************************************/
/* Function    : transmitPacket                                               */
/* Purpose     : Reads packet from the client and send it to microcontroller  */
/* Inputs      : Client socket, pointer to packet header and packet buffer    */
/* Outputs     : Returns OK or ERROR                                          */
/******************************************************************************/
    MLocal STATUS
transmitPacket(MSG_Q_ID sio32TxQId, chanProto *protoHdr)
{
    Int32 len;                      /* Length of packet                       */
    Byte  frameType;                /* Frame type and seqence number          */
    Byte  packet[MAX_PACKET_LEN];   /* buffer to hold the packet data         */
    Nat32 keepAliveCount = 0;       /* Consecutive keep alive packets sent    */

    FOREVER                         /* Repeat sending packets forever         */
    {
                                    /* Serial protocol sets operating mode    */
        if (protoHdr->protocol == RS485_MODE)
	{                           /* ********** RS485 Mode **************** */
         
                                    /* Read incomming packet from client task */
            len = msgQReceive(sio32TxQId, (char *) &packet, MAX_PACKET_LEN,
                protoHdr->keepAliveMode.ticks);

            if (len != ERROR)       /* Packet received from client side       */
            {
                if (protoHdr->debugLevel > 1)
                    logMsg("Pkt Rx from Client\n");

                                    /* Define frame type and sequence number  */
                frameType = SET_FRAME_HEADER(DATA_FRAME, protoHdr->nextTxSeqNo);
                protoHdr->nextTxSeqNo = (protoHdr->nextTxSeqNo == 0 ? 1 : 0);
                                    /* Send packet to microcontroller and     */
                                    /* retry until a reply is received        */
                transmitFrame(protoHdr, frameType, packet, len);

                keepAliveCount = 0; /* Reset keep alive packet counter        */
                                    /* Increment statistics                   */
                protoHdr->statistics.totalSent++;
            } /* if */

            else                   /* semTake timed out so send Keep Alive    */
            {                      /* Check num of consecutive KA's sent      */
                if ( (protoHdr->keepAliveMode.count == 0) ||
                     (keepAliveCount < protoHdr->keepAliveMode.count) )
                {
                    if (protoHdr->debugLevel > 2)
                        logMsg("Tx: Keep Alive\n");

                    keepAliveCount++; /* Increment consecutive number sent    */
                    transmitFrame(protoHdr,
                       SET_FRAME_HEADER(KEEP_ALIVE_FRAME, 0), NULL, 0);

                    if (keepAliveCount == protoHdr->keepAliveMode.count)
                        logMsg("Keep Alive Packets Stopped\n");
                } /* if */
            } /* else */
	} /* if RS485 Mode */

	else			    /* ******* RS232 or NO_PROTO_MODE ******* */
	{                           /* Read incomming packet from client task */
            if ((len = msgQReceive(sio32TxQId, (char *) &packet, MAX_PACKET_LEN,
                WAIT_FOREVER)) != ERROR)
	    {                       /* Packet received from client side       */
                                    /* Increment statistics                   */
	      protoHdr->statistics.totalSent++;
	      if (protoHdr->debugLevel > 1)
		logMsg("Pkt Rx from Client\n");

	      packet[len] = 0;    /* Null terminate packet                  */
	      write(protoHdr->sio32Fd, (char*) packet, len);
            } /* if */
        } /* if */
	
    } /* FOREVER */
} /* transmitPacket() */

/******************************************************************************/
/* Function    : transmitFrame                                                */
/* Purpose     : Encapsulates packet in frame and sends it to microcontroller */
/* Inputs      : Channel protocol data structure, packet, packet type & length*/
/* Outputs     : OK If frame sent and acknowledged, or ERROR                  */
/******************************************************************************/
    MLocal STATUS
transmitFrame(chanProto *protoHdr, Byte frameType,
    Byte *packet, Int32 packetLen)
{
                                    /* buffer to hold the packet data         */
    Byte  frame[MAX_PACKET_LEN + PROTO_HDR_SIZE];
    Nat32 frameLen;                 /* Length of frame                        */
    Int16 txTrys;                   /* counts transmit retry attempts         */
    STATUS status;                  /* Status of write function               */
                                    /* Add space for frame type               */
    frameLen = packetLen + sizeof(Byte);

    frame[FRAME_TYPE_P] = frameType;/* Add frame type descriptor to frame     */

    if (packetLen)                  /* Copy packet data to frame buffer       */
        bcopy(packet, &frame[FRAME_DATA_P], packetLen);

    txTrys = 0;
    while (txTrys++ < MAX_TX_TRYS)  /* Resend frame if an error occurred      */
    {
        if (protoHdr->debugLevel > 2)
            logMsg("Tx Data:%d\n", FRAME_SEQ_NO(frameType));

                                    /* Get exclusive access to sio32 channel  */
        semTake(protoHdr->writeMutexSem, WAIT_FOREVER);
                                    /* Sent the packet to the serial port     */
        status = write(protoHdr->sio32Fd, (char *) frame, frameLen);
                                    /* Release access to sio32 channel        */
        semGive(protoHdr->writeMutexSem);

        if (status == ERROR)
        {                           /* If we did not succesfully send the     */
                                    /* packet then increment error statistics */
            protoHdr->statistics.txError++;
            if (protoHdr->debugLevel)
                logMsg("Pkt dropped - write failed\n");
        } /* if */

        else
        {                           /* No reply expected for KEEP_ALIVE and   */
                                    /* SRQ_ACK and LINK_RESET frames so exit  */
            if (FRAME_TYPE(frameType) != DATA_FRAME)
                return (OK);

                                    /* Data Frame Send so wait for reply      */
                                    /* Wait for a packet back from micro      */
            if (semTake(protoHdr->frameRecvdSem, FRAME_RX_TIME) == OK)
                return(OK);

            else if (protoHdr->debugLevel)
                logMsg("Tx task resend packet\n");
        } /* else */
    } /* while */

    if (protoHdr->debugLevel)
        logMsg("Tx task dropped packet\n");

    return (ERROR);
} /* transmitFrame() */

/****************************************************************************/
/* Function    : copyFrameToPacket                                          */
/* Purpose     : Extract data portion of frame from frame buffer            */
/* Inputs      : Frame pointer, frame length and packet buffer pointer      */
/* Outputs     : Returns length of packet                                   */
/****************************************************************************/
    MLocal Int16
copyFrameToPacket(FAST Byte *frame, FAST Int16 frameLen, FAST Byte *packet)
{
    Byte    *packetStart;           /* Copy of packet pointer for length    */

    packetStart = packet;           /* copy packet pointer for length calc  */

    while (frameLen--)              /* Repeat for all bytes in frame buffer */
    {
        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    : readPacketFromMicro                                        */
/* Purpose     : Processes frames received from microcontroller             */
/* Inputs      : Channel protocol data, packet buffer pointer & buffer len  */
/* Outputs     : Returns length of packet, or ERROR                         */
/****************************************************************************/
    MLocal STATUS
readPacketFromMicro(protoHdr, buffer, maxBytes, pktType)
    FAST chanProto  *protoHdr;     /* pointer to channel specific data      */
    FAST Char       *buffer;       /* user buffer for reading the data into */
    FAST Int32      maxBytes;      /* maximum number of bytes to read in    */
    Byte *pktType;
{
    struct
    {
        Int16 length;               /* Received frame length                */
        Byte  type;                 /* Received frame type                  */
        Byte  checksum;             /* Received frame checksum              */
        Byte  *data;                /* Pointer to data portion of frame     */
    } rxFrame;                      /* Information on received frame        */

    Byte    checksum;               /* Checksum calcuated from Rx bytes     */
    Int16   packetLen;              /* Length of data portion of frame      */
    Byte    frameType;

    packetLen = 0;                  /* Set returned packet length to 0      */
    rxFrame.data = buffer;          /* Pointer to frame buffer              */

    if ((rxFrame.length =
        read(protoHdr->sio32Fd, (char*) rxFrame.data, maxBytes)) == ERROR)
    {
        if (protoHdr->debugLevel)
            logMsg ("read error\n");
        return(ERROR);
    } /* if */
                                    /* 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)
    {
        if (protoHdr->debugLevel)
            logMsg("Rx checksum error\n");
        return(ERROR);              /* Discard frame and return zero length */
    } /* if */

                                    /* Extract frame type from Rx frame     */
    rxFrame.data = getRxFrameType(rxFrame.data, &rxFrame.type);

                                    /* calc length of data portion of frame */
    rxFrame.length -= rxFrame.data - buffer;

                                    /* Return packet type                   */
    *pktType = (frameType = FRAME_TYPE(rxFrame.type));

    if (frameType == LINK_RESET_FRAME)
    {
        if (protoHdr->debugLevel)
            logMsg("Link Reset Received\n");
        resetSeqNos(protoHdr);
    } /* if */

    else if (frameType == SRQ_FRAME)
    {                               /* SRQ Frame so send acknowledge        */
        rxFrame.type =
            SET_FRAME_HEADER(SRQ_ACK_FRAME, FRAME_SEQ_NO(rxFrame.type));

        transmitFrame(protoHdr, rxFrame.type, NULL, 0);

        if (FRAME_SEQ_NO(rxFrame.type) == protoHdr->nextRxSrqSeqNo)
        {                           /* Increment frame sequence number      */
            protoHdr->nextRxSrqSeqNo = (protoHdr->nextRxSrqSeqNo ? 0 : 1);

            if (rxFrame.length > 0) /* Check length of data field           */
                                    /* Now extract data portion of frame    */
                packetLen +=
                    copyFrameToPacket(rxFrame.data, rxFrame.length, buffer);
        } /* if */
        else
        {
            if (protoHdr->debugLevel)
                logMsg("Duplicate SRQ packet recvd\n");
            packetLen = ERROR;
        } /* else */
    } /* if */

    else if (frameType == DATA_FRAME)
    {
        if (protoHdr->debugLevel > 2)
            logMsg ("Rx Data: %d %d %d\n",rxFrame.length,
                FRAME_SEQ_NO(rxFrame.type), protoHdr->nextRxSeqNo);

        if (FRAME_SEQ_NO(rxFrame.type) == protoHdr->nextRxSeqNo)
        {                           /* Increment frame sequence number      */
            protoHdr->nextRxSeqNo = (protoHdr->nextRxSeqNo ? 0 : 1);

                                    /* Wakeup tasks waiting for recvd frames*/
            semGive(protoHdr->frameRecvdSem);

            if (rxFrame.length > 0) /* Check length of data field           */
                packetLen +=        /* Now extract data portion of frame    */
                    copyFrameToPacket(rxFrame.data, rxFrame.length, buffer);
        } /* if */
        else
        {
            if (protoHdr->debugLevel)
                logMsg("Dup Data %d\n", FRAME_SEQ_NO(rxFrame.type));

            packetLen = ERROR;
        } /* else */
    } /* if */
    return (packetLen);
} /* readPacketFromMicro() */

/******************************************************************************/
/* Function    : checkPSUStatus                                               */
/* Purpose     : Reads Power Supply status from SIO32 mezzanine and sends it  */
/*               to the client                                                */
/* Inputs      : Client socket, client address                                */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
checkPSUStatus(sio32Request *sio32Req)
{
    Int32   PSGoodStatus;           /* Holds PS_GOOD Status Bits              */

                                    /* Read state of PS1_GOOD and PS2_GOOD    */
    PSGoodStatus = sio32PSGoodStatus();

    if (msgQSend (sio32Req->replyQId,
        (char *) &PSGoodStatus, sizeof(PSGoodStatus), WAIT_FOREVER,
        MSG_PRI_NORMAL) == ERROR)
        logMsg("SIO32 server: error sending PS_GOOD status to client\n");
} /* checkPSUStatus() */

/******************************************************************************/
/* Function    : initSio32Chan                                                */
/* Purpose     : Initialize sio32 device. Performs sio32DevCreate and open    */
/* Inputs      : Sio32 channel number and channel name                        */
/* Outputs     : ERROR or sio32 file descriptor                               */
/******************************************************************************/
    MLocal Int32
initSio32Chan(Nat16 channel, sio32InitReq *initRequest)
{
    Int32   sio32Fd;                /* File descriptor for SIO32 serial device*/

                                    /* Create a vxWorks device to use for IO  */
                                    /* Baud rate, data, stop and parity bits  */
    if (sio32DevCreate(initRequest->name, channel,
        initRequest->baudRate, initRequest->dataBits, initRequest->stopBits,
        initRequest->parity,   initRequest->protocol) != OK)
        return(ERROR);
                                    /* and open the device                    */
    if ((sio32Fd = open(initRequest->name, UPDATE, 0)) == ERROR)
        return(ERROR);

    return(sio32Fd);                /* return file descriptor to the caller   */
} /* initSio32Chan */

/****************************************************************************/
/* Function    : calcChecksum                                               */
/* Purpose     : Calculates a checksum. Value is 8 bit sum of all bytes     */
/* Inputs      : Buffer of bytes and length of buffer                       */
/* Outputs     : Returns checksum                                           */
/****************************************************************************/
    MLocal Byte
calcChecksum(FAST Byte *bytes, FAST Int16 len)
{
    FAST Nat16   i;                     /* loop counter                     */
    FAST 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     : Read byte from frame buffer and checks for byte stuffing   */
/* Inputs      : Buffer of bytes and pointer to return byte                 */
/* Outputs     : Returns byte and adjusted buffer pointer                   */
/****************************************************************************/
    MLocal Byte*
unStuffByte(FAST Byte *buffer, FAST 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    : getRxChecksum                                              */
/* Purpose     : Read checksum from received frame and check for stuffing   */
/* Inputs      : Frame pointer and frame length                             */
/* Outputs     : Returns received frame checksum                            */
/****************************************************************************/
    MLocal Byte
getRxChecksum(FAST Byte *frame, FAST 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     : Read frame type from received frame and check for stuffing */
/* Inputs      : Frame pointer and pointer to frame type                    */
/* Outputs     : Returns adjusted frame pointer                             */
/****************************************************************************/
    MLocal Byte*
getRxFrameType(FAST Byte *frame, FAST Byte *frameType)
{                                   /* Extract frame type from frame        */
                                    /* and return updated pointer           */
    return (unStuffByte(frame, frameType));
} /* getRxFrameType() */

/****************************************************************************/
/* Function    : resetSeqNos                                                */
/* Purpose     : Resets frame sequence numbers for transmit and receives    */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
resetSeqNos(FAST chanProto  *protoHdr)
{
    protoHdr->nextTxSeqNo = 0;     /* Next transmit frame sequence number   */
    protoHdr->nextRxSeqNo = 0;     /* Next receive frame sequence number    */
    protoHdr->nextRxSrqSeqNo = 0;  /* Next receive SRQ frame sequence number*/
} /* resetSeqNos() */

/*****************************************************************************/
/* Function    : sio32ClearStatistics                                        */
/* Purpose     : Initializes frame transmit/receive statistics               */
/* Inputs      : pointer to Channel statistics data structure                */
/* Outputs     : None                                                        */
/*****************************************************************************/
    Void
sio32ClearStatistics(Nat16 channel)
{
    if (channel < PORTS_PER_SIO32)
    {
        taskLock();
        bzero(protoHdr[channel].statistics, sizeof(pktStat));
        taskUnlock();
    } /* if */
} /* sio32ClearStatistics() */

/******************************************************************************/
/* Function    : sio32GetStatistics                                           */
/* Purpose     : Write channel data transmission statistics to client         */
/* Inputs      : Client socket, client address, pointer to channel statistics */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32GetStatistics(sio32Request *sio32Req)
{
                                    /* Send SIO32 init status to client task */
    if (msgQSend (sio32Req->replyQId,
        (char *) &protoHdr[sio32Req->channel].statistics,
        sizeof(pktStat), WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)
        logMsg("SIO32 server: error sending statistics to client\n");
} /* sio32GetStatistics() */

/******************************************************************************/
/* Function    : sio32DebugLevel                                              */
/* Purpose     : User command to set the debug message level for a channel    */
/* Inputs      : Channel number and desired debug level (0, 1, 2, or 3)       */
/* Outputs     : None                                                         */
/******************************************************************************/
    Void
sio32DebugLevel( Nat16 channel, Nat16 debug )
{
    protoHdr[channel].debugLevel = debug;
} /* sio32DebugLevel() */

/******************************************************************************/
/* Function    : sio32SetKeepAliveMode                                        */
/* Purpose     : Called from client. Set keep alive mode params ticks & count */
/* Inputs      : sio32Request from sio32Client                                */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32SetKeepAliveMode(sio32Request *sio32Req)
{
    Nat32     channel;              /* Sio32 Channel number                   */
    sio32KeepAliveMode *kaMode;     /* Holds args for channel initialization  */
    Int32    status = OK;           /* Return status from initSio32Chan()     */

    channel = sio32Req->channel;    /* Extract channel number                 */
                                    /* Overlay argument structure             */
    kaMode = (sio32KeepAliveMode *) sio32Req->args;

    if (kaMode->ticks == 0)         /* Use default if 0 is specified          */
        kaMode->ticks = KEEP_ALIVE_TIME;

    taskLock();
    protoHdr[channel].keepAliveMode.ticks = kaMode->ticks;
    protoHdr[channel].keepAliveMode.count = kaMode->count;
    taskUnlock();

                                    /* Send status to client task             */
    if (msgQSend (sio32Req->replyQId, (char *) &status, sizeof(status),
          WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)
        logMsg("SIO32 server: error sending ka mode status to client\n");
} /* sio32SetKeepAliveMode() */



