
/****************************************************************************/
/* Copyright 1991 to 1995, MBARI                                            */
/****************************************************************************/
/* Summary  : IBC Quad Serial Board Functions                               */
/* Filename : quad_ser.c                                                    */
/* Author   : Andrew Pearce                                                 */
/* Project  : New ROV IBC Based Data Concentrator                           */
/* Version  : 1.0                                                           */
/* Created  : 09/17/92                                                      */
/* Modified : 01/31/95                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/ibc/boards/quad_ser/quad_ser.c,v 1.3 1997/05/07 15:34:17 pean Exp $
 * $Log: quad_ser.c,v $
 * Revision 1.4  1998/08/04 11:55:17  audo
 * Added timer counter to fix Double buffer transmitter disable problem.
 *
 * Revision 1.3  1997/05/07 15:34:17  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 18:51:32  pean
 * Initial check in of IBC microcontroller software
 *
 * Revision 1.1  96/02/06  11:51:52  11:51:52  pean (Andrew Pearce)
 * Initial revision
 *
 * Revision 1.3  94/02/04  14:28:07  14:28:07  pean (Andrew Pearce)
 * Fixed problem with RS485 transmitter not disabling after bytes sent
 *
 * Revision 1.2  93/07/02  09:39:43  09:39:43  pean (Andrew Pearce)
 * Minor bug fixes and enhancements for Prototype Vehicle Tests
 *
 * Revision 1.1  93/06/17  15:22:40  15:22:40  pean (Andrew Pearce)
 * Initial revision
 *quad_ser.
*/
/****************************************************************************/

#pragma model(196)
#pragma nosignedchar

#include "C:/C96/include/stddef.h"      /* C Standard Definitions           */
#include "C:/C96/include/string.h"      /* C Standard Definitions           */
#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "microasm.h"                   /* Assembly language functions      */
#include "microdef.h"                   /* Microcontroller definitions      */
#include "microlib.h"                   /* Microcontroller Library decls.   */
#include "ring.h"                       /* Ring buffer support routines     */
#include "malloc.h"                     /* Malloc support routines          */
#include "microCmd.h"                   /* microcontroller commands         */
#include "applay.h"                     /* Application layer funcitions     */

#include "ibc_card.h"                   /* IBC Board Types and Addresses    */
#include "ibc_cmd.h"                    /* Core IBC application definitions */
#include "ibc_asm.h"                    /* IBC Assembly Language Functions  */
#include "pic.h"                        /* Intel 82C59 Interrupt Controller */

#include "quad_serial.h"                /* Quad Serial Board Definitions    */
#include "scc2698.h"                    /* Signetics SCC2698 UART Definition*/


                                        /* Forward Declarations for MLocals */
MLocal serialBoardEntry* serialBoardInit( Byte *boardBaseAddr );
MLocal Void sccInitUart( Byte *boardBaseAddr );
MLocal Void sccResetChannel(Byte *boardBaseAddr, Nat16 chan);

MLocal Void sccRS485TxCtrl(Byte *boardBaseAddr, Nat16 chan, Boolean state);
MLocal Void sccTxStartUp(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccWaitForRxIdle(serialBoardEntry *serialBoard, Nat16 chan);

MLocal Void sccSetBaudRate(Byte *boardBaseAddr, Nat16 chan, Nat16 baudRate);
Void sccSetTermChar(serialBoardEntry *serialBoard, Nat16 chan,
     Nat16 termChar);

MLocal Void sccEnableRx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccEnableTx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccDisableRx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccDisableTx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccEnableRxTx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccDisableRxTx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccSendBreak(serialBoardEntry *serialBoard, Nat16 chan, Nat16 mode);

MLocal Void sccFlushRx(serialBoardEntry *serialBoard, Nat16 chan);
MLocal Void sccFlushRxTx(serialBoardEntry *serialBoard, Nat16 chan);

MLocal Void sccInterrupt(serialBoardEntry *serialBoard, Nat16 chan,
    Reg Byte intStatus, Byte txRdy, Byte rxRdy);

MLocal Int16 sccBufRead(serialBoardEntry *serialBoard, Nat16 chan,
    Byte *buffer, Int16 nBytes);

MLocal Int16 sccBufWrite(serialBoardEntry *serialBoard, Nat16 chan,
    Byte *buffer, Int16 nBytes);

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 */
MLocal const 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}
};

/****************************************************************************/
/* Function    : serialBoardSearch                                          */
/* Purpose     : Scans IBC backplane looking for Quad Serial Boards         */
/* Inputs      : Pointer to IBC Board Entry Table and maximum table size    */
/* Outputs     : Returns number of boards found                             */
/****************************************************************************/
    Int16
serialBoardSearch( IBC_BoardEntry *IBC_Boards[], Int16 maxCards )
{
    Byte  *addr;                        /* Serial Board base address        */
    Int16 board;                        /* Number of Serial Boards found    */
    IBC_BoardEntry *boardEntry;         /* Pointer to Serial Board struct   */

    board = 0;                          /* Set board count to zero          */
    if (maxCards > 0)                   /* Check for space in IBC Card Table*/
    {                                   /* Search for Quad Serial Boards    */
        for (addr =  (Byte *) QUAD_SERIAL_0_ADDR;
             addr <= (Byte *) QUAD_SERIAL_1_ADDR;
             addr+= (QUAD_SERIAL_1_ADDR - QUAD_SERIAL_0_ADDR) )
        {                               /* NULL boardEntry if board missing */
            if ((boardEntry = (IBC_BoardEntry *) serialBoardInit(addr))
                    != NULL)
            {                           /* Add Serial board data struct to
                                        /* IBC Table */
                IBC_Boards[board++] = boardEntry;
                if (board == maxCards)  /* Check if IBC Card Table is full  */
                    return(board);      /* Return No of Serial Boards found */
            } /* if */
        } /* for */
    } /* if */

    return(board);                      /* Return No of Serial Boards found */
} /* serialBoardSearch() */

/****************************************************************************/
/* Function    : serialBoardInit                                            */
/* Purpose     : Quad Serial Board Initialization routine                   */
/* Inputs      : Base address of quad serial board                          */
/* Outputs     : Returns pointer to IBC board data structure                */
/****************************************************************************/
    MLocal serialBoardEntry*
serialBoardInit( Byte *boardBaseAddr )
{
    serialBoardEntry *serialBoard;      /* Pointer to board entry           */
    Reg sccUartChan  *uart;             /* Pointer to UART Data Structure   */
    Int16 chan;                         /* Serial Channel index             */

                                        /* Check if board exists by reading */
                                        /* UART 0 Mode reg 1                */
                                        /* Returns OK if board asserts xack */
    if ( ibcBusReadByte( UART_REG(boardBaseAddr, 0, MODE_REG1) ) == ERROR )
        return(( serialBoardEntry *) NULL);
                                        /* Board doesn't exist return NULL  */

    chan = sizeof(serialBoardEntry);
                                        /* Allocate space for board entry   */
    serialBoard = (serialBoardEntry *) malloc( sizeof (serialBoardEntry) );
    if (serialBoard == (serialBoardEntry *) NULL )
            return( (serialBoardEntry *) NULL);
                                        /* Insufficient memory return ERROR */

                                        /* Fill out board entry structure   */
    serialBoard->IBC_Board.baseAddress = boardBaseAddr;
    serialBoard->IBC_Board.boardType   = QUAD_SERIAL;
    serialBoard->IBC_Board.intrLevel   = IBC_INT_LVL_OFF;

                                         /* Clear Power Supply Alarms       */
                                         /* psuFault line True when no fault*/
                                         /* psuOk line False when no fault  */
    serialBoard->alarms.psuFault  = (unsigned) TRUE;
    serialBoard->alarms.psuGndFlt = (unsigned) FALSE;

    serialBoard->vspTimer = read_sysclock();

    sccInitUart( boardBaseAddr );        /* Initialize 2698 UART Hardware  */

    for (chan = 0; chan < SCC_CHAN_PER_UART; chan++)
    {
        uart = &serialBoard->uart[chan];

        uart->flags.errorStatus   = (unsigned) 0;
        uart->flags.chanOpen      = (unsigned) FALSE;
        uart->flags.handshake     = (unsigned) HANDSHAKE_OFF;
        uart->flags.protocol      = (unsigned) RS485_MODE;
        uart->flags.txBusy        = (unsigned) FALSE;
        uart->flags.rxIdle        = (unsigned) FALSE;
        uart->flags.vspEnabled    = (unsigned) FALSE;
        uart->flags.rxDataSRQPend = (unsigned) FALSE;
        uart->flags.txingBreak    = (unsigned) FALSE;
        uart->rxTerm = LINE_FEED;   /* Default termination character    */
        uart->termRecvd = 0;

        ring_init(&uart->readBuf,  uart->readBuffer,  QUAD_SER_BUF_SIZE);
        ring_init(&uart->writeBuf, uart->writeBuffer, QUAD_SER_BUF_SIZE);

                                /* Initialize channel dependent UART regs   */
                                /* Disable UART's Receiver and Transmitter  */
                                /* Reset Rx, Tx, Break and Error Flags      */
                                /* Disable RS485 transmitter                */
         sccResetChannel(boardBaseAddr, chan);

                                /* Set Default Baud Rate to 9600 baud       */
                                /* Set line format 8 data/1 stop            */
                                /* 2 = NO PARITY, Handshake OFF, RS485 mode */
         sccSetLineFormat(serialBoard, chan, 9600, 8, 1, 2, HANDSHAKE_OFF,
             RS485_MODE);

       } /* for */
                                          /* Switch On Serial Board LED     */
    ibcBusWriteByte( UART_REG(boardBaseAddr, 0, LED_TOGGLE_ADDR), 0 );

    return (serialBoard);                 /* Return pointer to serial struct*/
} /* serialBoardInit() */

/****************************************************************************/
/* Function    : serialBoardInterruptOn                                     */
/* Purpose     : Generate Interrupt from Quad Serial Board                  */
/* Inputs      : Pointer to Serial Board data structure                     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardInterruptOn( serialBoardEntry *serialBoard )
{
    sccEnableTx( serialBoard, 0 );      /* Enable Chan 0 Tx causes interrupt*/
} /* serialBoardInterruptOn() */

/****************************************************************************/
/* Function    : serialBoardInterruptOff                                    */
/* Purpose     : Clear Interrupt from Quad Serial Board                     */
/* Inputs      : Pointer to Serial Board data structure                     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardInterruptOff( serialBoardEntry *serialBoard )
{
    sccDisableTx( serialBoard, 0 );    /* Disable Chan 0 Tx clears interrupt*/
    ibcBusReadByte(                    /* Clear Interrupt                   */
         UART_REG(serialBoard->IBC_Board.baseAddress, 0, INT_STAT_REG) );
} /* serialBoardInterruptOff() */

/****************************************************************************/
/* Function    : serialBoardOpenChan                                        */
/* Purpose     : Open Quad Serial Board Channel                             */
/* Inputs      : Pointer to Serial Board data structure, channel number     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Int16
serialBoardOpenChan(serialBoardEntry *serialBoard, Nat16 chan)
{
    if ( chan > SCC_CHAN_PER_UART )
       return(ERROR);

                                   /* Set up user specified line format    */
    sccResetChannel(serialBoard->IBC_Board.baseAddress, chan);
                                   /* Reset Rx, Tx, Break and Error Flags   */
                                   /* Disable RS485 Transmitter             */

    sccFlushRxTx(serialBoard, chan); /* Flush input and output buffers      */

                                 /* Set up user specified line format       */
                                 /* Set Default Baud Rate to 9600 baud      */
                                 /* Set line format 8 data/1 stop           */
                                 /* 2 = NO PARITY, Handshake OFF, RS232 mode*/
    sccSetLineFormat(serialBoard, chan, 9600, 8, 1, 2, HANDSHAKE_OFF,
        RS485_MODE);

                                    /* Mark channel as open                 */
    serialBoard->uart[chan].flags.chanOpen  = (unsigned) TRUE;
    serialBoard->uart[chan].flags.txBusy    = (unsigned) FALSE;

    sccEnableRx(serialBoard, chan); /* Enable Recvr, Txmit is left disabled*/

    return (OK);
} /* serialBoardOpenChan() */

/****************************************************************************/
/* Function    : serialBoardCloseChan                                       */
/* Purpose     : Close Quad Serial Board Channel                            */
/* Inputs      : Pointer to Serial Board data structure, channel number     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardCloseChan(serialBoardEntry *serialBoard, Nat16 chan)
{
                                    /* Negate RTS to stop Transmit device   */
                                    /* from sneding any more data           */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress,
                chan, COMMAND_REG), NEGATE_RTSN);

                                    /* Mark channel as closed               */
    serialBoard->uart[chan].flags.chanOpen = (unsigned) FALSE;

                                    /* Disable receiver and transmitter     */
    sccDisableRxTx(serialBoard, chan);
} /* serialBoardClose */

/****************************************************************************/
/* Function    : serialBoardWrite                                           */
/* Purpose     : Writes nBytes from buffer to the specified serial channel  */
/* Inputs      : Serial Board structure, channel number, buffer, No bytes   */
/* Outputs     : Returns the actual number of bytes written or ERROR.       */
/****************************************************************************/
    Int16
serialBoardWrite(serialBoardEntry *serialBoard, Nat16 chan,
    Byte *buffer, Int16 nBytes)
{
    if (serialBoard->uart[chan].flags.chanOpen == (unsigned) FALSE)
        return (ERROR);

    return (sccBufWrite(serialBoard, chan, buffer, nBytes) );
} /* serialBoardWrite */

/****************************************************************************/
/* Function    : serialBoardRead                                            */
/* Purpose     : Put up to nBytes from specified serial channel into buffer */
/* Inputs      : Serial Board structure, channel number, buffer, No bytes   */
/* Outputs     : Returns the actual number of bytes read or ERROR.          */
/****************************************************************************/
    Int16
serialBoardRead(serialBoardEntry *serialBoard, Nat16 chan,
    Byte *buffer, Int16 nBytes)
{
    if (serialBoard->uart[chan].flags.chanOpen == (unsigned) FALSE)
        return (ERROR);

    return(sccBufRead (serialBoard, chan, buffer, nBytes) );
} /* serialBoardRead */

/****************************************************************************/
/* Function    : serialBoardRxFlush                                         */
/* Purpose     : Flush bytes from serial channel input buffer               */
/* Inputs      : Serial Board structure, channel number                     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardRxFlush(serialBoardEntry *serialBoard, Nat16 chan)
{
    sccFlushRx(serialBoard, chan);
} /* serialBoardRxFlush() */

/****************************************************************************/
/* Function    : sccBufWrite                                                */
/* Purpose     : Writes nBytes into ring buffer for specified serial channel*/
/* Inputs      : Serial Board structure, channel number, buffer, No bytes   */
/* Outputs     : Returns the actual number of bytes written                 */
/****************************************************************************/
    MLocal Int16
sccBufWrite(serialBoardEntry *serialBoard, Nat16 chan,
    Byte *buffer, Int16 nBytes)
{
    Int16 bytes = 0;
                                    /* Copy data from user to ring buffer   */
    while ( ( bytes++ < nBytes) &&
            (ring_put(&serialBoard->uart[chan].writeBuf, *buffer++)) );

    PIC_disable;                    /* LOCK INTERRUPTS                      */
                                    /* If transmitter is idle, start it up  */
    if (serialBoard->uart[chan].flags.txBusy)
        PIC_enable;                 /* UNLOCK INTERRUPTS                    */
    else
    {                               /* Set transmitter state busy           */
            serialBoard->uart[chan].flags.txBusy = (unsigned) TRUE;

            PIC_enable;             /* UNLOCK INTERRUPTS                    */
                                    /* Enable transmitter and send 1st byte */
            sccTxStartUp(serialBoard, chan);
    } /* else */

    return (bytes);                 /* Return number of bytes written       */
} /* sccBufWrite() */

/****************************************************************************/
/* Function    : sccBufRead                                                 */
/* Purpose     : Read nBytes from ring buffer for specified serial channel  */
/* Inputs      : Serial Board structure, channel number, buffer, No bytes   */
/* Outputs     : Returns the actual number of bytes read                    */
/****************************************************************************/
    MLocal Int16
sccBufRead(serialBoardEntry *serialBoard, Nat16 chan,
        Byte *buffer, Int16 nBytes)
{
     Int16 bytes = 0;
     Int16 rxByte;

     if (nBytes == 0) return (0);

     do                            /* Copy data from ring to user buffer   */
     {
         if ( (rxByte = ring_get(&serialBoard->uart[chan].readBuf)) == ERROR)
             return (bytes);

         *buffer++ = (Byte) rxByte;
         bytes++;

         if (rxByte == serialBoard->uart[chan].rxTerm)
         {
             PIC_disable;           /* LOCK INTERRUPTS                      */
             serialBoard->uart[chan].termRecvd--;
             PIC_enable;            /* Unlock Interrupts                    */

             return (bytes);
         } /* if */
     } while (bytes < nBytes);

     return (bytes);                /* Return number of bytes read          */
} /* sccBufRead() */

/****************************************************************************/
/* Function    : sccFlushRx                                                 */
/* Purpose     : Flush Serial Board Receive buffer                          */
/* Inputs      : Pointer to Serial Board data structure, channel number     */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccFlushRx(serialBoardEntry *serialBoard, Nat16 chan)
{
    PIC_disable;                    /* LOCK INTERRUPTS                      */

    serialBoard->uart[chan].termRecvd = 0;

                                    /* Re-initializing ring buffer to flush */
    ring_init(&serialBoard->uart[chan].readBuf,
            serialBoard->uart[chan].readBuffer, QUAD_SER_BUF_SIZE);
    PIC_enable;                     /* Unlock Interrupts                    */
} /* sccFlushRx */

/****************************************************************************/
/* Function    : sccFlushRxTx                                               */
/* Purpose     : Flush Serial Board Receive and Transmit buffers            */
/* Inputs      : Pointer to Serial Board data structure, channel number     */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccFlushRxTx(serialBoardEntry *serialBoard, Nat16 chan)
{
    PIC_disable;                    /* LOCK INTERRUPTS                      */

    serialBoard->uart[chan].termRecvd = 0;

                                    /* Re-initializing ring buffer to flush */
    ring_init(&serialBoard->uart[chan].readBuf,
            serialBoard->uart[chan].readBuffer, QUAD_SER_BUF_SIZE);

                                    /* Re-initializing ring buffer to flush */
    ring_init(&serialBoard->uart[chan].writeBuf,
            serialBoard->uart[chan].writeBuffer, QUAD_SER_BUF_SIZE);

    PIC_enable;                     /* Unlock Interrupts                    */
} /* sccFlushRxTx */

/****************************************************************************/
/* Function    : sccTxStartUp                                               */
/* Purpose     : Start transmitter for the specified serial channel         */
/* Inputs      : Serial Board structure, channel number                     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
sccTxStartUp(serialBoardEntry *serialBoard, Nat16 chan)
{
                                  /* Everything depends on operating mode   */
    if (serialBoard->uart[chan].flags.protocol == (unsigned) RS232_MODE)
                                          /* ********** RS232 Mode ******** */
        sccEnableRxTx(serialBoard, chan); /* Enable the transmitter. This   */
                                          /* causes a TxRDY Interrupt and   */
                                          /* ISR sends the first byte       */
    else
    {                                     /* ********* RS485 Mode ********* */
        sccEnableRx(serialBoard, chan);   /* Enable the UART Rx, disable Tx */
        sccWaitForRxIdle(serialBoard, chan);  /* Wait for Rx to go idle     */
        sccDisableRx(serialBoard, chan);  /* Disable the UART receiver      */

                                          /* Enable RS485 Transmitter       */
        sccRS485TxCtrl(serialBoard->IBC_Board.baseAddress, chan, ON);

        sccEnableTx(serialBoard, chan);   /* Enable the transmitter. This   */
                                          /* causes a TxRDY Interrupt and   */
                                          /* ISR sends the first byte       */
    } /* else */
} /* sccTxStartUp() */

/****************************************************************************/
/* Function    : sccInitUart                                                */
/* Purpose     : All serial channels of the SCC2698 uart are initialized:   */
/*               MPO is set to RTS Mode, RS485 Transmitter is disabled &    */
/*               Receiver Ready interrupts are enabled                      */
/* Inputs      : Base address of quad serial board                          */
/* Outputs     : Returns pointer to IBC board data structure                */
/****************************************************************************/
    MLocal Void
sccInitUart( Byte *boardBaseAddr )
{
    Reg Nat16 chan;                 /* channel loop counter                 */

                                    /* Initialize all channels in a UART    */
    for (chan = 0; chan < SCC_CHAN_PER_UART; chan += 2)
    {
                                    /* Set MPOa & MPOb pins to RTSN Mode and*/
                                    /* MPP1A/MPP1B, MPP2A/MPP2B pins to out */

        ibcBusWriteByte( UART_REG(boardBaseAddr, chan, OUT_CONFIG_REG),
                MPOA_RTSN | MPOB_RTSN | MPP_OUTPUTS );

                                    /* Disable RS485 Transmitters           */
        sccRS485TxCtrl(boardBaseAddr, chan,     OFF);
        sccRS485TxCtrl(boardBaseAddr, chan + 1, OFF);

                                    /* Interrupt enabled for Receive Byte,  */
                                    /* or Transmit Byte                     */
        ibcBusWriteByte( UART_REG(boardBaseAddr, chan, INT_MASK_REG),
                RxRDYA | RxRDYB | TxRDYA | TxRDYB | COUNTER_RDY);

    } /* for */
} /* sccInitUart() */

/****************************************************************************/
/* Function    : sccResetChannel                                            */
/* Purpose     : resets a SCC2698 serial channel to the following state:    */
/*               Receiver, Transmitter & Error Status set to power up state */
/*               Break change interrupt is cleared, UART Rx and Tx disabled */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccResetChannel(Byte *boardBaseAddr, Nat16 chan)
{
                                    /* Put Rx and Rx in reset state        */
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG), RESET_RX);
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG), RESET_TX);
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG), RESET_ERROR);
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG), RESET_BREAK);

                                    /* Disable recevier and transmitter     */
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG),
                    NO_COMMAND | RX_DISABLE | TX_DISABLE);

                                   /* Disable RS485 transmitter            */
    sccRS485TxCtrl(boardBaseAddr, chan, OFF);
} /* sccResetChannel() */

/****************************************************************************/
/* Function    : sccEnableRx                                                */
/* Purpose     : Enable a channel's receiver                                */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccEnableRx(serialBoardEntry *serialBoard, Nat16 chan)
{
                                  /* Enable UART Receiver                   */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), RX_ENABLE);
} /* sccEnableRx() */

/****************************************************************************/
/* Function    : sccEnableTx                                                */
/* Purpose     : Enable a channel's transmitter                             */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccEnableTx(serialBoardEntry *serialBoard, Nat16 chan)
{
                                  /* Enable UART Transmitter                */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), TX_ENABLE);
} /* sccEnableTx() */

/****************************************************************************/
/* Function    : sccEnableRxTx                                              */
/* Purpose     : Enable a channel's receiver & transmitter                  */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccEnableRxTx(serialBoardEntry *serialBoard, Nat16 chan)
{
                                  /* Enable UART Transmitter                */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), RX_ENABLE | TX_ENABLE);
} /* sccEnableRxTx() */

/****************************************************************************/
/* Function    : sccDisableRx                                               */
/* Purpose     : Disable a channel's receiver                               */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccDisableRx(serialBoardEntry *serialBoard, Nat16 chan)
{
                                  /* Disable UART Receiver                  */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), RX_DISABLE);
} /* sccDisableRx() */

/****************************************************************************/
/* Function    : sccDisableTx                                               */
/* Purpose     : Disable a channel's transmitter                            */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccDisableTx(serialBoardEntry *serialBoard, Nat16 chan)
{
    serialBoard->uart[chan].flags.txBusy = (unsigned) FALSE;

                                  /* Disable UART Transmitter               */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), TX_DISABLE);
} /* sccDisableTx() */

/****************************************************************************/
/* Function    : sccDisableRxTx                                             */
/* Purpose     : Disable a channel's receiver and transmitter               */
/* Inputs      : Base address of quad serial board, channel number          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccDisableRxTx(serialBoardEntry *serialBoard, Nat16 chan)
{
    serialBoard->uart[chan].flags.txBusy = (unsigned) FALSE;

                                  /* Disable UART Receiver                  */
    ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
            COMMAND_REG), NO_COMMAND | RX_DISABLE | TX_DISABLE);
} /* sccDisableRxTx() */

/****************************************************************************/
/* Function    : sccRS485TxCtrl                                             */
/* Purpose     : Enable/Disable of RS485 transmitter.                       */
/* Inputs      : Base address of quad serial board, channel number, state   */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccRS485TxCtrl(Byte *boardBaseAddr, Nat16 chan, Boolean state)
{
                                   /* Enable or disable RS485 Transmitter   */
   ibcBusWriteByte( UART_REG(boardBaseAddr, chan, COMMAND_REG),
                    (state == OFF ? NEGATE_RTSN : ASSERT_RTSN) );
} /* sccRS485TxCtrl() */

/****************************************************************************/
/* Function    : sccBaudRate                                                */
/* Purpose     : converts numeric baud rate value to the manifest constant  */
/*               required for the scc.                                      */
/* Inputs      : Serial channel, baud rate manifest constant                */
/* Outputs     : Returns Bxxxx or ERROR                                     */
/****************************************************************************/
    Int16
sccBaudRate(baudRate)
    Nat16   baudRate;             /* user specified baud rate               */
{
    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      */
} /* sccBaudRate */

/****************************************************************************/
/* Function    : sccSetLineFormat                                           */
/* Purpose     : Set baud rate, data bits, stop bits & parity for channel   */
/* Inputs      : Serial board address, channel, line format parameters      */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
sccSetLineFormat(serialBoardEntry *serialBoard, Nat16 chan, Nat16 baudRate,
    Nat16 dataBits, Nat16 stopBits, Nat16 parity, Boolean handshake,
    protocol proto)
{
    Reg Byte *boardAddr;            /* Address of serial board              */

                                    /* Fetch serial board base address      */
    boardAddr = serialBoard->IBC_Board.baseAddress;

                                   /* Set BaudRate                          */
    sccSetBaudRate(boardAddr, chan, sccBaudRate(baudRate));

                                    /* Set pointer to MODE_REG1             */
    ibcBusWriteByte( UART_REG(boardAddr, chan, COMMAND_REG), RESET_MR1 );

                                    /* Initalize parity & data bit settings*/
    ibcBusWriteByte( UART_REG(boardAddr, chan, MODE_REG1),
            (handshake ? RxRTS_ON : RxRTS_OFF) |
            RxINT_RxREADY | CHAR_ERR_MODE | PARITY(parity) |
            DATA_BITS(dataBits) );

    if (chan >= 2)                  /* Channels 2 & 3 do not support CTS    */
        handshake = HANDSHAKE_OFF;
                                    /* Initialize stop bit setting          */
    ibcBusWriteByte( UART_REG(boardAddr, chan, MODE_REG2),
       (handshake ? TxCTS_ON : TxCTS_OFF) | CHAN_NORMAL |
       (proto == RS485_MODE  ? TxRTS_ON : TxRTS_OFF) | STOP_BIT(stopBits) );

    serialBoard->uart[chan].baudRate = baudRate;
    serialBoard->uart[chan].dataBits = dataBits;
    serialBoard->uart[chan].stopBits = stopBits;
    serialBoard->uart[chan].parity   = parity;
    serialBoard->uart[chan].flags.handshake = handshake;
    serialBoard->uart[chan].flags.protocol = (unsigned) proto;

    if (proto == RS485_MODE)
    {                               /* Wait for line to become idle         */
        sccWaitForRxIdle(serialBoard, chan);
        sccFlushRx(serialBoard, chan);  /* Flush read output buffer         */
    } /* if */

    else                            /* RTS must be asserted to enable       */
                                    /* automatic RTS generation by receiver */
        ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress,
            chan, COMMAND_REG), ASSERT_RTSN);

} /* sccSetLineFormat() */

/****************************************************************************/
/* Function    : sccSetBaudRate                                             */
/* Purpose     : Sets baud rate for serial channel                          */
/* Inputs      : Serial board address, channel, baud rate manifest constant */
/* Note        : The AUX_CTRL_REG select the baud rate group FOR ADJACENT   */
/*               CHANNELS so both channels MUST use rates from same group   */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccSetBaudRate(Byte *boardBaseAddr, Nat16 chan, Nat16 baudRate)
{
                                  /* Select Baud Rate Group and Counter Mode*/
				  /* clocked by CLK/16 for the Timer/Counter*/
    ibcBusWriteByte( UART_REG(boardBaseAddr, (chan & 0xfe), AUX_CTRL_REG),
        ((baudRate & 0x10) << 3) | COUNTER_TxCLK);

    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, CLK_SEL_REG),
        ((baudRate & 0x0F) << 4) | (baudRate & 0x0F));
} /* sccSetBaudRate() */


/****************************************************************************/
/* Function    : sccSetTermChar                                             */
/* Purpose     : Sets the Termination Character for serial channel          */
/* Inputs      : Serial board address, channel, baud rate manifest constant */
/* Note        : Sets rxTerm variable in uart record                        */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
sccSetTermChar(serialBoardEntry *serialBoard, Nat16 chan, Nat16 termChar)
{
    sccUartChan    *uart;           /* Pointer to UART Data Structure       */

    uart = &serialBoard->uart[chan];
    uart->rxTerm = termChar;        /* Assign term char to data structure   */
    uart->termRecvd = 0;            /* Clear term char received flag        */
} /* sccSetTermChar() */

/****************************************************************************/
/* Function    : sccSetloopback                                             */
/* Purpose     : Sets loopback mode for specified serial channel            */
/* Inputs      : Serial board address, channel, loopback enable/disable     */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccSetLoopback(Byte *boardBaseAddr, Nat16 chan,
               Nat16 stopbits, Boolean loopbackCtrl)
{
    ibcBusWriteByte( UART_REG(boardBaseAddr, chan, MODE_REG2),
        (loopbackCtrl == TRUE ? CHAN_LOOPBACK : CHAN_NORMAL) |
         TxRTS_OFF | TxCTS_OFF | stopbits );
} /* sccSetLoopback() */

/****************************************************************************/
/* Function    : sccWaitForRxIdle                                           */
/* Purpose     : waits for a quiet serial channel receiver.                 */
/* Inputs      : Serial board address, channel                              */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccWaitForRxIdle(serialBoardEntry *serialBoard, Nat16 chan)
{
    do
    {                             /* Set channel idle flag. This is cleared */
                                  /* by the interrupt handler when a byte   */
                                  /* is recevied                            */
        serialBoard->uart[chan].flags.rxIdle = (unsigned) TRUE;
                                  /* Wait for longer than a character time  */
        taskDelay(QUAD_SER_RX_IDLE_TIME);

    } while (serialBoard->uart[chan].flags.rxIdle == (unsigned) FALSE);
                                  /* Flag is still true so the line is idle */
} /* sccWaitForRxIdle() */

/****************************************************************************/
/* Function    : sccSendBreak                                               */
/* Purpose     : Send a break Command to Uart                               */
/* Inputs      : Base address of quad serial board, channel number, Break   */
/*               Mode (1 = on or 0 = off)                                   */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccSendBreak(serialBoardEntry *serialBoard, Nat16 chan, Word mode)
{
    if (mode == 0)
    {                        /* 0 = Break Off                              */
                             /* Disable UART Transmitter                   */
          ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
             COMMAND_REG), STOP_BREAK | TX_DISABLE |
             ((serialBoard->uart[chan].flags.protocol == (unsigned) RS485_MODE)
             ? RX_ENABLE : 0) );

          serialBoard->uart[chan].flags.txingBreak = (unsigned) FALSE;

                                    /* Re-enable Tx interrupt after break   */
          ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress,
            (chan & 0xfe), INT_MASK_REG), RxRDYA | RxRDYB |
            (serialBoard->uart[chan & 0xfe].flags.txingBreak    ? 0 : TxRDYA) |
            (serialBoard->uart[(chan & 0xfe) +1].flags.txingBreak ? 0 : TxRDYB));

                                    /* Check for RS485 mode & disable Tx    */
          if (serialBoard->uart[chan].flags.protocol == (unsigned) RS485_MODE)
              sccRS485TxCtrl(serialBoard->IBC_Board.baseAddress, chan, OFF);

       } /* if */
       else
       {                            /* Non-zero = Break On                  */

                                    /* Set Transmitting Break Flag          */
          serialBoard->uart[chan].flags.txingBreak = (unsigned) TRUE;

                                    /* disable tx interrupt for any channel */
                                    /* that is transmitting a break         */
          ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress,
            (chan & 0xfe), INT_MASK_REG), RxRDYA | RxRDYB |
            (serialBoard->uart[chan & 0xfe].flags.txingBreak      ? 0 : TxRDYA) |
            (serialBoard->uart[(chan & 0xfe) +1].flags.txingBreak ? 0 : TxRDYB));

                                    /* Check for RS485 mode & enable Txmiter*/
          if (serialBoard->uart[chan].flags.protocol == (unsigned) RS485_MODE)
              sccRS485TxCtrl(serialBoard->IBC_Board.baseAddress, chan, ON);

          ibcBusWriteByte( UART_REG(serialBoard->IBC_Board.baseAddress, chan,
                           COMMAND_REG), START_BREAK | TX_ENABLE |
             ((serialBoard->uart[chan].flags.protocol == (unsigned) RS485_MODE)
             ? RX_DISABLE : 0) );

    } /* else */
} /* sccSendBreak() */

/****************************************************************************/
/* Function    : serialBoardIsr                                             */
/* Purpose     : Interrupt Handler for Quad Serial Board UART Interrupts    */
/* Inputs      : Serial board address                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardIsr(Reg serialBoardEntry *serialBoard)
{
    Reg Nat16 chan;                 /* Channel number in UART (0-7)         */
    Reg Byte  intStatus;            /* Interrupt Status Register            */

    intStatus = ibcBusReadByte(     /* Interrupt Status for channels 0 & 1  */
         UART_REG(serialBoard->IBC_Board.baseAddress, 0, INT_STAT_REG) );

    if (intStatus)
    {                                /* Process interrupts for channel 0    */
        sccInterrupt(serialBoard, 0, intStatus, TxRDYA, RxRDYA);
                                     /* Process interrupts for channel 1    */
        sccInterrupt(serialBoard, 1, intStatus, TxRDYB, RxRDYB);
    } /* if */

    intStatus = ibcBusReadByte(      /* Interrupt Status for channels 2 & 3 */
         UART_REG(serialBoard->IBC_Board.baseAddress, 2, INT_STAT_REG) );

    if (intStatus)
    {                                /* Process interrupts for channel 2    */
        sccInterrupt(serialBoard, 2, intStatus, TxRDYA, RxRDYA);
                                     /* Process interrupts for channel 3    */
        sccInterrupt(serialBoard, 3, intStatus, TxRDYB, RxRDYB);
    } /* if */

} /* serialBoardIsr() */

/****************************************************************************/
/* Function    : sccInterrupt                                               */
/* Purpose     : Processes Interrupt for a single SCC2698 UART Serial Chan  */
/* Inputs      : Serial board address, bit masks for Tx & Rx Interrupts     */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
sccInterrupt(serialBoardEntry *serialBoard, Nat16 chan,
    Reg Byte intStatus, Byte txRdy, Byte rxRdy)
{
    Reg sccUartChan  *uart;             /* Pointer to UART Data Structure   */
    Reg Byte *boardAddr;                /* ddress of serial board          */
    Reg Byte rxByte;                    /* Character recevied byte          */
    Reg Int16 txByte;                   /* Character to send byte           */
    Word status;                        /* Uart error Status                */

                                        /* Fetch serial board base address  */
    boardAddr = serialBoard->IBC_Board.baseAddress;
    uart = &serialBoard->uart[chan];    /* fetch pointer to channel struct  */

    if (intStatus & rxRdy)              /* Check for byte received interrupt*/
    {                                   /* Read received byte from UART     */
        while((status = ibcBusReadByte(UART_REG(boardAddr, chan, STATUS_REG)))
                 & RxRDY)
        {
            if ( (status &= (RxOVERRUN_ERR | RxPARITY_ERR | RxFRAMING_ERR)) )
            {
                uart->flags.errorStatus |= status;
                ibcBusWriteByte( UART_REG(boardAddr, chan, COMMAND_REG),
                        RESET_ERROR );
            } /* if */

            rxByte = ibcBusReadByte(UART_REG(boardAddr, chan, RX_HOLD_REG) );

            if (uart->flags.chanOpen)   /* If the channel is open then      */
            {                           /* Save the byte in receive buffer  */
                if (ring_put(&uart->readBuf, rxByte) > 0)

                                        /* Check for termination character  */
                    if (rxByte == uart->rxTerm)
                        uart->termRecvd++;

                uart->flags.rxIdle = FALSE;
                                        /* Used by sccWaitForRxIdle to flag */
                                        /* activity on this channel Rx line */
            } /* if */
       } /* while */
    } /* if */

    if (uart->flags.chanOpen)
    {                                   /* Check for Transmit Interrupt Flag*/
        if ((intStatus & txRdy) && !(uart->flags.txingBreak))

        {                               /* Fetch next byte and send it      */
            if ( (txByte = ring_get(&uart->writeBuf)) != ERROR)
            {                           /* got a character to be output     */
                ibcBusWriteByte(UART_REG(boardAddr, chan, TX_HOLD_REG),
                        (Byte) txByte);

                if (ring_entries(&uart->writeBuf) == 0)
                {                       /* Last byte has been sent          */
                                        /* So disable Transmitter           */
                    sccDisableTx(serialBoard, chan);
                                    /* Initialize CT to count for ~2 Char   */
                                    /* Times or 24 bit times 0x18           */
                    ibcBusWriteByte( UART_REG(boardAddr, chan, CT_UPPER_REG),
			             0x00);
                    ibcBusWriteByte( UART_REG(boardAddr, chan, CT_LOWER_REG), 
			             0x18);

                                    /* Setup CT to run in TxCLK mode        */
                    ibcBusWriteByte( UART_REG(boardAddr, chan, AUX_CTRL_REG),
                                     COUNTER_TxCLK);

                                    /* Start Counter Timer              */
                    ibcBusReadByte(UART_REG(boardAddr, chan, START_TIMER));

                } /* if */
            } /* if */
            else
            {
                sccDisableTx(serialBoard, chan);
                sccRS485TxCtrl(boardAddr, chan, OFF);

                                        /* Re-enable recevier if RS485 Mode */
                if (uart->flags.protocol == (unsigned) RS485_MODE)
                    sccEnableRx(serialBoard, chan);

            } /* if */
        } /* if */
    } /* if */

    if (intStatus & COUNTER_RDY)
    {
        /* Two Character times have passed since Tx Disable */
        /* Stop and reset Counter Timer */
        ibcBusReadByte(UART_REG(boardAddr, chan, STOP_TIMER));

        /* Re-enable recevier if RS485 Mode */
        if (uart->flags.protocol == (unsigned) RS485_MODE)
             sccEnableRx(serialBoard, chan);
    }/* if */
} /* sccInterrupt() */

/****************************************************************************/
/* Function    : serialBoardPSUStatus                                       */
/* Purpose     : read state of PS1_FLT & PS1_OK Power Supply Status Signals */
/* Inputs      : Serial board address                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
serialBoardCheckPSUStatus(serialBoardEntry *serialBoard)
{
    Int16 psu;
                                    /* Read status of PS1_FLT & FGND1 lines  */
                                    /* and return their state. -ve true logic*/
    psu = ~(ibcBusReadByte(UART_REG(serialBoard->IBC_Board.baseAddress,
          QUAD_SER_PSU_CHAN, IN_PORT_REG)) )  & 0x05;

    if( ( (psu & PS1_FLT_BIT)   != (Boolean) serialBoard->alarms.psuFault) ||
        ( (psu & FGND1_FLT_BIT)    != (Boolean) serialBoard->alarms.psuGndFlt))
    {
        writeServiceRequest(3, QUAD_SER_PSU_FAULT_SRQ,
                            serialBoard->IBC_Board.baseAddress, psu);

        serialBoard->alarms.psuFault  = (unsigned) (psu & PS1_FLT_BIT);
        serialBoard->alarms.psuGndFlt = (unsigned) (psu & FGND1_FLT_BIT);
    } /* if */
} /* serialBoardPSUStatus() */

/****************************************************************************/
/* Function    : serialBoardFunctions                                       */
/* Purpose     : Quad Serial board real-time loop functions                 */
/* Inputs      : serial Board data structure                                */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
serialBoardFunctions( serialBoardEntry *serialBoard )
{
    Nat16 chan;
    Int16 nBytes;
    Byte  sendBuffer[MAX_PACKET_LEN];

    if ((read_sysclock() - serialBoard->vspTimer) > VSP_TIME_PERIOD)
    {
        for (chan = 0; chan < SCC_CHAN_PER_UART; chan++)
        {
                               /* NOTE : If a Vme reset occurs while a vsp is */
                               /* enabled, and data is streaming into the quad*/
                               /* serial board, there is nothing implemented  */
                               /* to disable the VSP.  There is a potential   */
                               /* for Memory overrun problems. 1/31/95 audo   */

             if ( (serialBoard->uart[chan].flags.vspEnabled) &&
                 (ring_entries(&serialBoard->uart[chan].readBuf)) )
             {
                               /* Limit nBytes to allow for status in buffer*/
                 nBytes = min(ring_entries(&serialBoard->uart[chan].readBuf),
                          MAX_PACKET_LEN - (sizeof(Word) * 4));

                 nBytes = serialBoardRead(serialBoard, chan,
                     sendBuffer + (sizeof(Word) * 4), nBytes);

                 wordsToBuf( &sendBuffer, 4, QUAD_SER_BYTES_IN_BUF_SRQ,
                     serialBoard->IBC_Board.baseAddress, chan, nBytes);

                 writeSRQPacket( sendBuffer, nBytes + (sizeof(Word) * 4));
            } /* if */
        } /* for */

        serialBoard->vspTimer = read_sysclock();
    } /* if */


    for (chan = 0; chan < SCC_CHAN_PER_UART; chan++)
    {
        if (serialBoard->uart[chan].flags.errorStatus)
        {
            serialBoard->uart[chan].flags.errorStatus = (unsigned) 0;
            writeServiceRequest(3, QUAD_SER_RECV_ERROR_SRQ,
                    serialBoard->IBC_Board.baseAddress, chan);
        } /* if */
    } /* for */

    serialBoardCheckPSUStatus(serialBoard);
} /* serialBoardFunctions() */

/****************************************************************************/
/* Function    : serialBoardTermRecvd                                       */
/* Purpose     : Return number of termination characters received           */
/* Inputs      : serial Board data structure, serial channel                */
/* Outputs     : Returns number of terminators received                     */
/****************************************************************************/
    Int16
serialBoardTermRecvd(serialBoardEntry *serialBoard, Nat16 chan)
{
    return (serialBoard->uart[chan].termRecvd);
} /* serialBoardTermRecvd() */

/****************************************************************************/
/* Function    : serialBoardRxBytes                                         */
/* Purpose     : Return number of bytes received                            */
/* Inputs      : serial Board data structure, serial channel                */
/* Outputs     : Returns number of bytes in receive buffer                  */
/****************************************************************************/
    Int16
serialBoardRxBytes(serialBoardEntry *serialBoard, Nat16 chan)
{
    return (ring_entries(&serialBoard->uart[chan].readBuf));
} /* serialBoardRxBytes() */

/****************************************************************************/
/* Function    : quadSerGetLineParmCmd                                      */
/* Purpose     : Process command to Get Line Parmeters                      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : Quad Serial Board line Parameters                          */
/****************************************************************************/
    Void
quadSerGetLineParmCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */


    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    replyToCommand(7, CMD_OK,
                   (Word) serialBoard->uart[chan].baudRate,
                   (Word) serialBoard->uart[chan].dataBits,
                   (Word) serialBoard->uart[chan].stopBits,
                   (Word) serialBoard->uart[chan].parity,
                   (Word) serialBoard->uart[chan].flags.handshake,
                   (Word) serialBoard->uart[chan].flags.protocol);
} /* quadSerGetLineParmCmd */

/****************************************************************************/
/* Function    : quadSerSetLineParmCmd                                      */
/* Purpose     : Process command to Set Line Parmeters                      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None, but Quad Serial Board line Parameters are modified   */
/****************************************************************************/
    Void
quadSerSetLineParmCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Nat16   baudRate;           /* Serial Channel Baud Rate                 */
    Nat16   dataBits;           /* Serial Channel number of data bits       */
    Nat16   stopBits;           /* Serial Channel number of stop bits       */
    Nat16   parity;             /* Serial Channel parity bit                */
    Boolean handshake;          /* Serail Channel handshake on or off       */
    protocol proto;             /* SerialChannel protocol RS232 or RS485    */


    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 9, &command, &boardAddr, &chan, &baudRate,
                 &dataBits, &stopBits, &parity, &handshake, &proto);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) ||
         (sccBaudRate(baudRate) == ERROR) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

                                    /* Disable Receiver and Transmitter      */
    sccResetChannel(serialBoard->IBC_Board.baseAddress, chan);
    sccFlushRxTx(serialBoard, chan);    /* Flush read output buffer         */

                                    /* Set line format                       */
    sccSetLineFormat(serialBoard, chan, baudRate, dataBits,
            stopBits, parity, handshake, proto);

    sccEnableRx(serialBoard, chan); /* Enable Receiver, Transmiter disabled */

    replyToCommand(1, CMD_OK);      /* Report status to serial port         */
} /* quadSerSetLineParmCmd() */

/****************************************************************************/
/* Function    : quadSerSetTermCharCmd                                      */
/* Purpose     : Process command to Set Quad Serial Board Termination Char  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None, but Quad Serial Board Term Char Parameter is modified*/
/****************************************************************************/
    Void
quadSerSetTermCharCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Nat16   termChar;           /* Serial Board desired termination char    */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr, &chan, &termChar);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    sccSetTermChar(serialBoard, chan, termChar);

    replyToCommand(1, CMD_OK);      /* Report status to serial port     */
} /* quadSerSetTermCharCmd() */

/****************************************************************************/
/* Function    : quadSerOpenCmd                                             */
/* Purpose     : Process command to Open Quad Serial Board Serial port      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerOpenCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
                Byte *commandBuf)
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    if (serialBoardOpenChan(serialBoard, chan) == OK )
         replyToCommand(1, CMD_OK);      /* Report status to serial port     */
    else replyToCommand(1, CMD_ERROR);

} /* quadSerOpenCmd() */

/****************************************************************************/
/* Function    : quadSerCloseCmd                                            */
/* Purpose     : Process command to Open Quad Serial Board Serial port      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerCloseCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
                 Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    serialBoardCloseChan(serialBoard, chan);

    replyToCommand(1, CMD_OK);      /* Report status to serial port     */
} /* quadSerCloseCmd() */

/****************************************************************************/
/* Function    : quadSerReadCmd                                             */
/* Purpose     : Process command to Read from Quad Serial Board Serial Port */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerReadCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    Int16   nBytes;             /* Number of bytes in buffer                */
    sendStruct    sendBuffer;

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr, &chan, &nBytes);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        serialBoard->uart[chan].flags.rxDataSRQPend = FALSE;
        return;
    } /* if */
                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

                                /* Limit nBytes to allow for status in buffer*/
    nBytes = min(nBytes, QUAD_SER_BUF_SIZE);

    serialBoard->uart[chan].flags.rxDataSRQPend = FALSE;

    wordToBuf( (Byte *) &sendBuffer.status, CMD_OK );
    nBytes = serialBoardRead(serialBoard, chan, sendBuffer.buffer, nBytes);
    writeDataPacket( (Byte *) &sendBuffer, nBytes + sizeof(sendBuffer.status) );

} /* quadSerReadCmd() */

/****************************************************************************/
/* Function    : quadSerWriteCmd                                            */
/* Purpose     : Process command to Write to Quad Serial Board Serial Port  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerWriteCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   nBytes;             /* Number of bytes in buffer                */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr, &chan, &nBytes);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];
                                /* Write out the Quad_ser Uart */
    serialBoardWrite( serialBoard, chan, commandBuf + sizeof(Word) * 4, nBytes);

    replyToCommand(1, CMD_OK);      /* Report status to serial port     */
} /* quadSerWriteCmd() */

/****************************************************************************/
/* Function    : quadSerFlushCmd                                            */
/* Purpose     : Process command to Flush Quad Serial Board Serial Port     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerFlushCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan );

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    sccFlushRxTx( serialBoard, chan );

    replyToCommand(1, CMD_OK);      /* Report status to serial port     */
} /* quadSerFlushCmd() */

/****************************************************************************/
/* Function    : quadSerRxBytesCmd                                          */
/* Purpose     : Process command to return Number of Bytes in Rx Buffer     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : Returns Int16 number of bytes in buffer                    */
/****************************************************************************/
    Void
quadSerRxBytesCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   bytes;              /* Number of bytes in buffer                */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3 &command, &boardAddr, &chan );

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    bytes = ring_entries(&serialBoard->uart[chan].readBuf);

    replyToCommand(2, CMD_OK, bytes);
} /* quadSerRxBytesCmd() */

/****************************************************************************/
/* Function    : quadSerTxBytesCmd                                          */
/* Purpose     : Process command to return Number of Bytes in Tx buffer     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : Returns Int16 number of bytes in buffer                    */
/****************************************************************************/
    Void
quadSerTxBytesCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   bytes;             /* Number of bytes in buffer                */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);


                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    bytes = ring_entries(&serialBoard->uart[chan].writeBuf);

    replyToCommand(2, CMD_OK, bytes);      /* Report status to serial port   */
} /* quadSerTxBytesCmd() */

/****************************************************************************/
/* Function    : quadSerTermCharsCmd                                        */
/* Purpose     : Process command to return Number of Term Chars in buffer   */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : Returns Int16 number of bytes in buffer                    */
/****************************************************************************/
    Void
quadSerTermCharsCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   termChars;          /* Number of Term Chars                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);


                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    termChars = serialBoardTermRecvd(serialBoard, chan);

    replyToCommand(2, CMD_OK, termChars); /* Report status to serial port   */
} /* quadSerTermCharsCmd() */

/****************************************************************************/
/* Function    : quadSerSendBreakCmd                                        */
/* Purpose     : Process command to Send Break On or Break Off Command      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : Returns Nothing                                            */
/****************************************************************************/
    Void
quadSerSendBreakCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   bytes;             /* Number of bytes in buffer                */
    Nat16   mode;              /* Break Mode On or Off                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr, &chan, &mode);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    sccSendBreak( serialBoard, chan, mode);

    replyToCommand(1, CMD_OK);      /* Report status to serial port     */
} /* quadSerSendBreakCmd() */


/****************************************************************************/
/* Function    : vspOpenCmd                                                 */
/* Purpose     : Process command to Open Quad Serial Board Serial port      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
vspOpenCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
                Byte *commandBuf)
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    if (serialBoardOpenChan(serialBoard, chan) == OK )
    {
        serialBoard->uart[chan].flags.vspEnabled = (unsigned) TRUE;
        replyToCommand(1, CMD_OK);      /* Report status to serial port     */
    } /* if */

    else
        replyToCommand(1, CMD_ERROR);
} /* vspOpenCmd() */

/****************************************************************************/
/* Function    : quadSerGetPsuStatusCmd                                     */
/* Purpose     : Process command to read Quad Serial Board Psu Status       */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerGetPsuStatusCmd(IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
         Byte *commandBuf )
{
    Int16   psu;
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Board base address from command buf      */
    serialBoardEntry *serialBoard;/* Board Entry data structure        */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, QUAD_SERIAL) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Extract pointer to quadser data structure */
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    psu = ~(ibcBusReadByte(UART_REG(serialBoard->IBC_Board.baseAddress,
          QUAD_SER_PSU_CHAN, IN_PORT_REG)) )  & 0x05;

                           /* Send Alarm Status to serial port         */
    replyToCommand(2, CMD_OK, psu);

 } /* quadSerGetPsuStatusCmd() */

/****************************************************************************/
/* Function    : qsProtoOpenCmd                                             */
/* Purpose     : Process command to Open Quad Serial Board Serial port      */
/*               for use with MBARI 485 protocol                            */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
qsProtoOpenCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
                Byte *commandBuf)
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];

    if (serialBoardOpenChan(serialBoard, chan) == OK )
    {                           /* Set Line Format for Protocol 485          */
        sccSetLineFormat(serialBoard, chan, 38400, 8, 1, 2, 0, RS485_MODE);
                                /* Set protocol Enabled flag in Proto Struct */
        /*serialBoard->uart[chan].flags.protoEnabled = (unsigned) TRUE;*/
                                /* Send reset SerialLink Command            */
#if 0
        resetQsSerialLink();
#endif
        replyToCommand(1, CMD_OK);      /* Report status to serial port     */
    } /* if */

    else
        replyToCommand(1, CMD_ERROR);
} /* qsProtoOpenCmd() */

/****************************************************************************/
/* Function    : quadSerFwdCmd                                              */
/* Purpose     : Process command to Forward to Quad Serial Board Serial Port*/
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
quadSerFwdCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Serial Board base address from command buf*/
    Nat16   chan;               /* Serial Channel index                     */
    Int16   nBytes;             /* Number of bytes in buffer                */

    serialBoardEntry *serialBoard;      /* Pointer to board entry           */

                                /* Extract command and arguments from buffer*/
                                /* Skip first two words in header           */

    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &chan);

                                /* Find card table index for this address   */
    if ( ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR) || (chan >= SCC_CHAN_PER_UART) )
    {                           /* Card or chan not present so return error */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */


                                /* Extract pointer to Quad Serial data struct*/
    serialBoard = (serialBoardEntry *) IBC_CardTable[card];
                                /* Write out the Quad_ser Uart               */


    wordsFromBuf(commandBuf + sizeof(Word) * 3, 2, command, &nBytes);

    serialBoardWrite(serialBoard, chan, commandBuf + sizeof(Word) * 3, nBytes);

    replyToCommand(1, CMD_OK);  /* Report status to serial port              */

#if 0
    if ( (nBytes = serialBoardRead(serialBoard, buffer, MAX_PACKET_LEN))
        != ERROR)
                                /* Send response from attached micro to VME  */
        return (writeDataPacket(buffer, nBytes));
    else
        replyToCommand(1, CMD_ERR);  /* Report status to serial port         */

#endif
    replyToCommand(3, CMD_OK, command, nBytes);  /* DELETE - JUST FOR TESTING*/

} /* quadSerFwdCmd() */













