/******************************************************************************/
/* Copyright 1991 to 1996 MBARI                                               */
/******************************************************************************/
/* Summary  : SIO32 UART Driver for vxWorks                                   */
/* Filename : sio32Drv.c                                                      */
/* Author   : Andrew Pearce                                                   */
/* Project  : Tiburon ROV                                                     */
/* Version  : Version 1.0                                                     */
/* Created  : 06/21/91                                                        */
/* Modified : 12/04/98                                                        */
/* Archived :                                                                 */
/******************************************************************************/

#include "vxWorks.h"                /* vxWorks system declarations            */
#include "iosLib.h"                 /* vxWorks IO system declarations         */
#include "semLib.h"                 /* vxWorks Semaphore declarations         */
#include "memLib.h"                 /* vxWorks Memory allocation declarations */
#include "rngLib.h"                 /* vxWorks ring buffer library            */
#include "taskLib.h"                /* vxWorks Task Libarary support decls    */
#include "vxLib.h"                  /* vxWorks vxTas routine definition       */
#include "configAll.h"              /* vxWorks configAll for INUM_TO_IVEC     */
#include "mbariTypes.h"             /* MBARI style guide type declarations    */
#include "mbariConst.h"             /* Miscellaneous constants                */
#include "ModNum.h"                 /* MBARI module numbers for error code    */
#include "sio32Drv.h"               /* SIO32 Driver information               */
#include "scc2698.h"                /* SCC 2698 hardware definitions          */

#ifdef BUILD_MV162_IP
#include "mv162-ip.h"               /* Motorola MV162 Board IP Module defs    */
#include "ipOctal.h"                /* IP Octal Board hardware information    */
#endif

#ifdef BUILD_TELEM_SIO32
#include "telem_card.h"             /* MBARI Telemetry Card hardware details  */
#endif

#undef  RX_QUIET_TIME
#define RX_QUIET_TIME        1      /* 1 msec at 38400 baud, 2 at 19200 etc   */
#define TX_DISABLE_DELAY    60      /* Time to send 1 byte at 38400 = 10/38400*/
                                    /* = 60 * sio32 timer period (0.000004340)*/

#define DLE 0x10                    /* Delete Character for Framing           */
#define STX 0x02                    /* Start of text character                */
#define ETX 0x03                    /* End of text character                  */
#define CR  0x0d                    /* Carriage Return                        */

typedef enum
     { IDLE, SOF_DLE, SOF_STX, DATA, CKSUM, EOF_DLE, EOF_ETX, EOF_END } txState;

typedef struct                      /* ****** Channel Control Structure ***** */
{
    DEV_HDR devHdr;                 /* vxWorks I/O device header              */
    Nat16   uart;                   /* Uart number for this channel           */
    Nat16   chan;                   /* Channel number in the UART             */
    MBool   created;                /* TRUE if device has been created        */
    MBool   chanOpen;               /* TRUE if device is currently open       */

    Int16   baudRate;               /* Baud rate for this channel             */
    Int16   dataBits;               /* Data Bits for this channel             */
    Int16   stopBits;               /* Stop Bits for this channel             */
    Int16   parity;                 /* Parity for this channel                */

    sioProto protocol;              /* Selects RS232 or RS485 protocol mode   */
    MBool    loopback;              /* Local loopback enable status           */

    RING_ID  rdBuf;                 /* ring buffer for read                   */
    RING_ID  wrtBuf;                /* ring buffer for write                  */

    SEM_ID   mutexSem;              /* Mutual exclusion semaphore             */
    MBool    wrtStateBusy;          /* Transmitter busy flag for TxStartUp    */
    MBool    discardRxByte;         /* Set to discard next character received */

    MBool    rxIdle;                /* Indicates no bytes have been received  */
    Nat16    rxIdleTime;            /* Receiver idle time for waitForRxIdle   */

    MBool    frameStartRecvd;       /* Frame start sequence received          */
    MBool    frameDelimRecvd;       /* Frame delimeter received flag          */

    txState  frameTxState;          /* Txmit Finite State Machine State       */
    MBool    frameTxDLESent;        /* True if DLE sent by transmitter        */
    Byte     frameTxChecksum;       /* Transmitted frame checksum             */

    SEM_ID   frameRecvdSem;         /* Frame recevied semaphore               */
    SEM_ID   frameSentSem;          /* Frame sent semaphore                   */

    SEM_ID   timerDoneSem;          /* Timer Expired semaphore                */
    SEM_ID   timerBusySem;          /* Timer Busy semaphore                   */
    Nat16    timerChan;             /* Channel using timer                    */

    volatile Byte *uartCommandReg;  /* UART Command Register Address          */
    volatile Byte *uartRxTxDataReg; /* UART Rx/Tx Data Register Address       */
    volatile Byte *uartIntStatusReg;/* UART Interrupt Status Register Address */
} sio32DevChan;

#define UART    sio32DevHeader->uart    /* macro to access a channel's uart   */
#define CHAN    sio32DevHeader->chan    /* macro to access a channel's chan   */

                                    /* ******* UART Control Structure ******* */
                                    /* We need one structure for each UART    */
static struct
{
    MBool           exists;         /* Flags presence of UART on serial card  */
    sio32DevChan    *channel;       /* Pointers to each channel data structure*/
} sio32Uart[SIO32_MAX_UARTS];       /* One structure needed for each UART     */

typedef struct                      /* Used to setup baudTable described below*/
{
    Nat16 baudRate;                 /* User specified baud rate               */
    Nat16 baudValue;                /* Value to write to Clock Select Register*/
} baud;

                                    /* baudTable is an array of available baud*/
                                    /* rates and the values to write to the   */
                                    /* Clock Select Register to get that rate */
LOCAL baud baudTable[] =
{
        {   50, B50},    {   75, B75},    {  110, B110},   {  150, B150},
        {  200, B200},   {  300, B300},   {  600, B600},   { 1200, B1200},
        { 1800, B1800},  { 2400, B2400},  { 4800, B4800},  { 9600, B9600},
        {19200, B19200}, {38400, B38400}
};

Nat16  sio32Debug;                  /* sets driver diagnostic output level    */
MLocal Int16 sio32DrvNum;           /* driver index into IO switch table      */

MLocal sio32DevChan* sio32Open(sio32DevChan *sio32DevHdr, Char *remainder,
    Int32 mode);

MLocal Int32 sio32Close(sio32DevChan *sio32DevHdr);
MLocal Int32 sio32Read (sio32DevChan *sio32DevHdr, Char *buffer, Int32 nBytes);
MLocal Int32 sio32Write(sio32DevChan *sio32DevHdr, Char *buffer, Int32 nBytes);
MLocal Int32 sio32Ioctl(sio32DevChan *sio32DevHdr, Int32 ioctlCode, Int32 *arg);
Void sio32Interrupt(Nat32 uart);
MLocal Void sio32ChanInterrupt(Nat32 uart, Nat32 chan);
MLocal Void sio32TxStartUp(sio32DevChan *sio32DevHdr);
MLocal Void sio32DisableRxTx(Nat16 uart, Nat16 chan);
MLocal Void sio32RS485TxCtrl(Nat16 uart, Nat16 chan, Nat16 mode);
MLocal Void sio32SetRxIdleTime(Nat16 uart, Nat16 chan, Nat32 baudRate);
MLocal Void sio32WaitForRxIdle(Nat16 uart, Nat16 chan);

MLocal Int16 getFrameFromRing(sio32DevChan *sio32Hdr, Byte *frame, Int16 len);
MLocal Void  transmitterFSM(Nat32 uart, Nat32 chan);
MLocal Void sio32Error(Int32 errorNum, char *errorMsg);

MLocal Void sio32Flush   (FAST sio32DevChan *sio32DevHeader);
MLocal Void sio32FlushRd (FAST sio32DevChan *sio32DevHeader);
MLocal Void sio32FlushWrt(FAST sio32DevChan *sio32DevHeader);

MLocal Int32 sio32BufRead (FAST sio32DevChan *sio32DevHeader, Char *buffer,
    Int32 maxBytes);

MLocal Int32 sio32BufWrite (FAST sio32DevChan *sio32DevHeader, Char *buffer,
    Int32 nBytes);

/*****************************************************************************
 * sio32UartExists - This routine checks for the presence of an Octal UART
 * on the SIO32 serial board by checking for memory at the UART address. It
 * returns ERROR or OK.
 *****************************************************************************/
    MLocal STATUS
sio32UartExists(uart)
    Nat16   uart;                   /* uart number to check                   */
{
    char    probeVal;               /* location used by vxMemProbe            */

                                    /* Look for memory at UART address        */
    return (vxMemProbe ((char*) SIO32_UART_ADDR(uart), READ, sizeof(probeVal),
                                &probeVal));

} /* sio32UartExists() */

/*****************************************************************************
 * sio32InitUart - All serial channels in the SCC2698 uart are initialized
 * to the following state:
 *
 *      Multi purpose output pin 0 (MPOA & MPOB) are set to RTS Mode
 *      RS485 Transmitter is disabled
 *      Receiver Ready interrupts are enabled
 *
 *****************************************************************************/
    MLocal Void
sio32InitUart(Nat32 uart)
{
    FAST   Nat32 chan;              /* channel loop counter                   */

                                    /* Initialize all channels in a UART      */
    for (chan = 0; chan < SIO32_CHAN_PER_UART; chan += 2)
    {                               /* Set MPOa & MPOb pins to RTSN Mode and  */
                                    /* MPP pins to outputs                    */
        *SIO32_REG(uart, chan, OUT_CTRL_REG) =
            MPOA_RTSN | MPOB_RTSN | MPP_OUTPUTS;

                                    /* Disable RS485 Transmitters             */
        *SIO32_REG(uart,  chan,      COMMAND_REG) = NEGATE_RTSN;
        *SIO32_REG(uart, (chan + 1), COMMAND_REG) = NEGATE_RTSN;

                                    /* Interrupt on Receiver, Transmitter or  */
        *SIO32_REG(uart, chan, INT_MASK_REG) =
            COUNTER_RDY | RxRDYA | RxRDYB | TxRDYA | TxRDYB;
    } /* for */
} /* sio32InitUart() */

/******************************************************************************/
/* Function    : sio32ResetChan                                               */
/* Purpose     : resets a SCC2698 serial channel to the following state:      */
/*               Receiver, Transmitter & Error Status set to power up state   */
/*               Break change interrupt is cleared                            */
/*               Receiver and Transmitter are disabled                        */
/*               RTS Line (RS485 Transmitter Enable) Disabled                 */
/* Inputs      : IP Module Identifier, serial channel number                  */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32ResetChan(Nat32 uart, Nat32 chan)
{
    *sio32Uart[uart].channel[chan].uartCommandReg = RESET_RX;
    *sio32Uart[uart].channel[chan].uartCommandReg = RESET_TX;
    *sio32Uart[uart].channel[chan].uartCommandReg = RESET_ERROR;
    *sio32Uart[uart].channel[chan].uartCommandReg = RESET_BREAK;

    sio32DisableRxTx(uart, chan);   /* Disable receiver and transmitter       */
                                    /* Disable RS485 transmitter              */
    sio32RS485TxCtrl(uart, chan, OFF);
} /* sio32ResetChan() */

/*****************************************************************************
 * sio32EnableRx - Enables a channel's receiver
 *****************************************************************************/
    MLocal Void
sio32EnableRx(Nat16 uart, Nat16 chan)
{
                                    /* Enable UART Receiver                   */
    *sio32Uart[uart].channel[chan].uartCommandReg = NO_COMMAND | RX_ENABLE;
} /* sio32EnableRx() */

/*****************************************************************************
 * sio32EnableTx - Enables a channel's transmitter
 *****************************************************************************/
    MLocal Void
sio32EnableTx(Nat16 uart, Nat16 chan)
{
                                    /* Enable UART Transmitter                */
    *sio32Uart[uart].channel[chan].uartCommandReg = NO_COMMAND | TX_ENABLE;
} /* sio32EnableTx() */

/*****************************************************************************
 * sio32EnableRxTx - Enables a channel's receiver & transmitter
 *****************************************************************************/
    MLocal Void
sio32EnableRxTx(Nat16 uart, Nat16 chan)
{
                                    /* Enable UART Transmitter and Receiver   */
    *sio32Uart[uart].channel[chan].uartCommandReg =
       NO_COMMAND | TX_ENABLE | RX_ENABLE;
} /* sio32EnableRxTx() */

/*****************************************************************************
 * sio32DisableRx - Disables a channel's receiver
 *****************************************************************************/
    MLocal Void
sio32DisableRx(Nat16 uart, Nat16 chan)
{
                                    /* Disable UART Receiver                  */
    *sio32Uart[uart].channel[chan].uartCommandReg = NO_COMMAND | RX_DISABLE;
} /* sio32DisableRx() */

/*****************************************************************************
 * sio32DisableTx - Disables a channel's transmitter
 *****************************************************************************/
    MLocal Void
sio32DisableTx(Nat16 uart, Nat16 chan)
{
                                    /* Disable UART Transmitter               */
    *sio32Uart[uart].channel[chan].uartCommandReg = NO_COMMAND | TX_DISABLE;
} /* sio32DisableTx() */

/*****************************************************************************
 * sio32DisableRxTx - Disables a channel's receiver and transmitter. Returns
 * the present state of the receive and transmitter so we can restore the
 * channel's state if necessary.
 *****************************************************************************/
    MLocal Void
sio32DisableRxTx(Nat16 uart, Nat16 chan)
{
                                    /* Disable UART Transmitter and Receiver  */
    *sio32Uart[uart].channel[chan].uartCommandReg
       = NO_COMMAND | TX_DISABLE | RX_DISABLE;
} /* sio32DisableRxTx() */

/*****************************************************************************
 * sio32RS485TxCtrl - Controls state of RS485 transmitter. This routine will
 * toggle th Transmit Enable line to the RS485 transceiver chip.
 *****************************************************************************/
    MLocal Void
sio32RS485TxCtrl(Nat16 uart, Nat16 chan, Nat16 mode)
{
    if (mode == OFF)
        *sio32Uart[uart].channel[chan].uartCommandReg = NEGATE_RTSN;
                                    /* Disable RS485 Transmitter              */
    else
        *sio32Uart[uart].channel[chan].uartCommandReg = ASSERT_RTSN;
                                    /* Enable RS485 Transmitter               */
} /* sio32RS485TxCtrl() */

/*****************************************************************************
 * sio32PSGoodStatus - reads the state of the PS1_GOOD and PS2_GOOD signals
 * and returns their status. They are connected to the SCC2698 MPI1B and
 * MPI1A pins respectively.
 *****************************************************************************/
    Int32
sio32PSGoodStatus( Void )
{                                   /* Read status of PS_GOOD signals         */
                                    /* and return their state. +ve true logic */
    return(((*SIO32_REG(PS_GOOD_UART, PS_GOOD_CHAN, IN_PORT_REG)) & 0x0c) >> 2);
} /* sio32PSGoodStatus() */

/*****************************************************************************
 * sio32SetLineFormat - sets the number of data bits, stop bits and parity
 * for the specified serial channel. Baud Rate is set by sio32SetBaudRate.
 * The values passed in are manifest constants which are defined in sio32.h
 * sio32DataBits, sio32StopBits and sio32Parity will translate numeric or
 * string values to the manifest constants.
 *****************************************************************************/
    Void
sio32SetLineFormat(uart, chan, dataBits, stopBits, parity)
    Nat16   uart;                   /* Uart Number on Mezzanine card (0-3)    */
    Nat16   chan;                   /* Channel number in UART (0-7)           */
    Nat16   dataBits;               /* Manifest constant selects 5-8 data bits*/
    Nat16   stopBits;               /* Manifest constant selects 1-2 stop bits*/
    Nat16   parity;                 /* Manifest constant select parity        */
{
                                    /* Set pointer to MODE_REG1               */
    *sio32Uart[uart].channel[chan].uartCommandReg = RESET_MR1;

                                    /* Initialize parity and data bit settings*/
    *SIO32_REG(uart, chan, MODE_REG1) = RxRTS_OFF | RxINT_RxREADY
                                        | CHAR_ERR_MODE | parity | dataBits;

                                    /* Initialize stop bit setting            */
    *SIO32_REG(uart, chan, MODE_REG2) = CHAN_NORMAL | TxRTS_ON | TxCTS_OFF
                                        | stopBits;
} /* sio32SetLineFormat() */

/******************************************************************************/
/* Function    : sio32SetBaudRate                                             */
/* Purpose     : Sets the serial line baud rate                               */
/* Inputs      : uart, channel, baud rate manifest contant                    */
/* Outputs     : None                                                         */
/* Note        : Two registers are written by this routine; AUX_CTRL_REG and  */
/*               CLK_SEL_REG. The AUX_CTRL_REG select the baud rate group     */
/*               FOR THE CHANNEL PAIR (0/1, 2/3 etc). Therefore, both         */
/*               channels MUST use baud rates from the same group.            */
/******************************************************************************/
    Void
sio32SetBaudRate(uart, chan, baudRate)
    Nat16   uart;                   /* Uart Number on Mezzanine card (0-3)    */
    Nat16   chan;                   /* Uart channel number                    */
    Nat16   baudRate;               /* Manifest constant specifies Baud Rate  */
{
    *SIO32_REG(uart, chan, CLK_SEL_REG)  = (((baudRate & 0x0F) << 4) |
                                             (baudRate & 0x0F));

                                    /* Select Baud Rate Group and Counter Mode*/
                                    /* clocked by CLK/16 for the Timer/Counter*/
    *SIO32_REG(uart, (chan & 0xfe), AUX_CTRL_REG) =
        ((baudRate & 0x10) << 3) | COUNTER_CLK_D16 ;

} /* sio32SetBaudRate() */

/******************************************************************************/
/* Function    : sio32SetLoopback                                             */
/* Purpose     : enables or disables loopback mode for channel specified      */
/* Inputs      : uart, channel, stop bits, loopback mode                      */
/* Outputs     : None                                                         */
/******************************************************************************/
    Void
sio32SetLoopback(uart, chan, stopBits, loopbackCtrl)
    Nat16   uart;                   /* Uart Number on Mezzanine card (0-3)    */
    Nat16   chan;                   /* Channel number in UART (0-7)           */
    Nat16   stopBits;               /* Manifest constant selects 1-2 stop bits*/
    MBool   loopbackCtrl;           /* Switches Loopback On/Off               */
{
    if (loopbackCtrl == FALSE)      /* Disable Local Loopback mode            */
        *SIO32_REG(uart, chan, MODE_REG2) =   CHAN_NORMAL | TxRTS_ON |
                                                TxCTS_OFF | stopBits;
    else                            /* Enable Local Loopback mode             */
        *SIO32_REG(uart, chan, MODE_REG2) = CHAN_LOOPBACK | TxRTS_ON |
                                                TxCTS_OFF | stopBits;
} /* sio32SetLoopback() */

/******************************************************************************/
/* Function    : sio32BaudRate                                                */
/* Purpose     : converts numeric baud rate value to the manifest constant    */
/* Inputs      : Baud rate in bits/sec                                        */
/* Outputs     : Returns Bxxxx or ERROR                                       */
/******************************************************************************/
    MLocal Int16
sio32BaudRate(Nat16 baudRate)
{
    FAST Nat32   baud;              /* Counter to index the baud rate table   */

    for (baud = 0; baud < NELEMENTS(baudTable); baud++)
    {                               /* search the baud rate table looking for */
                                    /* a match. When one is found, return the */
                                    /* correct value for the SCC2698          */
        if (baudTable[baud].baudRate == baudRate)
                return(baudTable[baud].baudValue);
    } /* for */
    return(ERROR);                  /* Specified baud rate not available      */
} /* sio32BaudRate */

/******************************************************************************/
/* Function    : sio32DataBits                                                */
/* Purpose     : converts numeric data bit value to the manifest constant     */
/* Inputs      : Number of data bits                                          */
/* Outputs     : Returns manifest constant or ERROR                           */
/******************************************************************************/
    MLocal Int32
sio32DataBits(Nat16 dataBits)
{
    switch (dataBits)               /* Look up the manifest constant based    */
    {                               /* on the user specified value            */
        case 5:
            return(CS5);
        case 6:
            return(CS6);
        case 7:
            return(CS7);
        case 8:
            return(CS8);

        default:
            return(ERROR);          /* Requested number of data bits is not   */
    } /* switch */                  /* supported                              */
} /* sio32DataBits */

/******************************************************************************/
/* Function    : sio32StopBit                                                 */
/* Purpose     : converts numeric stop bit value to the manifest constant     */
/* Inputs      : Number of stop bits                                          */
/* Outputs     : Returns manifest constant or ERROR                           */
/******************************************************************************/
    MLocal Int32
sio32StopBits(Nat16 stopBits)
{
    switch (stopBits)
    {
        case 1:
            return(CSTOPB1);
        case 2:
            return(CSTOPB2);

        default:
            return(ERROR);
    } /* switch */
} /* sio32StopBits */

/******************************************************************************/
/* Function    : sio32Parity                                                  */
/* Purpose     : converts parity string, NONE, ODD, EVEN to manifest constant */
/* Inputs      : Number of data bits                                          */
/* Outputs     : Returns manifest constant or ERROR                           */
/******************************************************************************/
    MLocal Int32
sio32Parity(char *parity)
{
    if (strcmp(parity, "NONE"  ) == 0) return(PARNONE);
    if (strcmp(parity, "EVEN"  ) == 0) return(PAREVEN);
    if (strcmp(parity, "ODD"   ) == 0) return(PARODD);

                                    /* Special wake up mode                   */
    if (strcmp(parity, "WAKEUP") == 0) return(PARWAKEUP);
    return(ERROR);                  /* Requested parity is not available      */
} /* sio32Parity */

/******************************************************************************/
/* Function    : sio32WaitForRxIdle                                           */
/* Purpose     : waits for a quiet serial channel receiver                    */
/* Inputs      : uart and channel numbers                                     */
/* Outputs     : None                                                         */
/* Note        : Checks state of the rxIdle flag that is set by the Rx ISR    */
/*               A flag is set (rxIdle) and the routine sleeps for a short    */
/*               while. If a byte is received the interrupt handler will clear*/
/*               the flag. This routine checks the state of the flag when it  */
/*               wakes up. If it is still true then no bytes were received and*/
/*               the line is presumed to be quiet.                            */
/******************************************************************************/
    MLocal Void
sio32WaitForRxIdle(Nat16 uart, Nat16 chan)
{
    Nat32 timerChan;
                                    /* Set channel idle flag. This is cleared */
                                    /* by the interrupt handler when a byte   */
                                    /* is recevied                            */
    timerChan = (chan & 0xfe);

                                       /* get exclusive access to the timer   */
    semTake( sio32Uart[uart].channel[timerChan].timerBusySem, WAIT_FOREVER );

                                    /* Store channel using timer for ISR      */
    sio32Uart[uart].channel[timerChan].timerChan = chan;
        
    do
    {
      *SIO32_REG(uart, timerChan, STOP_TIMER);

      sio32Uart[uart].channel[chan].rxIdle = TRUE;

                                    /* Wait for longer than a character time  */
      *SIO32_REG(uart, timerChan, CT_UPPER_REG) =
        (sio32Uart[uart].channel[chan].rxIdleTime >> 8);

      *SIO32_REG(uart, timerChan, CT_LOWER_REG) =
        (sio32Uart[uart].channel[chan].rxIdleTime & 0xff);

      *SIO32_REG(uart, timerChan, START_TIMER);

      semTake(sio32Uart[uart].channel[chan].timerDoneSem, WAIT_FOREVER); 

    } while (sio32Uart[uart].channel[chan].rxIdle == FALSE);
                                    /* Flag is still true so the line is idle */

    semGive( sio32Uart[uart].channel[timerChan].timerBusySem);

} /* sio32WaitForRxIdle() */

/******************************************************************************/
/* Function    : sio32SetRxIdleTimes                                          */
/* Purpose     : sets receiver idle time based on the specified baud rate     */
/* Inputs      : uart and channel numbers , baud rate                         */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32SetRxIdleTime(Nat16 uart, Nat16 chan, Nat32 baudRate)
{
                                    /* Calculate the time the receiver should */
                                    /* be idle at the specified baud rate     */
    sio32Uart[uart].channel[chan].rxIdleTime =
      (SIO32_CLK_FREQ * 11L * (38400L/baudRate)) / (38400L * 16L);

} /* sio32SetRxIdleTime() */

/*****************************************************************************
 * sio32Error - error print utility. Prints an error message and sets errno.
 *****************************************************************************/
    MLocal Void
sio32Error(Int32 errorNum, char *errorMsg)
{
    if (sio32Debug) printf("%s", errorMsg);
        errnoSet(errorNum);
} /* sio32Error() */

/******************************************************************************/
/* Function    : sio32Drv                                                     */
/* Purpose     : SIO32 Driver Initialization Function. Installs the driver    */
/* Inputs      : Hardware interrupt vector base and interrupt level           */
/* Outputs     : Reurns OK or ERROR and adds driver to OS driver table        */
/* Note        : allocates data structers, establishes the interrupt service  */
/*               routine for the driver and initializes the SIO32 hardware.   */
/******************************************************************************/
    STATUS
sio32Drv(Nat16 sio32IntVecBase, Nat16 sio32IntLevel)
{
    FAST    Nat32   chan;           /* Channel counter                        */
    FAST    Nat32   uart;           /* Uart Counter                           */
    FAST    Nat32   channel;        /* channel index for intConnect           */

    if (sio32DrvNum > 0)            /* check if driver already installed      */
        return (OK);                /* Yes so do nothing                      */

    sio32Debug = SIO32_DIAG_LEVEL;  /* select diagnostics output level        */

                                    /* look for presence of SIO32 hardware    */
    if (sio32BoardProbe(0, sio32Debug) != OK)
        return(ERROR);              /* No card present                        */

    if ((sio32IntVecBase % 16) != 0)/* Vector base must be on a 16 vector     */
    {                               /* Boundary. Maximum vector is 255.       */
                                    /* Set error number and return ERROR      */
        sio32Error(ESIO32_BAD_IVEC, "SIO32 Bad Interrupt Vector Base\n");
        return(ERROR);
    } /* if */

    if (!sio32Debug)                /* Print initialization message           */
    {
        printf("Initializing SIO32 Driver... ");
        printf("\n");
    } /* if */

    for (uart = 0, channel = 0; uart < SIO32_MAX_UARTS; uart++)
    {
        if (sio32UartExists(uart) != OK)        /* See if this UART exists    */
            sio32Uart[uart].exists = FALSE;     /* This UART doesn't exist    */
        else
        {
            sio32InitUart(uart);                /* Initialize UART Hardware   */
            sio32Uart[uart].exists = TRUE;      /* This UART exists           */

                                    /* allocate channel control structure     */
            if ((sio32Uart[uart].channel =
                calloc(SIO32_CHAN_PER_UART, sizeof(sio32DevChan))) == NULL)
            {
                sio32Error(ESIO32_MALLOC, "channel structure malloc failed\n");
                return(ERROR);
            } /* if */

#ifdef DEBUG_MODE
            printf ("UART Base 0x%x\n", SIO32_UART_ADDR(uart));
#endif
                                /* Fill in the channel control structure      */
            for (chan = 0; chan < SIO32_CHAN_PER_UART; chan++)
            {
                                /* Initialize channel control data structers  */
                sio32Uart[uart].channel[chan].uart     = uart;
                sio32Uart[uart].channel[chan].chan     = chan;
                sio32Uart[uart].channel[chan].created  = FALSE;
                sio32Uart[uart].channel[chan].chanOpen = FALSE;
                sio32Uart[uart].channel[chan].protocol = RS232_MODE;
                sio32Uart[uart].channel[chan].rxIdle   = FALSE;
                sio32Uart[uart].channel[chan].loopback = FALSE;

                sio32Uart[uart].channel[chan].frameStartRecvd = FALSE;
                sio32Uart[uart].channel[chan].frameDelimRecvd = FALSE;

                sio32Uart[uart].channel[chan].frameTxState = IDLE;
                sio32Uart[uart].channel[chan].frameTxDLESent = FALSE;

                sio32Uart[uart].channel[chan].frameTxChecksum = 0;

                sio32Uart[uart].channel[chan].wrtStateBusy  = FALSE;
                sio32Uart[uart].channel[chan].discardRxByte = FALSE;

                                /* Create frame received synchronization sem  */
                sio32Uart[uart].channel[chan].frameRecvdSem =
                    semBCreate(SEM_Q_FIFO, SEM_EMPTY);

                                /* Create frame sent synchronization semaphore*/
                sio32Uart[uart].channel[chan].frameSentSem =
                    semBCreate(SEM_Q_FIFO, SEM_EMPTY);

		sio32Uart[uart].channel[chan].timerDoneSem =
		  semBCreate (SEM_Q_FIFO, SEM_EMPTY);

		sio32Uart[uart].channel[chan].timerBusySem =
		  semMCreate (SEM_Q_FIFO | SEM_DELETE_SAFE);

		sio32Uart[uart].channel[chan].timerChan = 0;

                                /* Address of UART Command reg                */
                sio32Uart[uart].channel[chan].uartCommandReg =
                    SIO32_REG(uart, chan, COMMAND_REG);

                                /* Address of RX/TX Data reg for channel      */
                sio32Uart[uart].channel[chan].uartRxTxDataReg =
                    SIO32_REG(uart, chan, RX_HOLD_REG);

                                /* Address of interrupt status reg for channel*/
                sio32Uart[uart].channel[chan].uartIntStatusReg =
                    SIO32_REG(uart, (chan & 0xfe), INT_STAT_REG);

                                /* Initialize channel dependent UART registers*/
                                /* Disable UART's Receiver and Transmitter    */
                                /* Reset Rx, Tx, Break and Error Flags        */
                                /* Disable RS485 transmitter                  */
                sio32ResetChan(uart, chan);

                                /* Set Default Baud Rate to 9600 baud         */
                sio32SetBaudRate(uart, chan, B9600);
                                /* Calculate idle time at 9600 baud           */
                sio32SetRxIdleTime(uart, chan, 9600);

                                /* Set line format: 8 data, No Parity, 1 stop */
                sio32SetLineFormat(uart, chan, CS8, CSTOPB1, PARNONE);

            } /* for */

#ifdef BUILD_MV162_IP
	    sio32InitInterrupts(uart, sio32IntVecBase++, sio32IntLevel);
#endif
                                /* Initialize SIO32 Interrupt Handling        */
        } /* else */
    } /* for */
                                    /* Initialize SIO32 Interrupt Handling    */
#ifdef BUILD_TELEM_SIO32
    sio32InitInterrupts(sio32IntVecBase, sio32IntLevel);
#endif
                                    /* Add the driver to the vxWorks IO System*/
                                    /* sio32DrvNum is global for the driver ID*/
    if ((sio32DrvNum = iosDrvInstall( (FUNCPTR) NULL, (FUNCPTR) NULL,
        (FUNCPTR) sio32Open,  (FUNCPTR) sio32Close, (FUNCPTR) sio32Read,
        (FUNCPTR) sio32Write, (FUNCPTR) sio32Ioctl)) < 0)
        return (ERROR);             /* error number set by iosDrvInstall      */

    if (sio32Debug)
        printf("sio32Drv: Driver Installed. sio32DrvNum = %d\n", sio32DrvNum);
    else
        printf("done.\n");

    return (OK);                    /* driver successfully installed          */
} /* sio32Drv */

/******************************************************************************/
/* Function    : sio32DevCreate                                               */
/* Purpose     : Creates an sio32 device entry for use by open/close etc      */
/* Inputs      : Name, channel, initial line parameters, protocol             */
/* Outputs     : Returns OK or ERROR                                          */
/******************************************************************************/
    STATUS
sio32DevCreate(char *name, Nat32 channel, Nat32 baudRate, Nat32 dataBits,
    Nat32 stopBits, char *parity, sioProto protocol)
{
    FAST    Nat16 uart;             /* UART for this channel                  */
    FAST    Nat16 chan;             /* chan number within the uart            */

    if (sio32DrvNum <= 0)           /* Check for presence of SIO32 card driver*/
    {
        sio32Error(ESIO32_NO_DRIVER, "SIO32 Driver Not Installed\n");
        return(ERROR);
    } /* if */

    if (channel >= PORTS_PER_SIO32) /* check for valid channel number         */
    {
        sio32Error(ESIO32_NO_CHAN, "SIO32 No Channel\n");
        return(ERROR);
    } /* if */
                                    /* Calculate UART and Channel number      */
    uart = channel / SIO32_CHAN_PER_UART;
    chan = channel % SIO32_CHAN_PER_UART;

                                     /* check that the channel's UART exists  */
    if (sio32Uart[uart].exists != TRUE)
    {
        sio32Error(ESIO32_NO_CHAN, "sio32DevCreate: SIO32 No Channel\n");
        return(ERROR);
    } /* if */

                                     /* check that the channel's initialized  */
    if (sio32Uart[uart].channel[chan].chan != chan)
    {
        sio32Error(ESIO32_NO_CHAN, "sio32DevCreate: SIO32 No Channel\n");
        return(ERROR);
    } /* if */

                                    /* range check user specified baud rate   */
                                    /* and convert to SIO32 register format   */
    if ( (sio32Uart[uart].channel[chan].baudRate =
           sio32BaudRate((Nat16) baudRate ) ) == ERROR)
    {
        sio32Error(ESIO32_EBAUD, "sio32DevCreate: Bad baud rate specified\n");
        return(ERROR);
    } /* if */

                                    /* Baud rate changed so re-calculate the  */
                                    /* receiver idle time                     */
    sio32SetRxIdleTime(uart, chan, baudRate);

                                    /* range check user specified data bits   */
                                    /* and convert to SIO32 register format   */
    if ((sio32Uart[uart].channel[chan].dataBits =
        sio32DataBits((Nat16) dataBits)) == ERROR)
    {
        sio32Error(ESIO32_EDBITS, "sio32DevCreate: Bad data bits specified\n");
        return(ERROR);
    } /* if */
                                    /* range check user specified stop bits   */
                                    /* and convert to SIO32 register format   */
    if ((sio32Uart[uart].channel[chan].stopBits =
        sio32StopBits((Nat16) stopBits)) == ERROR)
    {
        sio32Error(ESIO32_ESBITS, "sio32DevCreate: Bad stop bits specified\n");
        return(ERROR);
    } /* if */
                                    /* range check user specified parity      */
                                    /* and convert to sio32 register format   */
    if ((sio32Uart[uart].channel[chan].parity =
        sio32Parity(parity)) == ERROR)
    {
        sio32Error(ESIO32_EPARITY, "sio32DevCreate: Bad parity specified\n");
        return(ERROR);
    } /* if */
                                    /* Save user specified protocol for later */
    sio32Uart[uart].channel[chan].protocol = protocol;

                                    /* if this device already exists then     */
                                    /* don't create it. We use the TAS to     */
                                    /* prevent other tasks from creating this */
                                    /* channel simultaneously and also mark   */
                                    /* the channel as having been created     */
    if (vxTas(&(sio32Uart[uart].channel[chan].created)) == FALSE)
    {
        sio32Error(ESIO32_CEXIST, "sio32DevCreate: Channel already created\n");
        return(OK);
    } /* if */

                                    /* allocate read and write ring buffers */
    if ((sio32Uart[uart].channel[chan].wrtBuf = rngCreate(BUFFER_SIZE)) == NULL)
    {
        sio32Uart[uart].channel[chan].created = FALSE;
        sio32Error(ESIO32_TY_DEV, "sio32DevCreate: Cannot allocate buffers\n");
        return(ERROR);
    } /* if */

    if ((sio32Uart[uart].channel[chan].rdBuf = rngCreate(BUFFER_SIZE)) == NULL)
    {
        rngDelete(sio32Uart[uart].channel[chan].wrtBuf);
        sio32Uart[uart].channel[chan].created = FALSE;
        sio32Error(ESIO32_TY_DEV, "sio32DevCreate: Cannot allocate buffers\n");
        return(ERROR);
    } /* if */

    sio32Uart[uart].channel[chan].mutexSem =
            semMCreate (SEM_Q_FIFO | SEM_DELETE_SAFE);

                                    /* Add the device to the vxWorks IO system*/
    if (iosDevAdd(&(sio32Uart[uart].channel[chan].devHdr), name,
            sio32DrvNum) != OK)
    {
        sio32Uart[uart].channel[chan].created = FALSE;

        if (sio32Debug)
            printf("sio32DevCreate: error adding %s to I/O system.\n", name);
        return(ERROR);
    } /* if */

    if (sio32Debug)
        printf("sio32DevCreate: %s created\n", name);

    return(OK);
} /* sio32DevCreate */

/******************************************************************************/
/* Function    : sio32Open                                                    */
/* Purpose     : opens the specified SIO32 device (called via IO system)      */
/* Inputs      : device specific data structure, path name, mode              */
/* Outputs     : returns a pointer to the device specific data structure      */
/******************************************************************************/
    MLocal sio32DevChan*
sio32Open(sio32DevChan *sio32DevHeader, Char *remainder, Int32 mode)
{
                                    /* use vxTas to implement mutual exclusion*/
                                    /* to prevent simultaneous channel access */
    if (vxTas(&(sio32DevHeader->chanOpen)) == FALSE)
    {
        sio32Error(ESIO32_COPEN, "sio32Open: SIO32 Channel already open\n");
        return((sio32DevChan *) ERROR);
    } /* if */

    sio32Flush(sio32DevHeader);     /* Flush input and output buffers         */

    sio32ResetChan(UART, CHAN);     /* Set up user specified line format      */
                                    /* Disable Receiver and Transmitter       */
                                    /* Reset Rx, Tx, Break and Error Flags    */
                                    /* Disable RS485 Transmitter              */

                                    /* Set baud rate and line format options  */
    sio32SetBaudRate  (UART, CHAN, sio32DevHeader->baudRate);
    sio32SetLineFormat(UART, CHAN, sio32DevHeader->dataBits,
                                   sio32DevHeader->stopBits,
                                   sio32DevHeader->parity);

    sio32EnableRx(UART, CHAN);      /* Enable Receiver, Txmit is disabled     */

    if (sio32DevHeader->protocol != RS232_MODE)
    {                               /* Wait for line to become idle           */
        sio32WaitForRxIdle(UART, CHAN);
                                    /* Flush read output buffer               */
        sio32FlushRd(sio32DevHeader);
    } /* if */

    return(sio32DevHeader);
} /* sio32Open */

/******************************************************************************/
/* Function    : sio32Close                                                   */
/* Purpose     : closes the specified SIO32 device (called via IO system)     */
/* Inputs      : device specific data structure                               */
/* Outputs     : returns OK                                                   */
/******************************************************************************/
    MLocal Int32
sio32Close(sio32DevChan *sio32DevHeader)
{
    sio32DisableRxTx(UART, CHAN);   /* Disable Receiver and Transmitter      */

                                    /* Clear the channel open flag so we      */
                                    /* reopen this channel later              */
    sio32DevHeader->chanOpen = FALSE;

    return (OK);                    /* That's all folks!                      */
} /* sio32Close */

/******************************************************************************/
/* Function    : sio32Read                                                    */
/* Purpose     : reads up to maxBytes from the specified SIO32 device         */
/* Inputs      : device specific data structure, buffer, maxBytes             */
/* Outputs     : returns the actual number of bytes read or ERROR.            */
/******************************************************************************/
    MLocal Int32
sio32Read(sio32DevChan *sio32DevHeader, Char *buffer, Int32 maxBytes)
{
    if (sio32DevHeader->protocol == RS485_MODE)
        return (getFrameFromRing(sio32DevHeader, buffer, maxBytes));
                                    /* Get length of frame from buffer        */
    else
                                    /* Use tyLib read function and return     */
                                    /* actual number of bytes read            */
        return(sio32BufRead (sio32DevHeader, buffer, maxBytes));
} /* sio32Read */

/******************************************************************************/
/* Function    : sio32Write                                                   */
/* Purpose     : writes nBytes to the specified SIO32 device                  */
/* Inputs      : device specific data structure, buffer, nBytes               */
/* Outputs     : returns the actual number of bytes written or ERROR.         */
/* Note        : In RS232 mode the routine returns immediately. In RS485 mode */
/*               the routine blocks until all bytes have been sent so we can  */
/*               report collisions during the transmission                    */
/******************************************************************************/
    MLocal Int32
sio32Write(sio32DevChan *sio32DevHeader, Char *buffer, Int32 nBytes)
{
    Int32   status;                 /* Status from sio32BufWrite call         */

    status = sio32BufWrite(sio32DevHeader, buffer, nBytes);

    if (sio32DevHeader->protocol == RS485_MODE)
                                    /* Wait for frame to be sent              */
        semTake(sio32DevHeader->frameSentSem, WAIT_FOREVER);

    return(status);
} /* sio32Write */

/******************************************************************************/
/* Function    : sio32Ioctl                                                   */
/* Purpose     : provides various device specific functions for sio32 devices */
/* Inputs      : device specific data structure, ioctl code, argument         */
/* Outputs     : returns OR or ERROR, Data may be returned                    */
/******************************************************************************/
    MLocal Int32
sio32Ioctl(sio32DevChan *sio32DevHeader, Int32 ioctlCode, Int32 *arg)
{
    switch (ioctlCode)             /* Decide which request is being made     */
    {
        case FIONREAD:
            *arg = rngNBytes (sio32DevHeader->rdBuf);
            break;

        case FIONWRITE:
            *arg = rngNBytes (sio32DevHeader->wrtBuf);
            break;

        case FIOFLUSH:
            sio32Flush (sio32DevHeader);
            break;

        case FIORFLUSH:
            sio32FlushRd (sio32DevHeader);
            break;

        case FIOWFLUSH:
            sio32FlushWrt (sio32DevHeader);
            break;

        case FIOISATTY:
            return(TRUE);
            break;

        case FIOBAUDRATE:           /* Set a channel's baud rate              */
            sio32DevHeader->baudRate = sio32BaudRate((Nat16) *arg);

                                    /* Set baud rate to user specified value  */
            sio32SetBaudRate  (UART, CHAN, sio32DevHeader->baudRate);
                                    /* Re-calculate receiver idle time        */
            sio32SetRxIdleTime(UART, CHAN, (Nat32) *arg);
            break;

        case FIOLOOPBACK:           /* Toggle Local Loopback mode for testing */
            sio32DevHeader->loopback = ~(sio32DevHeader->loopback);
            sio32SetLoopback(UART, CHAN, sio32DevHeader->stopBits,
                                         sio32DevHeader->loopback);
            break;

        default:
            errnoSet (S_ioLib_UNKNOWN_REQUEST);
            return(ERROR);
            break;

    } /* switch */

    return (OK);
} /* sio32Ioctl */

/******************************************************************************/
/* Function    : sio32TxStartUp                                               */
/* Purpose     : Starts a channels transmiter                                 */
/* Inputs      : device specific data structure                               */
/* Outputs     : None, Transmit interrupt is enabled and data is sent by ISR  */
/******************************************************************************/
    MLocal Void
sio32TxStartUp(sio32DevChan *sio32DevHeader)
{
    FAST Nat32 uart = UART;
    FAST Nat32 chan = CHAN;
                                    /* Everything depends on operating mode   */
    if (sio32DevHeader->protocol == RS232_MODE)
                                            /* ********** RS232 Mode ******** */
        sio32EnableRxTx(uart, chan);        /* Enable the transmitter. This   */
                                            /* causes a TxRDY Interrupt and   */
                                            /* ISR sends the first byte       */
    else
    {                                       /* ********* RS485 Mode ********* */
        sio32EnableRx(uart, chan);          /* Enable the UART receiver       */
        sio32WaitForRxIdle(uart, chan);     /* Wait for Rx to go idle         */
        sio32DisableRx(uart, chan);         /* Disable the UART receiver      */

        sio32RS485TxCtrl(uart, chan, ON);   /* Enable RS485 Transmitter      */

        sio32EnableTx(uart, chan);          /* Enable the transmitter. This   */
                                            /* causes a TxRDY Interrupt and   */
                                            /* ISR sends the first byte       */
    } /* else */
} /* sio32TxStartUp() */

/****************************************************************************/
/* Function    : sio32Interrupt                                             */
/* Purpose     : scc2698 UART Interrupt Handler                             */
/* Inputs      : UART Identifier                                            */
/* Outputs     : NONE                                                       */
/****************************************************************************/
    Void
sio32Interrupt(Nat32 intSource)
{
    FAST Nat32  uart;               /* Uart Number on IP module (0-3) */
    FAST Nat32  chan;               /* Channel number in UART (0-7)         */

#ifdef BUILD_MV162_IP
    uart = intSource;

    for (chan = 0; chan < SIO32_CHAN_PER_UART; chan++)
      sio32ChanInterrupt(uart, chan);
#endif

#ifdef BUILD_TELEM_SIO32
                                    /* calc UART and channel interrupt source*/
    uart = intSource /  SIO32_CHAN_PER_UART;
    chan = intSource & (SIO32_CHAN_PER_UART - 1);

    sio32ChanInterrupt(uart, chan); /* Process interrupts for pair of channels*/
    sio32ChanInterrupt(uart, chan + 1);
#endif
} /* sio32Interrupt() */

/****************************************************************************/
/* Function    : sioChan32Interrupt                                         */
/* Purpose     : IP-Octal Module Interrupt Handler                          */
/* Inputs      : IP Module Identifier                                       */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    MLocal Void
sio32ChanInterrupt(Nat32 uart, Nat32 chan)
{
    FAST sio32DevChan *sio32DevHeader;  /* Channel Control Data Structure     */
    FAST Byte    intStatus;             /* Interrupt status register          */
    FAST Byte    eofChar;               /* End of Frame Character             */
    FAST Int32   temp;                  /* Temporary Register for Ring Put    */
    FAST Byte    rxByte;                /* Character recevied byte            */
    Byte         txByte;                /* Character to send byte             */
    SEM_ID       timerSem;

                                    /* fetch pointer to channel control data  */
    sio32DevHeader = sio32Uart[uart].channel + chan;

    intStatus = (*sio32DevHeader->uartIntStatusReg);
                                    /* Check for byte received interrupt flag */
    if (intStatus & ((chan & 0x01) ? RxRDYB : RxRDYA))
    {                               /* A byte was received so read it         */
                                    /* Read received byte from UART           */
      rxByte = *sio32DevHeader->uartRxTxDataReg;

                                    /* If the channel is open then            */
      if ((sio32DevHeader->chanOpen))
      {                             /* We get a bogus RxRDY after last byte   */
                                    /* is sent. This discards the bogus byte  */
	if (sio32DevHeader->frameTxState != IDLE)
	  transmitterFSM(uart, chan);
	else
	{
#ifdef DEBUG_MODE
	  logMsg("RxRDYA Int\n");
#endif
          if (sio32DevHeader->discardRxByte)
             sio32DevHeader->discardRxByte = FALSE;
          else
          {                         /* Read received byte from UART           */
                                    /* Save the byte in the receive buffer    */
	     RNG_ELEM_PUT (sio32DevHeader->rdBuf, rxByte, temp);

                                    /* Check for end of frame character       */
             eofChar = (sio32DevHeader->protocol == RS485_MODE ? ETX : CR);
             if (rxByte == eofChar)
               semGive(sio32DevHeader->frameRecvdSem);

                                    /* Used by sio32WaitForRxIdle to indicate */
                                    /* activity on this channel's receive line*/
             sio32DevHeader->rxIdle = FALSE;
          } /* else */
        } /* else */
      } /* if */
    } /* if */

                                    /* Counter/Timer Expired interrupt        */
    if (( (chan & 0x01) == 0x0) && (intStatus & COUNTER_RDY))
    {
      *SIO32_REG(uart, chan, STOP_TIMER);
      
      timerSem = (sio32Uart[uart].channel +
		  sio32DevHeader->timerChan)->timerDoneSem;

      if (timerSem != (SEM_ID) NULL)
	semGive(timerSem);
    } /* if */

    if (sio32DevHeader->chanOpen)
    {                               /* Check for Transmit Interrupt Flag      */
      if ( intStatus & ((chan & 0x01) ? TxRDYB : TxRDYA) )
      {
#ifdef DEBUG_MODE
	logMsg("TxRDY Int\n");
#endif  
                              /* If RS485 X.andy protocol selected then       */
        if (sio32DevHeader->protocol == RS485_MODE)
            transmitterFSM(uart, chan);
        else
        {                     /* RS232 or RS485 protocol mode                 */
                              /* Fetch next byte and send it                  */
            if (RNG_ELEM_GET (sio32DevHeader->wrtBuf, &txByte, temp))
                                    /* got a character to be output           */
                *sio32DevHeader->uartRxTxDataReg = txByte;
            else
            {                       /* Last byte has been sent, disable Txmit */
                sio32DevHeader->wrtStateBusy = FALSE;   /* no more chars  */
                sio32DisableTx(uart, chan);

                if (sio32DevHeader->protocol == RS485_NO_PROTO)
                {
                  sio32DevHeader->discardRxByte = TRUE;
                  sio32EnableRx(uart, chan);      /* Enable receiver      */
                } /* if */
            } /* else */
        } /* else */
      } /* if */
    } /* if */
} /* sio32ChanInterrupt() */

/******************************************************************************/
/* Function    : getFrameFromRing                                             */
/* Purpose     : Extract next frame from receiver ring buffer                 */
/* Inputs      : device specific data structure, frame buffer, buffer length  */
/* Outputs     : Returns frame length or ERROR                                */
/******************************************************************************/
    MLocal Int16
getFrameFromRing(sio32DevChan *sio32DevHdr, FAST Byte *frame, FAST Int16 maxLen)
{
    FAST MBool startFound = FALSE;
    FAST int temp;
    FAST Byte *frameStart;
    FAST Int16 count;
    Byte rxByte;

    frameStart = frame;             /* Save frame start pointer for length  */

    FOREVER
    {                               /* Read byte from rx receive ring       */
        while (RNG_ELEM_GET(sio32DevHdr->rdBuf, &rxByte, temp) == 0)
            semTake(sio32DevHdr->frameRecvdSem, WAIT_FOREVER);

        if (rxByte != DLE)          /* Check for DLE in frame               */
        {
            if (startFound)         /* If start of frame detected then      */
                if (count++ < maxLen)
                    *frame++ = rxByte;/* Save the received byte in frame buf  */

        } /* if */

        else
        {                           /* Read next byte from rx receive ring  */
            while (RNG_ELEM_GET(sio32DevHdr->rdBuf, &rxByte, temp) == 0)
                semTake(sio32DevHdr->frameRecvdSem, WAIT_FOREVER);

            if (rxByte == STX)      /* Check for Start of Frame             */
            {
                if (startFound == TRUE)
                {
                    if (sio32Debug) printf("get frame error 1\n");
                    return(ERROR);
                } /* if */

                count = 0;
                startFound = TRUE;  /* Set start found flag                 */
                frameStart = frame; /* Save frame start pointer for length  */
            } /* if */

            else if (rxByte == ETX)
            {                       /* Check for End of Frame               */
                if (startFound == FALSE)
                {
                    if (sio32Debug) printf("get frame error 2\n");
                    return(ERROR);
                }
                                    /* Length of frame                      */
                return (frame - frameStart);
            } /* else */

            else if (rxByte == DLE)
            {
                if (startFound)
                {                   /* Save stuffed DLE pair in buffer      */
                    if (count++ < (maxLen - 1))
            {
                        *frame++ = rxByte;
                        *frame++ = rxByte;
                    } /* if */
                } /* if */
            } /* else */
            else
            {
                if (sio32Debug) printf("get frame error 3\n");
                return(ERROR);
            } /* else */
      } /* else */
    } /* for */
} /* getFrameFromRing() */

/******************************************************************************/
/* Function    : transmitterFSM                                               */
/* Purpose     : Finite State Mackine to handle frame formatting and sending  */
/* Inputs      : uart and channel number                                      */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
transmitterFSM(FAST Nat32 uart, FAST Nat32 chan)
{
    Byte txByte;
    FAST int    temp;           /* Temporary register for RING_GET      */
                                    /* Channel Control Data Structure       */
    FAST sio32DevChan *sio32DevHeader;
                                    /* fetch pointer to channel control data*/
    sio32DevHeader = sio32Uart[uart].channel + chan;

    if (sio32DevHeader->frameTxDLESent)
    {                                   /* Last byte sent was a DLE so send */
                                        /* A additonal stuffed DLE          */
         *sio32DevHeader->uartRxTxDataReg = DLE;
                                        /* Set DLE state flag FALSE         */
        sio32DevHeader->frameTxDLESent = FALSE;
                                        /* Add stuffed DLE to checksum      */
        sio32DevHeader->frameTxChecksum += DLE;
    } /* if */

    else
    {
        switch (++sio32DevHeader->frameTxState)
        {
            case IDLE:
                break;

            case SOF_DLE:
                 *sio32DevHeader->uartRxTxDataReg = DLE;
                break;

            case SOF_STX:
                 *sio32DevHeader->uartRxTxDataReg = STX;
                sio32DevHeader->frameTxChecksum = 0;
                break;

            case DATA:
                if (RNG_ELEM_GET (sio32DevHeader->wrtBuf, &txByte, temp))
                {                           /* got a character to be output */
                     *sio32DevHeader->uartRxTxDataReg = txByte;
                    if (txByte == DLE)
                        sio32DevHeader->frameTxDLESent = TRUE;
                                    /* If byte sent is DLE then stuff it    */
                                    /* Add Tx Byte to checksum              */
                    sio32DevHeader->frameTxChecksum += txByte;
                    sio32DevHeader->frameTxState = SOF_STX;
                } /* if */
                else
                          /* No data to send so proceed to next state*/
                    transmitterFSM(uart, chan);
                break;

            case CKSUM:
                 *sio32DevHeader->uartRxTxDataReg =
                     sio32DevHeader->frameTxChecksum;

                if (sio32DevHeader->frameTxChecksum == DLE)
                    sio32DevHeader->frameTxDLESent = TRUE;
                break;

            case EOF_DLE:           /* End of Frame Sequence DLE ETX        */
                 *sio32DevHeader->uartRxTxDataReg = DLE;
                break;

            case EOF_ETX:           /* End of Frame Sequence DLE ETX        */
                 *sio32DevHeader->uartRxTxDataReg = ETX;

                                    /* UART will negate RTS after last bit  */
                                    /* has been sent. This disables RS485   */
                                    /* transmitter                          */
                sio32DisableTx(uart, chan);         /* Disable Transmitter  */
                sio32EnableRx(uart, chan);          /* Enable receiver      */
                sio32DevHeader->wrtStateBusy = FALSE;

                                     /* Identify channel used to talk to the*/
                                     /* on-board micro & jump to next state.*/
                                     /* This is needed because the hardware */
                                     /* does not connect the Rx & Tx lines  */
                                     /* so we don't get an Rx Intr after the*/
                                     /* last byte had been sent.            */
                if ( (chan == (SIO32_CHAN_PER_UART - 1)) &&
                     (uart == (SIO32_MAX_UARTS - 1)) )
                    sio32DevHeader->frameTxState = IDLE;

                semGive(sio32DevHeader->frameSentSem);
                break;

            case EOF_END:          /* Discards dummy RxRDY interrupt caused */
                sio32DevHeader->frameTxState = IDLE;
                break;             /* by enabling the receiver in EOF_ETX   */

            default:
                sio32DevHeader->frameTxState = IDLE;
                break;

        } /* switch */
    } /* else */
} /* transmitterFSM() */

/******************************************************************************/
/* Function    : sio32FlushRd                                                 */
/* Purpose     : Flushes a channel's read buffer                              */
/* Inputs      : device specific data structure                               */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32FlushRd (FAST sio32DevChan *sio32DevHeader)
{                                       /* get exclusive access to the device */
    semTake (sio32DevHeader->mutexSem, WAIT_FOREVER);
    rngFlush (sio32DevHeader->rdBuf);
    semGive (sio32DevHeader->mutexSem);
} /* sio32FlushRd() */

/******************************************************************************/
/* Function    : sio32FlushWrt                                                */
/* Purpose     : Flushes a channel's write buffer                             */
/* Inputs      : device specific data structure                               */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32FlushWrt (FAST sio32DevChan *sio32DevHeader)
{                                       /* get exclusive access to the device */
    semTake (sio32DevHeader->mutexSem, WAIT_FOREVER);
    rngFlush (sio32DevHeader->wrtBuf);
    semGive (sio32DevHeader->mutexSem);
} /* sio32FlushWrt() */

/******************************************************************************/
/* Function    : sio32Flush                                                   */
/* Purpose     : Flushes a channel's read and write buffers                   */
/* Inputs      : device specific data structure                               */
/* Outputs     : None                                                         */
/******************************************************************************/
    MLocal Void
sio32Flush (FAST sio32DevChan *sio32DevHeader)
{
    sio32FlushRd (sio32DevHeader);
    sio32FlushWrt(sio32DevHeader);
} /* sio32Flush */

/******************************************************************************/
/* Function    : sio32BufWrite                                                */
/* Purpose     : task level write routine for sio32 device                    */
/* Inputs      : device specific data structure, buffer, number of bytes      */
/* Outputs     : number of bytes actually written to device or ERROR          */
/******************************************************************************/
    MLocal Int32
sio32BufWrite (FAST sio32DevChan *sio32DevHeader, Char *buffer, Int32 nBytes)
{
    FAST bytesPut;
    FAST int oldlevel;

    semTake (sio32DevHeader->mutexSem, WAIT_FOREVER);

    bytesPut = rngBufPut (sio32DevHeader->wrtBuf, (char *) buffer, nBytes);

    if (!sio32DevHeader->wrtStateBusy)
    {
        oldlevel = intLock ();              /* LOCK INTERRUPTS */

                       /* check xmitter busy again, now that we're locked out */
        if (!sio32DevHeader->wrtStateBusy)
        {
            sio32DevHeader->wrtStateBusy = TRUE;

            intUnlock (oldlevel);           /* UNLOCK INTERRUPTS */

            sio32TxStartUp(sio32DevHeader);
        } /* if */
        else
            intUnlock (oldlevel);           /* UNLOCK INTERRUPTS */
    } /* if */

    semGive (sio32DevHeader->mutexSem);

    return (bytesPut);
} /* sio32BufWrite() */

/******************************************************************************/
/* Function    : sio32BufRead                                                 */
/* Purpose     : task level read routine for sio32 device                     */
/* Inputs      : device specific data structure, buffer, buffer length        */
/* Outputs     : Returns number of bytes read or ERROR                        */
/******************************************************************************/
    MLocal Int32
sio32BufRead(FAST sio32DevChan *sio32DevHeader, Char *buffer, Int32 maxBytes)
{
    FAST int    nBytes;
    FAST RING_ID ringId;

    FAST int temp;
    Byte rxByte;

    nBytes = 0;
    ringId = sio32DevHeader->rdBuf;

    FOREVER
    {
                                    /* Read byte from rx receive ring       */
        while (RNG_ELEM_GET(ringId, &rxByte, temp) == 0)
            semTake(sio32DevHeader->frameRecvdSem, WAIT_FOREVER);

        buffer[nBytes] = rxByte;
        if ( (nBytes++ > maxBytes) || (rxByte == CR) )
            return (nBytes);
    } /* FOREVER */
} /* sio32BufRead() */
