/*
 *            Copyright (c) 1998-2000 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 227                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 *
 *     Module Name: netos_serl.c
 *         Version: 1.00
 *   Original Date: 10/06/99
 *          Author: Shing Wu
 *        Language: Ansi C
 * Compile Options:
 * Compile defines:
 *       Libraries:
 *    Link Options:
 *
 *    Entry Points:
 *
 * Description.
 * =======================================================================
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 * 01/13/99  JW    Added support for modem control signls (DCD and DTR)
 *                 in onccIoctl() and added new definitions in h file
 *
 * 10/06/99  CHG   Changed declaration of get_baud_div() to eliminate
 *                 warning message from compiler.
 *                 Removed definition of NET_OS since it is set by build
 *                 file.
 *
 * 10/12/99  SW    1. Merge ioctl for sending extra cr
 *                 2. Fix problem with O_RDONLY being defined as 0
 *                 3. Fix use of semaphore in close
 *                 4. Open should create semaphore for the requested chan
 * 12/13/99  JW    1. Increased read/write buffers from 1K to 2K
 *                 2. Added code to set RECV_CLOSED_IRQ bit in control
 *                    register A in onccIoctl()
 *                 3. Fixed a typecast bug inside of onccIoctl()
 *                 4. Added code to check and handle RECV_CLOSED bit in
 *                    status register in onccIntRd()
 *                 5. Added code to read in multiple characters in FIFO
 *                    in onccIntRd (Shing's fix)
 *
 * 03/14/00  JFU 1. added * to arg on one of the statement in onccIoctl()
 *
 * 03/16/00  PMJ   Corrected netos_serlclose to flush and discard (cb_reset)
 *                 recieve data before completing close.
 *
 * 05/03/00 JFU     1. Added 115200 support
 *                  2. Corrected Odd parity setting
 *                  3. Improved hardware handshake problem in the onccIntRd 
 *                     and netos_serlread functions.
 *
 * 08/25/00 JFU     added DMA hardware flow control feature.
 *
 * 10/10/00 JFU     updated non parity setting and some time delay
 *
 * 10/25/00 JFU     updated some dma related bugs
 *
 * 11/20/00 JFU     updated baud rate calculation based on new frequency 
 *                  functions
 *
 * 11/21/00 PMJ     Removed references to XTAL_FREQ -- replaced with calls 
 *                  to NAgetXtalFreq()
 *
 * 11/28/00 PMJ     Clarified the processing in get_baud_div -- better 
 *                  reflection with the HW Ref manual.
 *
 * 11/29/00 PMJ     Changed the error comparison in get_baud_div to parts per Baudrate instead of
 *                  percent -- eliminating division (thus round off error) and improving accuracy.
 *
 * 11/29/00 JFU     added code to handle empty DMA buffer interrupt
 *
 * 12/08/00 JFU     added code for send > 32768 bytes in DMA mode
 *
 * 12/21/00 JFU     added major changes to the driver due to two reasons
 *                  (1) 50-0 chip serial problem.  - this chip still does not support hardware handshake.
 *                  (2) modify driver to handle changes in the serial port behavior due to changes with char gap timer.
 *
 * 2/5/01 JFU       Added support for 1036800 BAUD in DMA mode with hardware handshake.
 *
 * 04/18/01 NGUR    Fix problem with the default baud rate greater then 57K
 *
 * 05/04/01 PMJ     Adde fixes per Softworks regarding PPP failures.
 * 5/17/01 ESUN     Modified OnccIntRd(), netos_serlwrite(), cb_add(), cb_remove(),
 *                  and cb_reset() to solve XON/XOFF problem.
 *
 * 6/25/01 JFU      Clear up the file otherwise it is same as 1.3.1.0 or netos 3.00A
 *
 * 09/28/01 JW      minor fix in onccIoctl() for the SIO_SET_STATUS_DTR request
 *
 * 11/16/01 JFU     modify the driver to handle nonblocking dma problem.
 * 03/04/02  SW     Remove the use of bit field                  
 * 04/11/02  PMJ    Added support to sense the DSR state.  See SIO_GET_STATUS_DSR.
 * 04/16/02  DXL    Cleaned up variable declared but never reference; warnings generated by new compiler
 * 06/12/02  JFU    Removed a bug related to software handshaking    
 * 08/02/02  PMJ    New and improved serial API with select functionality and better
 *                  modem signals control.  Introducing the RTS Transmission encapsulation
 *                  which can be used for RS-485 half-duplex style communucation.
 * 08/07/02  PMJ    Included POSIX functions ttyname() and isatty().  Included some error 
 *                  handling around SIO_TERM_SEND_CR and SIO_TERM_ECHO ioctls.
 * 09/24/02  LZ     Added 20UM support.
 *                                              1. if the gap timer count is too big, set to the MAX the 10 bits can hold
 *                                              2. Using F_crystal to calculate the gap timer count
 *                                              3. For higher baud rate, TMODE = 1 and TDCR/RDCR = 00/00 to lower the divisor error
 * 11/10/02  JW     Modified get_char_gap_count() to compute the gap count for baud rates
 *                  up to 115200.  Previous code computed gap count only for baud rates lower
 *                  than 57600.
 * 03/28/03  JZW    Cleaning up some magic numbers in functions ser_dma_write and
 *                  get_baud_div
 *
 */
 
/**
 * There are two APIs for configuring and controlling the serial
 * ports.  The recommended API is based on the standard termios API
 * used in Linux systems.  These functions are described in the Serial
 * section of the online help.  In addition, the driver also supports
 * a set of ioctls.  The ioctls are deprecated and support for them
 * will be discontinued in a future release.  All new software should
 * use the termios API.
 *
 * The constant BSP_SERIAL_PORT_API in bsp.h must be set to
 * BSP_SERIAL_API_TERMIOS to use the standard API. The constant
 * BSP_SERIAL_PORT_API must be set to BSP_SERIAL_API_LEGACY to use the
 * deprecated API.
 *
 * @external
 * @category Serial:deprecatedSerial
 * @since 1.0
 * @overview Deprecated Serial API
 */

#include <string.h>
#include <errno.h>
#include <netosIo.h>
#include <reg_def.h>
#include <bsp.h>

#ifndef OLD_SERIAL_API
#define OLD_SERIAL_API /* Define to get defs for internals */
#include <serParam.h>
#undef OLD_SERIAL_API
#else
#include <serParam.h>
#endif

#include "netos_serl.h"
#include <NA_ISR.h>
#include <sysClock.h>
#include <ddi.h>
#include <Dma_def.h>

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
#define NARM_T2CR ((unsigned long volatile *)0xffb00018) /* Timer 2 Control */
#define NARM_T2SR ((unsigned long volatile *)0xffb0001c) /* Timer 2 Status  */
#endif

#include "assert.h"

#include "termios.h"
#include "termios_int.h"
#include "event_handler.h"

/* Used to weed out all the old code */
#define REALLY_OLD_DMA                  0
#define NEW_DMA				1
#define NEED_ECHO                       1

/* Function to notify event handling routines of event occurance */
extern void serl_cb_event(int, enum events);
extern void *fmemcpy( void *, const void *, int );

/*
 * Serial channel resource lock (used by serial driver & HDLC driver
 */
extern int serl_resource_lock[2];

/*
 * The following routones provide simple resource lock mechanism
 */
extern int GetResource(int *resource, void *owner);
extern int ReleaseResource(int *resource, void *owner);
static int onccTxStartup (SIO_CHAN * pSioChan);
static void onccIntRd (ONCC_CHAN * pChan);

/*#define DEBUG_SERIAL           this can be defined to activate debugging */
#define EXIT_SUCCESS        0
#define OK                  EXIT_SUCCESS
#define ERROR               (-1)
#define SIZE_OF_BUF         4096        /* increase from 1K to 2K ... J.W. */

#if REALLY_OLD_DMA
#define SIZE_OF_DMA_BUF     300         /* size of the DMA buffer for each buffer descriptor */
#endif

#define SW_BUF_THRESH_HOLD  400         /* arbitary number for sw handshake */

#if REALLY_OLD_DMA
#define NUM_DMA_RX_BUF      8           /* set the dma rx buffer number to 2  - arbitary */
#define M_QHEAD_SIZE        8           /* size of the dma queue */
#endif

struct ourSemaphore
{
    int state;              /* semaphore state  */
    int count;              /* semaphore count  */
};

/* semaphore for reading */
static struct ourSemaphore      r_sem[NUM_NCC_SER_CHAN];

/* semaphore for writing */
static struct ourSemaphore      w_sem[NUM_NCC_SER_CHAN];

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
#define  _DISAB_INTS(a, b)      a = NASetIRQ (IRQ_DISABLED) ; \
                                b = NASetFIQ(FIQ_DISABLED)
#define  _RESTOR_INT(a, b)      NASetIRQ (a) ; \
                                NASetFIQ(b)

#else
#define  _DISAB_INTS(a, b)      a = NASetIRQ (IRQ_DISABLED) ;
#define  _RESTOR_INT(a, b)      NASetIRQ (a) ;
#endif

#define _OPEN_SEMA(x)           ((struct ourSemaphore *)x)->state = 1; \
                                ((struct ourSemaphore *)x)->count = 1

#define _CLOSE_SEMA(x)          ((struct ourSemaphore *)x)->state = 0; \
                                ((struct ourSemaphore *)x)->count = 0

#if (BSP_SERIAL_FAST_INTERRUPT == FALSE)
#define _GET_SEMA(x)            {int priorInterruptState;                     \
                                 _DISAB_INTS(priorInterruptState, ignored);   \
                                 if (((struct ourSemaphore *)x)->count > 0) { \
                                   ((struct ourSemaphore *)x)->count = 0;     \
                                   _RESTOR_INT (priorInterruptState, ignored);\
                                 }                                        \
                                 else { _RESTOR_INT (priorInterruptState, \
                                          ignored);                       \
                                    return EBUSY;  }                      \
                                 }

#define PUT_SEMA(x)              {int priorInterruptState;                    \
                                  _DISAB_INTS(priorInterruptState, ignored);  \
                                  ((struct ourSemaphore *)x)->count++;        \
                                  _RESTOR_INT (priorInterruptState, ignored);}

#else
#define _GET_SEMA(x)             {int priorInterruptState, priorFIQState;     \
                                  _DISAB_INTS(priorInterruptState,            \
                                              priorFIQState);                 \
                                  if (((struct ourSemaphore *)x)->count > 0) {\
                                      ((struct ourSemaphore *)x)->count = 0;  \
                                      _RESTOR_INT (priorInterruptState,       \
                                                   priorFIQState);            \
                                  }                                           \
                                  else { _RESTOR_INT (priorInterruptState,    \
                                                      priorFIQState);         \
                                      return EBUSY;  }                        \
                                  }

#define PUT_SEMA(x)               {int priorInterruptState, priorFIQState;    \
                                   _DISAB_INTS(priorInterruptState,           \
                                               priorFIQState);                \
                                   ((struct ourSemaphore *)x)->count++;       \
                                   _RESTOR_INT (priorInterruptState,          \
                                                priorFIQState); }
#endif /* BSP_SERIAL_FAST_INTERRUPT */

#ifdef DEBUG_SERIAL
int rrdyc =0;                /* these are counter for debugging purpose */
int rbcc = 0;
int xonc = 0;
int xoffc = 0;
int recvc = 0;
#endif

/* circular buffer for storing input and output characters */
typedef struct {
    int fp, bp;                         /* front pointer, back pointer */
    int count;                          /* number of char in buffer */
    char data[SIZE_OF_BUF+1];           /* data array */
} CBUFFER;

#if REALLY_OLD_DMA
typedef struct{
    int count;                          /* number of bytes in the data array */
    int bd_id;                          /* buffer descriptor id */
} DMAQtype;

static volatile int m_qhead_head[NUM_NCC_SER_CHAN];
#if 0
static volatile int m_qhead_tail[NUM_NCC_SER_CHAN];  /* keeps track of the DMA input */
#endif
static volatile DMAQtype m_qhead[NUM_NCC_SER_CHAN][M_QHEAD_SIZE];               /* DMA data queue. */
#endif /* REALLY_OLD_DMA */

/* There are 2 buffers for each channel, one for recv and one
   for transmit, implemented using circular array */
static CBUFFER tx_buffer[NUM_NCC_SER_CHAN], rx_buffer[NUM_NCC_SER_CHAN];

#if REALLY_OLD_DMA
/*-----------------08/16/2000 2:02PM----------------
 * We are adding DMA tranmitting and receiving buffers
 * --------------------------------------------------*/
/*set up two receive buffers */
static char dma_ser_rx_buf[NUM_NCC_SER_CHAN][NUM_DMA_RX_BUF][SIZE_OF_DMA_BUF];
/* DMA transmission channel descriptor */
static volatile fb_buffer_desc_t  ser_tx_buffer_des[NUM_NCC_SER_CHAN];  

/* DMA receiving channel descriptor */
static volatile fb_buffer_desc_t  ser_rx_buffer_des[NUM_NCC_SER_CHAN][NUM_DMA_RX_BUF];  

#if 0
static int ser_DMA_bd_count[NUM_NCC_SER_CHAN];  /* keep tracks of receving buffer descriptor */
#endif
#endif /* REALLY_OLD_DMA */

/* Local data structures */
static ONCC_SIO_CHAN_PARAS devParas[] =
{
    {INT_VEC_SERIAL1_RX, SER1RX_INT_LVL, INT_VEC_SERIAL1_TX, SER1TX_INT_LVL},
    {INT_VEC_SERIAL2_RX, SER2RX_INT_LVL, INT_VEC_SERIAL2_TX, SER2TX_INT_LVL},
};

#if REALLY_OLD_DMA || NEW_DMA
static ONCC_SIO_CHAN_PARAS devDmaParas[] =
{
    {DMA_VEC_SERIAL1_RX, SER1RX_DMA_LVL, DMA_VEC_SERIAL1_TX, SER1TX_DMA_LVL}, 
    {DMA_VEC_SERIAL2_RX, SER2RX_DMA_LVL, DMA_VEC_SERIAL2_TX, SER2TX_DMA_LVL},
};
#endif

static ONCC_CHAN onccChan[NUM_NCC_SER_CHAN];
static unsigned int indexOfAvailableOnccStructures = 0;

#if NEED_ECHO
static void echo (int arg, int channel, void *buffer, int len);
#endif

static struct termios current_termios[NUM_NCC_SER_CHAN];
static struct serial_cntr_t serial_counters[NUM_NCC_SER_CHAN];
static struct serial_status_t serial_status[NUM_NCC_SER_CHAN];
static struct sigchange_stats_t serial_sigchange[NUM_NCC_SER_CHAN];

//char paulsRcvCharBuffer[1000];
//char paulsTxCharBuffer[1000];

/* ------------------------------------------------------------*/
/* Circular buffering implementation                           */
/* ------------------------------------------------------------*/


/* is buffer empty */
#define _CB_EMPTY(q)                ((((CBUFFER *)q)->count == 0) ? 1 : 0)

/* is buffer full */
#define _CB_FULL(q)                 ((((CBUFFER *)q)->count == SIZE_OF_BUF) \
                                        ? 1 : 0)

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
/* initialize */
#define _CB_RESET(q)       {int priorInterruptState, priorFIQState;           \
                            _DISAB_INTS(priorInterruptState, priorFIQState);  \
                            ((CBUFFER *)q)->fp = 1; ((CBUFFER *)q)->bp =0;    \
                            ((CBUFFER *)q)->count = 0;                        \
                            _RESTOR_INT (priorInterruptState, priorFIQState); }


/* insert a byte at rear */
#define _CB_ADD(q,c)       {int priorInterruptState, priorFIQState;           \
                            _DISAB_INTS(priorInterruptState, priorFIQState);  \
                            ((CBUFFER *)q)->bp++;                             \
                            if (((CBUFFER *)q)->bp > SIZE_OF_BUF)             \
                                ((CBUFFER *)q)->bp = 0;                       \
                            ((CBUFFER *)q)->data[((CBUFFER *)q)->bp]=(char)c; \
                            ((CBUFFER *)q)->count++;                          \
                            _RESTOR_INT (priorInterruptState, priorFIQState); }

/* remove a byte at front */
#define _CB_REMOVE(q,c)    {int priorInterruptState, priorFIQState;           \
                            _DISAB_INTS(priorInterruptState, priorFIQState);  \
                            *(char *)c =                                      \
				 ((CBUFFER *)q)->data[((CBUFFER *)q)->fp];    \
                            ((CBUFFER *)q)->fp++;                             \
                            if (((CBUFFER *)q)->fp > SIZE_OF_BUF)             \
                                ((CBUFFER *)q)->fp = 0;                       \
                            ((CBUFFER *)q)->count--;                          \
                            _RESTOR_INT (priorInterruptState, priorFIQState); }

#else
/* initialize */
#define _CB_RESET(q)       {int priorInterruptState;                          \
                            _DISAB_INTS(priorInterruptState, ignored);        \
                            ((CBUFFER *)q)->fp = 1; ((CBUFFER *)q)->bp =0;    \
                            ((CBUFFER *)q)->count = 0;                        \
                            _RESTOR_INT (priorInterruptState, ignored); }


/* insert a byte at rear */
#define _CB_ADD(q,c)       {int priorInterruptState;                          \
                            _DISAB_INTS(priorInterruptState, ignored);        \
                            ((CBUFFER *)q)->bp++;                             \
                            if (((CBUFFER *)q)->bp > SIZE_OF_BUF)             \
                                ((CBUFFER *)q)->bp = 0;                       \
                            ((CBUFFER *)q)->data[((CBUFFER *)q)->bp]= (char)c;\
                            ((CBUFFER *)q)->count++;                          \
                            _RESTOR_INT (priorInterruptState, ignored); }

/* remove a byte at front */
#define _CB_REMOVE(q,c)    {int priorInterruptState;                          \
                            _DISAB_INTS(priorInterruptState, ignored);        \
                            *(char*)c=((CBUFFER *)q)->data[((CBUFFER*)q)->fp];\
                            ((CBUFFER *)q)->fp++;                             \
                            if (((CBUFFER *)q)->fp > SIZE_OF_BUF)             \
                                ((CBUFFER *)q)->fp = 0;                       \
                            ((CBUFFER *)q)->count--;                          \
                            _RESTOR_INT (priorInterruptState, ignored); }


#endif /* BSP_SERIAL_FAST_INTERRUPT */

/* return number of bytes in buffer */
#define _CB_NBUFFER(q)              ((CBUFFER *)q)->count

#define _TX_FLOW_CONTROLLED(chan) ((chan->tx_hw_flow) || (chan->tx_sw_flow) \
                                  || (chan->tx_user_flow) )

static unsigned long iph_up_ctr[2];
//static unsigned long iph_down_ctr[2];
static unsigned long rx_buf_maxcount1[2];
static unsigned long rx_buf_maxcount2[2];
static unsigned long lines[2][32];
static unsigned long datalines[2][32];
static unsigned long morelines[2];
static unsigned long nreads[2][5];

static unsigned long rx_note[NUM_NCC_SER_CHAN];
static unsigned long rx_noted[NUM_NCC_SER_CHAN];

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
static int fiq_list[NUM_NCC_SER_CHAN];
static int fiq_ports;
static int fiq_up = 0;
#endif

static void
change_rx_flow(ONCC_CHAN * pChan)
{
    int flowstate = 0;
    int prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif

    if (pChan->rx_hw_flow || 
        pChan->rx_sw_flow || 
        pChan->rx_user_flow || 
        pChan->rx_alarm_flow) 
    {
        flowstate = 1;
    }

    _DISAB_INTS (prevInterruptState, prevFIQState);
    if (pChan->sw_flowc) {
        /* waiting for XMITR_RDY */
        while (!(XMITR_RDY_IRQ 
                 & (SER_REG (pChan->serChannel, STAT_REG))))
            ;
                
        SER_REG_BYTE(pChan->serChannel, DATA_REG) = 
            (flowstate) ? pChan->xoff_ch : pChan->xon_ch;

    }

    if (pChan->hw_flowc) {
        UINT32 reg = SER_REG(pChan->serChannel, CTRL_REGA);
        if (flowstate) {
            reg &= ~RTS_ON;
        } else {
            reg |= RTS_ON;
        }
        SER_REG (pChan->serChannel, CTRL_REGA) = reg;
    }
    _RESTOR_INT(prevInterruptState, prevFIQState);
}

/***********************************************************************
 *			DMA implementation funcs begin
 **********************************************************************/
#if NEW_DMA
#define NUM_RX_DMA_BUF		32
#define SIZE_RX_DMA_BUF		(SIZE_OF_BUF / NUM_RX_DMA_BUF)

#define RX_BMD_HIWAT		(NUM_RX_DMA_BUF * 2 / 3)
#define RX_BMD_LOWAT		(NUM_RX_DMA_BUF / 3)

/* DMA transmission channel descriptor */
static volatile fb_buffer_desc_t  tx_bmds[NUM_NCC_SER_CHAN];  

/* DMA receiving channel descriptor */
static volatile fb_buffer_desc_t  rx_bmds[NUM_NCC_SER_CHAN]
				         [NUM_RX_DMA_BUF];  

/* indices for circular queue processing */
static volatile fb_buffer_desc_t *rx_bmd_start[NUM_NCC_SER_CHAN];
static volatile fb_buffer_desc_t *rx_bmd_end[NUM_NCC_SER_CHAN];
static volatile fb_buffer_desc_t * volatile rx_bmd_in[NUM_NCC_SER_CHAN];
static volatile fb_buffer_desc_t * volatile rx_bmd_out[NUM_NCC_SER_CHAN];
static int rx_bmd_partial[NUM_NCC_SER_CHAN];

static volatile UINT32 *rx_dma_ctrl[NUM_NCC_SER_CHAN];
static volatile UINT32 *rx_dma_status[NUM_NCC_SER_CHAN];
static volatile UINT32 *rx_dma_bmd[NUM_NCC_SER_CHAN];

static volatile UINT32 *tx_dma_ctrl[NUM_NCC_SER_CHAN];
static volatile UINT32 *tx_dma_status[NUM_NCC_SER_CHAN];
static volatile UINT32 *tx_dma_bmd[NUM_NCC_SER_CHAN];

static int dmamode[NUM_NCC_SER_CHAN];
static int prevmode[NUM_NCC_SER_CHAN];

/* serial_dma_wrint
 *
 * Serial DMA write interrupt
 */
static int 
serial_dma_wrint(void *context)
{
    ONCC_CHAN *pChan = (ONCC_CHAN *)context;
    int channel = pChan->serChannel;
    UINT32 status = *tx_dma_status[channel];

    /* Acknowledge interrupt */
    *tx_dma_status[channel] |= status;

    /* Determine if there's an error */
    if (status & DMA_ERR_DONE_IP) {
        /* Bad BMD or buffer pointer: Implies queuing code queued wrong */
        assert(0);
    }
    if (status & DMA_BUF_NOT_RDY_IP) {
	/* Queue is finished because F was not set on the next BMD */

        *tx_dma_ctrl[channel] &= ~DMA_CHANNEL_ENABLE;
    }
    if (status & DMA_CHAN_ABORT_IP) {
	assert(0);
    }
#if 0
    if (status & DMA_NORM_DONE_IP) 
#endif
    {
	serial_counters[channel].tbytes += 
	    tx_bmds[channel].upper_word.bits.buflen;
    }

    return 0;
}

/* serial_dma_rdint
 *
 * Serial DMA read interrupt
 */
static int 
serial_dma_rdint(void *context)
{
    ONCC_CHAN *pChan = (ONCC_CHAN *)context;
    int channel = pChan->serChannel;
    volatile fb_buffer_desc_t *currbmd = 0;
    UINT32 status = *rx_dma_status[channel];

    /* Acknowledge interrupt */
    *rx_dma_status[channel] |= status;

    if (status & DMA_CHAN_ABORT_IP) {
	assert(0);
    }

    if (status & DMA_CLAER_ALL_INTERRUPTS & ~DMA_NORM_DONE_IP) {
	if (status & DMA_BUF_NOT_RDY_IP) {
	    serial_counters[channel].noflow++;
	} else if (status & DMA_ERR_DONE_IP) {
	    assert(0);
	} 
    }

    currbmd = rx_bmd_in[channel];

    while (currbmd->upper_word.bits.full) {
        volatile fb_buffer_desc_t *next_bmd;
	rx_note[channel]++;

	next_bmd = currbmd + 1;
	if (next_bmd == rx_bmd_end[channel]) {
	    next_bmd = rx_bmd_start[channel];
	}
    
	if (next_bmd == rx_bmd_out[channel]) {
	    break;
	}
	currbmd = next_bmd;
    } 
    rx_bmd_in[channel] = currbmd;

    if (!(serial_status[channel].estat & EV_IPS) ) {
	int in = rx_bmd_in[channel] - rx_bmd_start[channel];
	int out = rx_bmd_out[channel] - rx_bmd_start[channel];
	UINT32 count;
	if (in >= out)
	    count = in - out;
	else
	    count = in + NUM_RX_DMA_BUF - out;

	if (count > RX_BMD_HIWAT) {
	    //serial_status[channel].estat |= EV_IPS;
	    pChan->rx_hw_flow = TRUE; 
	    //serl_cb_event(channel, IPS_EVENT);
        
	    if (!(serial_status[channel].estat & EV_IPU)) {
		change_rx_flow(pChan);
	    }
	}
    }
    return 0;
}

/* serial_dma_start 
 *
 * To be called to set up the DMA channels for use when O_DMA is used.  */
static void
serial_dma_start(ONCC_CHAN *pChan)
{
    int channel = pChan->serChannel;
    int i;
    /* Configure BMDs */

    /* We're only using one transmit BMD for now */
    tx_bmds[channel].lower_word.bits.wrap = 1;
    tx_bmds[channel].lower_word.bits.intr = 0;
    tx_bmds[channel].lower_word.bits.last = 1;
    tx_bmds[channel].upper_word.bits.status = 0;
    tx_bmds[channel].upper_word.bits.full = 0;

    for (i = 0 ; i < NUM_RX_DMA_BUF ; i++) {
	rx_bmds[channel][i].lower_word.bits.wrap = 0;
	rx_bmds[channel][i].lower_word.bits.intr = 0;
	rx_bmds[channel][i].lower_word.bits.last = 0;
	rx_bmds[channel][i].lower_word.bits.sbp  
	    = (unsigned int)&rx_buffer[channel].data[i * SIZE_RX_DMA_BUF];

	rx_bmds[channel][i].upper_word.bits.status = 0;
	rx_bmds[channel][i].upper_word.bits.full   = 0;
	rx_bmds[channel][i].upper_word.bits.buflen = SIZE_RX_DMA_BUF;
	
    }
    /* Last BMD wraps to front */
    rx_bmds[channel][i - 1].lower_word.bits.wrap = 1;

    /* Configure BMD pointers */
    rx_bmd_start[channel] = &rx_bmds[channel][0];
    rx_bmd_end[channel]   = &rx_bmds[channel][NUM_RX_DMA_BUF];
    rx_bmd_in[channel]    = rx_bmd_start[channel];
    rx_bmd_out[channel]   = rx_bmd_start[channel];
    rx_bmd_partial[channel]  = 0;

    /* Configure controller initial register configuration */
    switch(channel) {
    case 0:
	rx_dma_ctrl[channel]   = (UINT32*)NARM_DCR7_ADDR;    
	rx_dma_status[channel] = (UINT32*)NARM_DSIER7_ADDR;
	rx_dma_bmd[channel]    = (UINT32*)NARM_DBDP7_ADDR;

	tx_dma_ctrl[channel]   = (UINT32*)NARM_DCR8_ADDR;
	tx_dma_status[channel] = (UINT32*)NARM_DSIER8_ADDR;
	tx_dma_bmd[channel]    = (UINT32*)NARM_DBDP8_ADDR;
	break;
    case 1:
	rx_dma_ctrl[channel]   = (UINT32*)NARM_DCR9_ADDR;    
	rx_dma_status[channel] = (UINT32*)NARM_DSIER9_ADDR;
	rx_dma_bmd[channel]    = (UINT32*)NARM_DBDP9_ADDR;

	tx_dma_ctrl[channel]   = (UINT32*)NARM_DCR10_ADDR;
	tx_dma_status[channel] = (UINT32*)NARM_DSIER10_ADDR;
	tx_dma_bmd[channel]    = (UINT32*)NARM_DBDP10_ADDR;
	break;
    };

    *rx_dma_ctrl[channel]   = (*rx_dma_ctrl[channel] 
			       & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK))
			       | (FLY_BY_WRITE);
    *rx_dma_status[channel] = (DMA_CLAER_ALL_INTERRUPTS | DMA_NORM_DONE_IE 
			       | DMA_ERR_DONE_IE | DMA_CHAN_ABORT_IE);
    *rx_dma_bmd[channel]    = (UINT32)&rx_bmds[channel][0];

    *tx_dma_ctrl[channel]   = (*tx_dma_ctrl[channel] 
			       & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK))
			       | (FLY_BY_READ);
    *tx_dma_status[channel] = (DMA_CLAER_ALL_INTERRUPTS | DMA_NORM_DONE_IE 
			       | DMA_ERR_DONE_IE | DMA_BUF_NOT_RDY_IE 
			       | DMA_CHAN_ABORT_IE);
    *tx_dma_bmd[channel]    = (UINT32)&tx_bmds[channel];

    /* Install DMA interrupt */
    NAInstallIsr(devDmaParas[channel].rxvector,
		 (NA_ISR_HANDLER)serial_dma_rdint,
		 (void *)pChan);

    NAInstallIsr(devDmaParas[channel].txvector,
		 (NA_ISR_HANDLER)serial_dma_wrint,
		 (void *)pChan);

    /* Tell serial channel we're using DMA w/ it */
    SER_REG(channel, CTRL_REGA) |= (XMITR_DMA_ON | RECV_DMA_ON);

    /* Enable receive channel */
    *rx_dma_ctrl[channel] |= DMA_CHANNEL_ENABLE;

}

/* serial_dma_stop
 *
 * To be called when exiting a session where O_DMA was active.
 * Disables the DMA channel
 */
static int
serial_dma_stop(ONCC_CHAN *pChan)
{
    int channel = pChan->serChannel;
    /* Go back to FIFO mode */
    SER_REG(channel, CTRL_REGA) &= ~(XMITR_DMA_ON | RECV_DMA_ON);

    /* Disable receive channels */
    *rx_dma_ctrl[channel] &= ~(DMA_CHANNEL_ENABLE);

    /* Uninstall DMA interrupt */
    /* Don't need to with channel disabled */

    /* Sanitize buffers for interrupt/polling use */
    /* Should be in the same shape they were before use */

    return 0;
}

/* serial_dma_write
 *
 * Queue data for transmit when O_DMA is configured
 */
    /* If TX BMD is free
     *     Set up BMD to transmit user buffer
     *     Wait for transmit to complete
     * else
     *     Return error
     */

    /* OR */

    /* If there are TX BMD(s) free
     *     Copy data into tx buffer
     *     Configure TX BMD(s) to transmit data
     * else
     *     Return error
     */
static int 
serial_dma_write(ONCC_CHAN *pChan, void * buffer, 
		 int length, int *bytesWritten) 
{
    int bytesToWrite = 0;
    const int MAX_DMA_BUFFER_SIZE = 32700;   /* max DMA buffer size */
    int channel = pChan->serChannel;

    if (tx_bmds[channel].upper_word.bits.full == 1) {
	return EBUSY;
    }

    bytesToWrite = (length > MAX_DMA_BUFFER_SIZE)
	? MAX_DMA_BUFFER_SIZE 
	: length;

    tx_bmds[channel].lower_word.bits.sbp = (unsigned long)buffer;

    tx_bmds[channel].upper_word.bits.buflen   = bytesToWrite;  
    tx_bmds[channel].upper_word.bits.full     = 1;            
    *bytesWritten = bytesToWrite;  /* bytesWritten has number of type sent */

    *tx_dma_ctrl[channel] |= DMA_CHANNEL_ENABLE;
    
    /* Wait for DMA transfer to complete - UGH */
    while (tx_bmds[channel].upper_word.bits.full == 1) {
	tx_thread_sleep(1);
    }
    return 0;

}

/* serial_dma_read
 *
 * read data when O_DMA is configured
 */
static int
serial_dma_read(ONCC_CHAN * pChan, char * buffer, int length, int *bytesRead)
{
    int prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
    volatile fb_buffer_desc_t *currbmd;
    int channel = pChan->serChannel;
    int ret = 0;

    /* While we have not fulfilled user requested data size
     *     Copy data from RX DMA buffers to user
     */

    currbmd = rx_bmd_out[channel];
    while (length) {
	int bmdlen;
	char *srcaddr;
	int tocopy;
		
        int dobreak = 0;
	/* Read blocking */
	while (currbmd == rx_bmd_in[channel]) {
	    // No more data to read right now
	    if (pChan->openmode & O_NONBLOCK) {
		ret = (*bytesRead) ? 0 : EAGAIN;
		dobreak = 1;
		break;
	    } else {
		// Block
		if (*bytesRead) {
		    dobreak = 1;
		    break; /* We've read data, no need to block */
		} else {
		    tx_thread_sleep(1);
		    currbmd = rx_bmd_out[channel];
		}
	    }
	}
	if (dobreak == 1) {
	    break;
	}

	bmdlen = currbmd->upper_word.bits.buflen - rx_bmd_partial[channel];
	srcaddr = (char *)
	    (currbmd->lower_word.bits.sbp + rx_bmd_partial[channel]);
	tocopy = (length > bmdlen) ? bmdlen : length;

	fmemcpy(buffer, srcaddr, tocopy);
	buffer += tocopy;
	length -= tocopy;
	bmdlen -= tocopy;
	rx_bmd_partial[channel] += tocopy;
	*bytesRead += tocopy;
	
	/* After consuming a BMD, return it to the correct size and move out*/
	if (bmdlen == 0) {
	    currbmd->upper_word.bits.buflen = SIZE_RX_DMA_BUF;
	    currbmd->upper_word.bits.full = 0;
	    
	    currbmd++;

	    if (currbmd == rx_bmd_end[channel]) {
		currbmd = rx_bmd_start[channel];
	    }

	    rx_bmd_partial[channel] = 0;
	    rx_bmd_out[channel] = currbmd;
	}
    }
    
    /* We may have made it possible to proceed after an NRIP condition
     * Hit the NRIP bit to possibly resume transfer */
    _DISAB_INTS (prevInterruptState, prevFIQState);
    *rx_dma_status[channel] = (*rx_dma_status[channel]
			       & ~(DMA_CLAER_ALL_INTERRUPTS)) 
	| DMA_BUF_NOT_RDY_IP;
    _RESTOR_INT(prevInterruptState, prevFIQState);

    /* Release flow control if necessary */
    //if(serial_status[channel].estat & EV_IPS) {
    if (pChan->rx_hw_flow == TRUE) {
	int in  = rx_bmd_in[channel]  - rx_bmd_start[channel] ;
	int out = rx_bmd_out[channel] - rx_bmd_start[channel] ;
	UINT32 count ;
	if(in >= out)
	    count = in - out ;
	else
	    count = in + NUM_RX_DMA_BUF - out ;

	if(count < RX_BMD_LOWAT) {
	    //_DISAB_INTS (prevInterruptState, prevFIQState);
	    //serial_status[channel].estat &= ~EV_IPS;
	    //_RESTOR_INT(prevInterruptState, prevFIQState);
	    pChan->rx_hw_flow = FALSE; 
	    //serl_cb_event(channel, IPS_EVENT);
	    // if not also stopped unconditionally
	    if(!(serial_status[channel].estat & EV_IPU)) {
		change_rx_flow(pChan) ;
	    }
	}
    }
    
    return ret;
}

#endif /* NEW_DMA */

/***********************************************************************
 *			DMA implementation funcs end
 **********************************************************************/


static int InsertUsersDataBuffer (CBUFFER *queue, char *userBuffer, 
                                  int szBuffer)
{
    int data_available, nbytes, bytesToCopyPhase1, prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif

    /* how much space is available?     */
    data_available = (SIZE_OF_BUF + 1) - _CB_NBUFFER (queue);
    
    /*  insert only the number of bytes that will fit in    */
    nbytes = (data_available > szBuffer) ? szBuffer : data_available;

    if (queue->bp + nbytes < sizeof(queue->data))
    {
        /*  one copy will do the trick  */
        fmemcpy (queue->data + queue->bp + 1, userBuffer, nbytes);
    }
    else
    {
        /*  insert the data in two moves   */
        bytesToCopyPhase1 = sizeof(queue->data) - (queue->bp + 1);

        fmemcpy (queue->data + queue->bp + 1, userBuffer, bytesToCopyPhase1);
        fmemcpy (queue->data, userBuffer + bytesToCopyPhase1,
                 nbytes - bytesToCopyPhase1);
    }

    _DISAB_INTS (prevInterruptState, prevFIQState);
    queue->count = queue->count + nbytes;
    queue->bp = queue->bp + nbytes;
    if (queue->bp > SIZE_OF_BUF)
    {
        queue->bp = queue->bp - sizeof(queue->data);
    }
    _RESTOR_INT (prevInterruptState, prevFIQState);

    return nbytes;
}

static int ExtractSerialDataIntoUsersBuffer (CBUFFER *queue, char *userBuffer, 
                                             int szBuffer)
{
    int data_available, nbytes, bytesToCopyPhase1, prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif

    /* how much data is ready?  */
    data_available = _CB_NBUFFER (queue);
    
    /*  extract only the number of bytes the user wants */
    nbytes = (data_available > szBuffer) ? szBuffer : data_available;

    if (queue->fp + nbytes > sizeof(queue->data))
    {
        /*  extract the data in two moves   */
        bytesToCopyPhase1 = sizeof(queue->data) - queue->fp;

        fmemcpy (userBuffer, queue->data + queue->fp, bytesToCopyPhase1);
        fmemcpy (userBuffer + bytesToCopyPhase1, queue->data, 
                 nbytes - bytesToCopyPhase1);
    }
    else
    {
        /*  one copy will do the trick  */
        fmemcpy (userBuffer, queue->data + queue->fp, nbytes);
    }

    _DISAB_INTS (prevInterruptState, prevFIQState);
    queue->count = queue->count - nbytes;
    queue->fp = queue->fp + nbytes;
    if (queue->fp > SIZE_OF_BUF)
    {
        queue->fp = queue->fp - sizeof(queue->data);
    }
    _RESTOR_INT (prevInterruptState, prevFIQState);

    return nbytes;
}



/****************************************************************************
 ****************************************************************************
 ***
 *** Function Name:  OptSet()
 ***
 *** Called by:      onccIoctl(),onccInitChannel()
 ***
 *** Comments: This routine sets up the hardware (serial Control Register A)
 *** depending on the option argument.  If the hardware cannot support the
 *** particular option then it ignores that part of the request.
 ***
 *** Returns OK upon success, or EIO for invalid arguments.
 ***************************************************************************
 ***************************************************************************/
static STATUS OptSet (ONCC_CHAN * pChan, int newOpts)
{
    UINT32 reg;

    if ((pChan == NULL) || (newOpts & 0xfffffe00))
    {
        return EIO;
    }

    /* get current register settings */
    reg = SER_REG (pChan->serChannel, CTRL_REGA);  
    reg &= REGA_OPTIONS_MASK;          /* reset current options settings */
    reg |= SER_CHANNEL_ENABLE;         /* channel is always enabled */

    newOpts &= ~inHUPCL;                 /* We don't support HUPCL */

    switch (newOpts & inCSIZE)
    {
    case inCS5:
        reg |= CTRL_FIVE_BITS;
        break;

    case inCS6:
        reg |= CTRL_SIX_BITS;
        break;

    case inCS7:
        reg |= CTRL_SEVEN_BITS;
        break;

    default:
    case inCS8:
        reg |= CTRL_EIGHT_BITS;
        break;
    }

    if (newOpts & inSTOPB)
    {
        reg |= CTRL_TWO_STOP_BITS;
    }
    else
    {
        reg |= CTRL_ONE_STOP_BIT;
    }

    if (newOpts & inPARENB) { /* Parity enabled */
        if (newOpts & inXPAR) { /* Mark/space parity? */
            if (newOpts & inPARODD) { 
                reg |= CTRL_MARK_PARITY;
            } else /* !inPARODD */ { 
                reg |= CTRL_SPACE_PARITY;
            }
        } else /* !inXPAR */ {
            if (newOpts & inPARODD) { 
                reg |= CTRL_ODD_PARITY;
            } else /* !inPARODD */ { 
                reg |= CTRL_EVEN_PARITY;
            }
        }
    } else {
        reg &= CTRL_NO_PARITY;
    }
        
    pChan->opMode &= ~OP_MODE_HANDSHAKE_MASK;

    //TODO: Eventually rather than have flow control modes be
    //exclusive, we'd like to have them be able to be used in concert

    if (newOpts & SIO_HW_RTS_CTS_HANDSHAKING) {
        reg |= (RTS_ON | CTS_XMIT );   /* hardware handshaking */
        pChan->sw_flowc = FALSE;
        pChan->hw_flowc = TRUE;
        pChan->opMode  |= OP_MODE_HW_HANDSHAKE;
    } else if (newOpts & SIO_SW_HANDSHAKING) {
#if REALLY_OLD_DMA || NEW_DMA /* might want to keep */
        if (!( pChan->openmode & O_DMA))
#endif
        {
            reg &= ~(CTS_XMIT | RTS_RECV);    /* Disable hardware handshake */
            pChan->sw_flowc = TRUE;
            pChan->hw_flowc = FALSE;
            pChan->xon_ch   = current_termios[pChan->serChannel].c_cc[VSTART];
            pChan->xoff_ch  = current_termios[pChan->serChannel].c_cc[VSTOP];
            pChan->opMode  |= OP_MODE_SW_HANDSHAKE;
#ifdef DEBUG_SERIAL
            printf("SIO_SW_HANDSHAKING IS SET \n");
#endif
        }
#if REALLY_OLD_DMA || NEW_DMA /* might want to keep */
        else
        {
            /* SW Handshaking doesn't work in DMA mode -- that makes sense  */
            return EIO;
        }
#endif
    } else { // No flow control
#if REALLY_OLD_DMA /* might want to keep */
        if (!( pChan->openmode & O_DMA))
#endif
        {
            /* if bit zero is set, disable hardware handshake */
            reg &= ~(CTS_XMIT | RTS_RECV);
            pChan->sw_flowc = FALSE;
            pChan->hw_flowc = FALSE;
            pChan->opMode  |= OP_MODE_NO_HANDSHAKE;
#ifdef DEBUG_SERIAL
            printf("SIO_NO_HANDSHAKING IS SET \n");
#endif
        }
#if REALLY_OLD_DMA /* might want to keep */
        else
        {
            /* doesn't work in DMA mode -- why not? */
            return EIO;
        }
#endif
    }

#ifdef DEBUG_SERIAL
    printf("rdy= %d, bc=%d recvc=%d  \n",rrdyc,rbcc,recvc);
    rrdyc = 0;
    rbcc = 0;
    xonc = 0;
    xoffc = 0;
    recvc=0;
#endif
    /* reset the device according to new options */
    SER_REG (pChan->serChannel, CTRL_REGA) = reg;  
    pChan->options = newOpts;           /* save new options */
    if ( pChan->sw_flowc) {             /* if we have software handshake */
        /* We enable receive character receive registers for xon and xoff */
        reg = SER_REG (pChan->serChannel, CTRL_REGB);  
        reg |= (RECV_DATA_MATCH1_ON | RECV_DATA_MATCH2_ON);     
        SER_REG (pChan->serChannel, CTRL_REGB) = reg;  

        /* Setup match registers for SW flow characters */
        reg = SER_REG (pChan->serChannel, RECV_MATCH_REG);  
        reg = ((0x000000ff & XON) << 24) + ((0x000000ff & XOFF)<<16);
        SER_REG (pChan->serChannel, RECV_MATCH_REG) = reg;

#if REALLY_OLD_DMA /* Should never get here with O_DMA anyway */
        if (!(pChan->openmode & O_DMA))
#else
            /* Should never get here with O_DMA */
            assert(!(pChan->openmode & O_DMA));
#endif
        {
            NAEnableIsr (pChan->txlevel); 
            //SER_REG (pChan->serChannel, CTRL_REGA) |= XMITR_RDY_IRQ;
            SER_REG (pChan->serChannel, CTRL_REGA) |= XMITR_HALF_FULL_IRQ;
        }

        SER_REG (pChan->serChannel, STAT_REG) = CLR_ALL_RX_CONDITIONS;

#ifdef DEBUG_SERIAL
        printf("enable xmitr_rdy_irq \n");
#endif
    }
    else {
        reg = SER_REG (pChan->serChannel, CTRL_REGB);
        reg &= ~(RECV_DATA_MATCH1_ON | RECV_DATA_MATCH2_ON);
        SER_REG (pChan->serChannel, CTRL_REGB) = reg;
    }

    return (OK);
}

/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  get_baud_div()
 ***
 *** Called by:      onccInitChannel(), onccIoctl()
 ***
 *** Comments: This routine returns the baud rate divisor which is required
 *** to set up the hardware for the desired baudrate.  Also returns STATUS:
 *** STATUS = to the minimum error for the baudrate using the divr value.
 ***
 *** This function calculates the optimum N value (Divr based on the values
 *** of SysClk or Xtal Frequency.
 *****************************************************************************
 *****************************************************************************/
struct baudTable {
    unsigned int value;
    unsigned int divr_or;
};
enum baudClkIdx {
    XTAL_IDX,
    SYS_IDX,
};

static struct baudTable Clocks[] = {
    { 1 /* Xtal placeholder */, BIT_RATE_CLK_EXT }, //XTAL_IDX
    { 1 /* Sysclk placeholder */, BIT_RATE_CLK_SYS }, //SYS_IDX
    {0,0},
};

static struct baudTable Clockmodes[] = {
    {16, BIT_RATE_X16},
    {8, BIT_RATE_X1 | BIT_TDCR_RDCR_X8 | BIT_RATE_RX_DPLL},
    {32, BIT_RATE_X1 | BIT_TDCR_RDCR_X32 | BIT_RATE_RX_DPLL},
    //{1, BIT_RATE_X1 | BIT_RATE_RX_DPLL},
    {0,0},
};

static STATUS
get_baud_div(int baudrate, UINT32 *divr)
{
    const unsigned int ELEVEN_BIT_MAX = 2047;
    unsigned int Clk, bestClkIdx = 0;
    unsigned int clockMode, bestModeIdx = 0;
    unsigned int err, bestErr = 0xffffffff;
    unsigned int N, bestN = ELEVEN_BIT_MAX + 1;
    int clkindex = 0;
    int modeindex = 0;

    /* Iterate through clocks */
    while (Clocks[clkindex].value) {
        switch(clkindex) {
        case XTAL_IDX:
            Clk = NAgetXtalFreq();
            break;
        case SYS_IDX:
            Clk = NAgetSysClkFreq();
            break;
        default:
            assert(0);
            return 100; /* 100% error */
        };
        
        modeindex = 0;
        /* Iterate through divisors */
        while ( (clockMode = Clockmodes[modeindex].value) ) {

            /* Compute N */
            N = Clk / (clockMode * 2 * baudrate) - 1;

            /* Round up? */
            if ( (Clk % (clockMode * 2 * baudrate)) 
                 > (clockMode * baudrate)) 
            {
                /* remainder is more than half */
                N++;
            }

            /* Compute err */
            if (N > ELEVEN_BIT_MAX) {
                err = 0xffffffff;
            } else {
                unsigned int realBaud = Clk / (clockMode * 2 * (N + 1)) ;
                if ((int)realBaud > baudrate) {
                    err = realBaud - baudrate;
                } else {
                    err = baudrate - realBaud;
                }
            }

            if (err < bestErr) {
                bestN = N;
                bestErr = err;
                bestModeIdx = modeindex;
                bestClkIdx = clkindex;
            }

            if (bestErr == 0) {
                break;
            }
            modeindex++;
        }

        if (bestErr == 0) {
            break;
        }
        clkindex++;
    }

    if (bestN == ELEVEN_BIT_MAX + 1) {
        return 100;
    }

    if (bestErr == 0xffffffff) {
        return 100;
    }

    *divr = Clocks[bestClkIdx].divr_or 
        | Clockmodes[bestModeIdx].divr_or
        | bestN;

    return bestErr * 100 / baudrate;
    
}


/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  get_char_gap_count()
 ***
 *** Called by:      onccInitChannel(), onccIoctl()
 ***
 *** Comments: This routine returns the value for the receive character gap
 *** timer. The following equation is from the NCC manual and is supposed to
 *** result in a value which is 10 times the character period for the channel
 *** bit rate.
 *** CT = [(10*Fxtal)/(baudrate * 512)] - 1
 ******************************************************************************
 *****************************************************************************/

static int
get_char_gap_count (int baudrate)
{
    UINT32 ct = MIN_GAP;

    if ((baudrate > MIN_BAUD_RATE) && (baudrate <= MAX_BAUD_RATE)) {
        ct = (UINT32)((10 * NAgetXtalFreq()) / (baudrate * 512)) - 1;
    }
    return (ct);
}




/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccInitChannel()
 ***
 *** Called by:      onccSioDevInit()
 ***
 *** Comments:  Hardware initialization of the NCC serial channel.  
 ******************************************************************************
 *****************************************************************************/
static void onccInitChannel (ONCC_CHAN * pChan)
{
    UINT32 divr;
    UINT32 reg;

    /*** Receive Match Register Configuration ***/
    reg = 0;
    SER_REG (pChan->serChannel, RECV_MATCH_REG) = reg;

    /*** Receive Buffer Gap Timer Register Configuration ***/
    reg = 0;
    SER_REG (pChan->serChannel, RECV_BUF_TIMER_REG) = reg;
    pChan->baudRateError = DEFAULT_BAUD_RATE_ERROR;

    /*** Bit-Rate (baud) Register Configuration ***/
    reg = BIT_GEN_ON | BIT_RATE_X16 | RECV_CLK_SRC_INT | 
        XMIT_CLK_SRC_INT | RECV_CLK_OUT_OFF | XMIT_CLK_OUT_OFF | 
        BIT_RATE_CLK_EXT;

    if (get_baud_div (pChan->baudRate, &divr) > pChan->baudRateError)
        pChan->baudRate = DEFAULT_BAUD_RATE;  /* choose sane rate on error */

    reg |= divr;   /* get_baud_div() returns default divr */
    SER_REG (pChan->serChannel, BAUD_REG) = reg;

    /*** Receive Character Timer Register Configuration ***/
    reg = get_char_gap_count (pChan->baudRate);
    SER_REG (pChan->serChannel, RECV_CHAR_TIMER_REG) = ENABLE_TIMER | reg;

    /*** Control Register B Configuration ***/
    /* Match timer features are not used */
    reg = RECV_CHAR_GAP_TIMER_ON;
    SER_REG (pChan->serChannel, CTRL_REGB) = reg;

    /*** Control Register A Configuration ***/
    /* Enable the serial channel */
    SER_REG (pChan->serChannel, CTRL_REGA) = 0;/*init ctrl reg,turns off ints*/
    OptSet (pChan, pChan->options);    /* go set up options in Reg A */

    // Any potential problem with baud rate and gap timers
    // will be fixed in the netos_serlopen
}

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
void
realSerFIQ_Handler(void)
{
    int i;
    static unsigned int fiq_counter = 0;
    fiq_counter++;

    for ( i = 0 ; i < NUM_NCC_SER_CHAN ; i++ ) {
        if (fiq_list[i]) {
            onccIntRd(&onccChan[i]);
        }
    }
    *NARM_T2SR = 0x40000000;
    return;
}

extern void serFIQ_Handler(void);
#endif

/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccIoctl()
 ***
 *** Called by:
 ***
 *** Comments: This routine handles the IOCTL messages from the user.
 *** RETURNS: OK on success, EIO on device error, ENOSYS on unsupported
 *** request.  Supported requests are: SIO_BAUD_SET/GET, SIO_HW_OPTS_SET/GET,
 *** SIO_MODE_SET/GET.
 ******************************************************************************
 *****************************************************************************/
static STATUS onccIoctl (SIO_CHAN * pSioChan, int request, int arg)
{
    STATUS status;           /* status to return */
    ONCC_CHAN *pChan = (ONCC_CHAN *) pSioChan;
    UINT32 baud_divr;        /* baud rate divisor */
    UINT32 reg;  /* pllreg;               temporary register */

    status = ENOSYS;
    if (request == SIO_BAUD_ERROR_SET)  /* set the baudrate acceptable error */
    {
        pChan->baudRateError = *(int*)arg;
        status = OK;
    }

    if (request == SIO_BAUD_ERROR_GET)  /* get the baudrate acceptable error 1% is default value */
    {
        status = OK;
        *(int *) arg = pChan->baudRateError;
    }

    if (request == SIO_BAUD_SET)
    {
        reg = (UINT32) *(int*)arg;
        status = get_baud_div (*(int*)arg, &baud_divr);  /* returns EIO if invalid */
        if (status <= pChan->baudRateError)
        {
            reg = SER_REG (pChan->serChannel, BAUD_REG) 
                & ~( 
                    (BIT_RATE_DIV_MASK) | 
                    (BIT_RATE_CLK_SYS) | 
                    (BIT_RATE_X1) |
                    (BIT_TDCR_RDCR_X8) | 
                    (BIT_TDCR_RDCR_X16) | 
                    (BIT_RATE_RX_DPLL) | 
                    (BIT_TDCR_RDCR_X32) 
                    );
            /*disable serial port*/
            SER_REG (pChan->serChannel, CTRL_REGA) &= ~0x80000000;
            SER_REG (pChan->serChannel, BAUD_REG) = baud_divr | reg;
            /*enable serial port*/
            SER_REG (pChan->serChannel, CTRL_REGA) |=0x80000000;
#if REALLY_OLD_DMA
            if( !(pChan->openmode & O_DMA))
#endif
            {
                reg = get_char_gap_count (*(int*)arg)*2+10;
                /*if it is too big, set to the MAX the 10 bits can hold, LZ*/
                reg = (reg > 0x3ff)? 0x3ff:reg;  
                SER_REG (pChan->port, RECV_CHAR_TIMER_REG) = 
                    ENABLE_TIMER | reg;
            }
#if REALLY_OLD_DMA
            else
            {
                /* have to increase the time value for DMA */
                reg = get_char_gap_count (*(int*)arg)*2+10;  
                reg =(reg < 11)? 10:reg;
                /*if it is too big, set to the MAX the 10 bits can hold, LZ*/
                reg = (reg > 0x3ff)? 0x3ff:reg;  
                SER_REG (pChan->port, RECV_CHAR_TIMER_REG) = 
                    ENABLE_TIMER | reg;
            }
#endif

            pChan->baudRate = *(int*)arg;
            
            //Fall through to mode setting below if necessary
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
#if NEW_DMA
	    if (pChan->openmode & O_DMA) 
	    {
		/* Go back to the correct mode when we close */
		if (pChan->baudRate >= 115200) {
		    prevmode[pChan->serChannel] = SIO_MODE_FIQ;
		} else {
		    prevmode[pChan->serChannel] = SIO_MODE_INT;
		}
	    } else 
#endif /* NEW_DMA */
	    {
		if (pChan->baudRate >= 115200) {
		    if (pChan->channelMode != SIO_MODE_FIQ) {
			request = SIO_MODE_SET;
			arg = SIO_MODE_FIQ;
		    }
		} else {
		    if (pChan->channelMode != SIO_MODE_INT) {
			request = SIO_MODE_SET;
			arg = SIO_MODE_INT;
		    }
		}
	    }
#endif /* (BSP_SERIAL_FAST_INTERRUPT == TRUE) */
            status = OK;
        }
        else
        {
            status = ERANGE;        /* baud rate error out of range */
        }
    }

    if (request == SIO_BAUD_GET)
    {
        status = OK;
        *(int *) arg = pChan->baudRate;
    }

    //Don't call from outside this function anymore!!
    if (request == SIO_MODE_SET)
    {
        if ( (arg != SIO_MODE_INT)
#if REALLY_OLD_DMA
             && (arg != SIO_MODE_DMA)
#endif
#if NEW_DMA
	     && (arg != SIO_MODE_DMA)
#endif
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
             && (arg != SIO_MODE_FIQ) 
#endif
             )
        {
            status = EIO;
        }
        else
        {
            /* disable all serial interrupts */
            NADisableIsr (pChan->rxlevel);
            //NADisableIsr (pChan->txlevel);
            SER_REG (pChan->serChannel, CTRL_REGA) &= REGA_INTFLAGS_MASK;

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
            if (arg != SIO_MODE_FIQ) {
                if (fiq_list[pChan->serChannel] == 1) {
                    fiq_list[pChan->serChannel] = 0;
                    fiq_ports--;
                }
                
                if (fiq_ports == 0) {
                    /* Disable the timer. */
                    *NARM_T2CR &= 0x7fffffff;  
                    fiq_up = 0;
                }
            }
#endif
#if NEW_DMA
	    if (arg != SIO_MODE_DMA) {
		if (dmamode[pChan->serChannel]) {
		    serial_dma_stop(pChan);
		    dmamode[pChan->serChannel] = 0;
		}
	    }
#endif

            status = OK;
            pChan->channelMode = arg;
            if (arg == SIO_MODE_INT)   /* enable rx interrupt only */
            {                          /* tx int is enabled in tx_startup() */
                NAEnableIsr (pChan->rxlevel);
                SER_REG (pChan->serChannel, CTRL_REGA) &= REGA_INTFLAGS_MASK;
                SER_REG (pChan->serChannel, CTRL_REGA) |= (RECV_IRQ_MASK);
            }

#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
#ifdef STATISTICAL_PROFILER
#error "profiler and serial_fiq cannot be on together"
#endif
            if (arg == SIO_MODE_FIQ) 
            {
                unsigned long ** fiq;
                int InterruptFreq = 23040 / 16; /* 16 char times at 230400 */
                int timerCount;

                if (fiq_up == 0) {
                    fiq_up = 1;
                    NADisableIsr (TIMER2_INT);
                    NAUninstallIsr (TIMER2_INT);
                    
                    fiq = (unsigned long **)0x3c;
                    *fiq = (unsigned long *)&serFIQ_Handler;
                    
                    timerCount = (NAgetXtalFreq() / (8 * InterruptFreq)) - 1;
                    if (timerCount > 134217727)
                    {
                        timerCount=
                            (NAgetXtalFreq() / (4096 * InterruptFreq)) - 1;
                        *NARM_T2CR |= (1 << 28);
                    }
                    *NARM_T2CR |= timerCount;
                    
                    /* Disable the timer. */
                    *NARM_T2CR &= 0x7fffffff;  
                    /* Turn on fast ints and enable ints. */
                    *NARM_T2CR |= 0x60000000;  
                    /* Enable the timer. */
                    *NARM_T2CR |= 0x80000000;  
                }

                if (fiq_list[pChan->serChannel] == 0) {
                    fiq_list[pChan->serChannel] = 1;
                    fiq_ports++;
                }
            }
#endif /* BSP_SERIAL_FAST_INTERRUPT == TRUE */
#if NEW_DMA
	    if (arg == SIO_MODE_DMA) {
		if (!dmamode[pChan->serChannel]) {
		    serial_dma_start(pChan);
		    dmamode[pChan->serChannel] = 1;
		}
	    }
#endif /* NEW_DMA */
        }
    }

    if (request == SIO_MODE_GET)
    {
        status = OK;
        *(int *) arg = pChan->channelMode;
    }

    if (request == SIO_HW_OPTS_SET)
    {
        status = OK;
        if (pChan->options != *(int*)arg)       /* added * to arg */
        {
            status = OptSet (pChan, *(int*)arg);/* added the *(int*)... J.W. */
        }
    }

    if (request == SIO_HW_OPTS_GET)
    {
        status = OK;
        *(int *)arg = pChan->options;
    }

    /* add support for echo */
    /* TODO: Move to the oldapi_shim */
#if NEED_ECHO
    if (request == SIO_TERM_ECHO)
    {
        if (pChan->channelMode != SIO_MODE_INT
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
            && pChan->channelMode != SIO_MODE_FIQ
#endif
            )
        {
            status = EIO;
        }
        else
        {
            status = OK;
            if (pChan->openmode & O_RDWR)
                pChan->echo = *(int *)arg;
            else
                status = EIO;
        }
    }
#endif

    /* add support for sending extra \r */
    if (request == SIO_TERM_SEND_CR)
    {
        if (pChan->channelMode != SIO_MODE_INT
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
            && pChan->channelMode != SIO_MODE_FIQ
#endif
            )
        {
            status = EIO;
        }
        else
        {
            status = OK;
            if (*(int *)arg != TERM_SEND_CR_OFF)
            {
                pChan->opMode  |= OP_MODE_INSERT_CR;
            }
            else
            {
                pChan->opMode  &= ~OP_MODE_INSERT_CR;
            }
        }
    }

    /* add support to check DCD bit... J.W. */
    if (request == SIO_GET_STATUS_DCD)
    {
        status = OK;
        if (SER_REG (pChan->serChannel, STAT_REG) & DCD_STATE)
            *(int *)arg = 1;
        else
            *(int *)arg = 0;
    }

    /* add support to set DTR bit... J.W. */
    if (request == SIO_SET_STATE_DTR)
    {
        status = OK;
        if ((*(int *)arg) == 0)
            SER_REG (pChan->serChannel, CTRL_REGA) &= (~DTR_ON);
        else
            SER_REG (pChan->serChannel, CTRL_REGA) |= DTR_ON;
    }

    /*  Support for monitoring the DSR state    */
    if (request == SIO_GET_STATUS_DSR)
    {
        status = OK;
        if (SER_REG (pChan->serChannel, STAT_REG) & DSR_STATE)
            *(int *)arg = 1;
        else
            *(int *)arg = 0;
    }

    /*  Support for monitoring the CTS state    */
    if (request == SIO_GET_STATUS_CTS)
    {
        status = OK;
        if (SER_REG (pChan->serChannel, STAT_REG) & CTS_STATE)
            *(int *)arg = 1;
        else
            *(int *)arg = 0;
    }

    /*  Support for controlling RTS state    */
    if (request == SIO_SET_STATE_RTS)
    {
        if (pChan->opMode & OP_MODE_RTS_TX_ENCAPSULATION)
        {
            /*  don't mess with the RTS state when you are in this mode */
            status = EIO;
        }
        else
        {
            status = OK;
            if ((*(int *)arg) == 0)
            {
                SER_REG (pChan->serChannel, CTRL_REGA) &= (~RTS_ON);
            }
            else
            {
                SER_REG (pChan->serChannel, CTRL_REGA) |= RTS_ON;
            }
        }
    }



    /*  this mode activates the RTS signal only during active transmissions */
    if (request == SIO_RTS_XMIT_ENCAPSULATION)
    {
        if ((*(int *)arg) == 0)
        {
            /*  turn off the RTS encapsulation  */
            if (pChan->opMode & OP_MODE_RTS_TX_ENCAPSULATION)
            {
                pChan->opMode  &= ~OP_MODE_RTS_TX_ENCAPSULATION;
                status          = OK;
            }
            else
            {
                /*  only allowed with no handshaking    */
                status          = EIO;
            }
        }
        else
        {
            /*  this can only be openned in the No handshake mode   */
            if (pChan->opMode & OP_MODE_NO_HANDSHAKE)
            {
                pChan->opMode          |= OP_MODE_RTS_TX_ENCAPSULATION;
                status                  = OK;
                pChan->inactiveRtsState = SER_REG(pChan->serChannel, 
                                                  CTRL_REGA) & RTS_ON;
                pChan->activeRtsState   = pChan->inactiveRtsState ^ RTS_ON;
                pChan->chars2Encapsuate = 0;
            }
            else
            {
                /*  only allowed with no handshaking    */
                status          = EIO;
            }
        }
    }

    return (status);
}

static STATUS
tcseta(SIO_CHAN *pSioChan, struct termios *termios_p)
{
    ONCC_CHAN *pChan = (ONCC_CHAN*)pSioChan;
    STATUS status = EIO;
    STATUS tmpstatus = OK;
    int opts = 0;
    tcflag_t cflags_set = 0;
    tcflag_t iflags_set = 0;
    tcflag_t xflags_set = 0;

    /* do SIO_BAUD_SET if necessary  */
    if (termios_p->baud != current_termios[pChan->serChannel].baud) {
        tmpstatus = onccIoctl(pSioChan, SIO_BAUD_SET, (int)&termios_p->baud);
        if (tmpstatus == ERANGE) {
            return status;
        }
        current_termios[pChan->serChannel].baud = termios_p->baud;
        status = OK;
    }

    /* do SIO_HW_OPTS_SET if necessary */
    switch (termios_p->c_cflag & CSIZE) {
    case CS5:
        opts |= inCS5;
        cflags_set |= CS5;
        break;
    case CS6:
        opts |= inCS6;
        cflags_set |= CS6;
        break;
    case CS7:
        opts |= inCS7;
        cflags_set |= CS7;
        break;
    case CS8:
        opts |= inCS8;
        cflags_set |= CS8;
        break;
    }
    
    if (termios_p->c_cflag & CSTOPB) {
        opts |= inSTOPB;
        cflags_set |= CSTOPB;
    }

    if (termios_p->c_cflag & PARENB) {
        opts |= inPARENB;
        cflags_set |= PARENB;
        if (termios_p->c_cflag & PARODD) {
            opts |= inPARODD;
            cflags_set |= PARODD;
        }
        if (termios_p->c_xflag & XPAR) {
            opts |= inXPAR;
            xflags_set |= XPAR;
        }
    }

    /* Partial and arbitrary flow control is not allowed in xk, only 
     * RTS/CTS, or full software or nothing */
    if ( (termios_p->c_cflag & CRTSCTS) == CRTSCTS ) {
        opts |= SIO_HW_RTS_CTS_HANDSHAKING;
        cflags_set |= CRTSCTS;
    }  
    
    if ( (termios_p->c_iflag & (IXON | IXOFF)) == (IXON | IXOFF) ) {
        opts |= SIO_SW_HANDSHAKING;
        iflags_set |= IXON | IXOFF;
    } else if ( !((termios_p->c_cflag & CRTSCTS) == CRTSCTS) ) {
        opts |= SIO_NO_HANDSHAKING;
    }

    tmpstatus = onccIoctl(pSioChan, SIO_HW_OPTS_SET, (int)&opts);
    
    if (tmpstatus != OK) {
        return status;
    }

    current_termios[pChan->serChannel].c_cflag 
        &= ~(CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS);
    current_termios[pChan->serChannel].c_cflag |= cflags_set;
    cflags_set = 0;

    current_termios[pChan->serChannel].c_iflag &= ~(IXON | IXOFF);
    current_termios[pChan->serChannel].c_iflag |= iflags_set;
    iflags_set = 0;

    current_termios[pChan->serChannel].c_xflag &= ~(XPAR);
    current_termios[pChan->serChannel].c_xflag |= xflags_set;
    xflags_set = 0;

    

    /* do SIO_TERM_SEND_CR if necessary: ONLCR */
    if ( ((termios_p->c_oflag 
          ^ current_termios[pChan->serChannel].c_oflag) & ONLCR)
#if NEW_DMA
	 && !(pChan->openmode & O_DMA) 
#endif
	 ) 
    {
        if (termios_p->c_oflag & ONLCR) {
            opts = TERM_SEND_CR_ON;
        } else {
            opts = TERM_SEND_CR_OFF;
        }

        tmpstatus = onccIoctl(pSioChan, SIO_TERM_SEND_CR, (int)&opts);
        if (tmpstatus != OK) {
            return status;
        }
        current_termios[pChan->serChannel].c_oflag ^= ONLCR;
    }

    /* do SIO_RTS_XMIT_ENCAPSULATION if necessary: CRTSTOGGLE */
    if ( ((termios_p->c_cflag 
          ^ current_termios[pChan->serChannel].c_cflag) & CRTSTOGGLE)
#if NEW_DMA
	 && !(pChan->openmode & O_DMA) 
#endif
	 ) 
    {
        if (termios_p->c_cflag & CRTSTOGGLE) {
            opts = SIO_RTS_XMIT_ENCAPSULATION;
        } else {
            opts = 0;
        }
        tmpstatus = onccIoctl(pSioChan, SIO_RTS_XMIT_ENCAPSULATION,(int)&opts);
        if (tmpstatus != OK) {
            return status ;
        }
        current_termios[pChan->serChannel].c_cflag ^= CRTSTOGGLE;
    }

    /* Parity flags, we don't do anything, but allow them to be set, so that
     * we can at least do 0xff escaping */
#if NEW_DMA
    if (!(pChan->openmode & O_DMA)) 
#endif
    {
	current_termios[pChan->serChannel].c_iflag &= 
	    ~(PARMRK | IGNPAR | ISTRIP);
	current_termios[pChan->serChannel].c_iflag |= 
	    termios_p->c_iflag & (PARMRK | IGNPAR | ISTRIP);
    }

    /* XALTPIN */
    if (termios_p->c_xflag & XALTPIN) {
        current_termios[pChan->serChannel].c_xflag |= XALTPIN;
    } else {
        current_termios[pChan->serChannel].c_xflag &= ~XALTPIN;
    }
    
    /* XFORCEDCD */
    if (termios_p->c_xflag & XFORCEDCD) {
        current_termios[pChan->serChannel].c_xflag |= XFORCEDCD;
    } else {
        current_termios[pChan->serChannel].c_xflag &= ~XFORCEDCD;
    }

    if (termios_p->c_cc[VSTART] 
        != current_termios[pChan->serChannel].c_cc[VSTART] )
    {
        pChan->xon_ch
            = current_termios[pChan->serChannel].c_cc[VSTART]
            = termios_p->c_cc[VSTART];
    }

    if (termios_p->c_cc[VSTOP] 
        != current_termios[pChan->serChannel].c_cc[VSTOP] )
    {
        pChan->xoff_ch
            = current_termios[pChan->serChannel].c_cc[VSTOP]
            = termios_p->c_cc[VSTOP];
    }

    /* TODO */

    /* INPCK?? */

    status = OK; /* Must have finished okay */
    return status;
}

/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  digiIoctl()
 ***
 *** Called by:
 ***
 *** Comments: This routine handles the IOCTL messages from the user.
 *** RETURNS: OK on success, EIO on device error, ENOSYS on unsupported
 *** request.  
 ******************************************************************************
 *****************************************************************************/
static STATUS digiIoctl (SIO_CHAN * pSioChan, int request, int arg)
{
    STATUS status;           /* status to return */
    ONCC_CHAN *pChan = (ONCC_CHAN *) pSioChan;
    struct termios *termios_p;
#if 0 /* For when we have TCSENDBREAK */
    int duration;
#endif
    int queue_selector, action, prevInterruptState, opt;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
    char c;
    int *done;
    struct serial_cntr_t *cntrs;
    struct serial_status_t *serl_status;
    struct sigchange_stats_t *sigchange;
    struct serial_buffer_t *bufs;
    unsigned char mout;
    unsigned char prev;

    status = (STATUS)ENOSYS;

    switch (request) {
    case TCGETA: 
        status = OK;

        termios_p = (struct termios*)arg;
        memcpy(termios_p, &current_termios[pChan->serChannel], 
               sizeof(current_termios[pChan->serChannel]));
        break; 
    case TCSETA:
        termios_p = (struct termios*)arg;
        status = tcseta(pSioChan, termios_p);
        break;
    case TCSENDBREAK:
        status = ENOSYS; /* TODO: Currently no ability to send break */

        //duration = arg;

        serl_cb_event(pChan->serChannel, TXB_EVENT);
        break;
    case TCDRAIN:
        status = OK;

#if NEW_DMA
	if (pChan->openmode & O_DMA) {
	    status = ENOSYS;
	    break;
	}
#endif

        /* wait until transmitting buffer is empty */
        while (!_CB_EMPTY(&tx_buffer[pChan->serChannel]))  
        {
            tx_thread_sleep (1);
        }

        /* Wait for hardware to stop transmitting */    
        while ( (SER_REG( pChan->serChannel, STAT_REG) & XMITR_EMPTY) == 0 ) {
            tx_thread_sleep(1);
        }

        break;
    case TCFLUSH:
        status = OK;
#if NEW_DMA
	if (pChan->openmode & O_DMA) {
	    status = ENOSYS;
	    break;
	}
#endif
        queue_selector = (enum flush_opts)arg;
        if (queue_selector & TCIFLUSH) { /* Throw away input */
            _CB_RESET (&rx_buffer[pChan->serChannel]);

            //check_rx_flow(pChan);
        } 

        if (queue_selector & TCOFLUSH) { /* Throw away output */
            _CB_RESET (&tx_buffer[pChan->serChannel]);
        }

        assert(!(arg & ~TCIOFLUSH ));
        if (!arg & ~TCIOFLUSH) {
            status = EIO;
        }
        break;
    case TCFLOW:
        status = OK; 

#if NEW_DMA
	if (pChan->openmode & O_DMA) {
	    status = ENOSYS;
	    break;
	}
#endif

        action = (enum flow_opts)arg;
        switch (action) {
        case TCOOFF:
            pChan->tx_user_flow = FALSE;
            serial_status[pChan->serChannel].estat &= ~EV_OPU;
            serl_cb_event(pChan->serChannel, OPU_EVENT);
            onccTxStartup(pSioChan);
            break;
        case TCOON:
            pChan->tx_user_flow = TRUE;
            serial_status[pChan->serChannel].estat |= EV_OPU;
            serl_cb_event(pChan->serChannel, OPU_EVENT);
            break;
        case TCIOFF:
            pChan->rx_user_flow = FALSE;
            serial_status[pChan->serChannel].estat &= ~EV_IPU;
            serl_cb_event(pChan->serChannel, IPU_EVENT);

            change_rx_flow(pChan);
            break;
        case TCION:
            pChan->rx_user_flow = TRUE;
            serial_status[pChan->serChannel].estat |= EV_IPU;
            serl_cb_event(pChan->serChannel, IPU_EVENT);
            
            change_rx_flow(pChan);
            break;
        case TCOSOFF:
        case TCOSON:
        case TCISOFF:
        case TCISON:
        case TCOXOFF:
        case TCOXON:
        case TCIXOFF:
        case TCIXON:
            //TODO - all the extra flow states



        default:
            status = EIO;
            break;
        }

        break;
    case TCSENDNOW:
        status = OK;
#if NEW_DMA
	if (pChan->openmode & O_DMA) {
	    status = ENOSYS;
	    break;
	}
#endif
        c = (char)arg;
        
        _DISAB_INTS (prevInterruptState, prevFIQState);
        /* waiting for XMITR_RDY */
        while (!(XMITR_RDY_IRQ & (SER_REG (pChan->serChannel, STAT_REG))));

        /* send out char */
        SER_REG_BYTE(pChan->serChannel, DATA_REG) = c;  
        _RESTOR_INT (prevInterruptState, prevFIQState);
        
        serl_cb_event(pChan->serChannel, TXI_EVENT);
        break;
    case TCGETCOUNTERS:
        status = OK;

        cntrs = (struct serial_cntr_t*)arg;
        memcpy(cntrs, &serial_counters[pChan->serChannel], 
               sizeof(struct serial_cntr_t));
        break;
    case TCGETSTATS:
        status = OK;
        
        serl_status = (struct serial_status_t*)arg;
        serl_status->mstat = 0;

        opt = SER_REG (pChan->serChannel, STAT_REG);

        if (current_termios[pChan->serChannel].c_xflag & XALTPIN) {
            /* Swap DCD and DSR */
            if (opt & DCD_STATE)
                serl_status->mstat |= MS_DSR;

            if (opt & DSR_STATE)
                serl_status->mstat |= MS_DCD;
        } else {
            if (opt & DCD_STATE)
                serl_status->mstat |= MS_DCD;

            if (opt & DSR_STATE)
                serl_status->mstat |= MS_DSR;
        }

        if (current_termios[pChan->serChannel].c_xflag & XFORCEDCD) {
            serl_status->mstat |= MS_DCD;
        }

        if (opt & RI_STATE)
            serl_status->mstat |= MS_RI;
            
        if (opt & CTS_STATE)
            serl_status->mstat |= MS_CTS;

        opt = SER_REG (pChan->serChannel, CTRL_REGA);

        if (opt & DTR_ON)
            serl_status->mstat |= MS_DTR;

        if (opt & RTS_ON)
            serl_status->mstat |= MS_RTS;

        serl_status->estat = serial_status[pChan->serChannel].estat;

        //memcpy(serl_status, &serial_status[pChan->serChannel], 
        //      sizeof(struct serial_status_t));
        break;
    case TCGETSIGCHANGE:
        status = OK;

        sigchange = (struct sigchange_stats_t *)arg;
        memcpy(sigchange, &serial_sigchange[pChan->serChannel], 
               sizeof(struct sigchange_stats_t));
        break;
    case TCSETSIGNALS:

        opt = SER_REG (pChan->serChannel, CTRL_REGA);
        prev = 0;

        if (opt & DTR_ON)
            prev |= MS_DTR;
        
        if (opt & RTS_ON)
            prev |= MS_RTS;

        mout = (unsigned char)arg;
        opt = (mout & MS_DTR) ? 1 : 0;

        status = onccIoctl(pSioChan, SIO_SET_STATE_DTR, (int)&opt);
        if ( (status == OK) && 
             !(current_termios[pChan->serChannel].c_cflag & CRTS) 
             ) 
        {
            opt = (mout & MS_RTS) ? 1 : 0;
            status = onccIoctl(pSioChan, SIO_SET_STATE_RTS, (int)&opt);
        }

        if ( (prev ^ mout) & MS_DTR ) { /* DTR changed */
            serial_sigchange[pChan->serChannel].dtr_counter++;
            serl_cb_event(pChan->serChannel, DTR_DELTA);

            serial_status[pChan->serChannel].mstat ^= MS_DTR;
        }

        if ( (prev ^ mout) & MS_RTS ) { /* RTS changed */
            serial_sigchange[pChan->serChannel].rts_counter++;
            serl_cb_event(pChan->serChannel, RTS_DELTA);

            serial_status[pChan->serChannel].mstat ^= MS_RTS;
        }

        break;
    case TCGETBUFFERS:
        status = OK;
#if NEW_DMA
	if (pChan->openmode & O_DMA) {
	    status = ENOSYS;
	    break;
	}
#endif
        bufs = (struct serial_buffer_t *)arg;
        bufs->rxbuf = _CB_NBUFFER(&rx_buffer[pChan->serChannel]);
        bufs->txbuf = _CB_NBUFFER(&tx_buffer[pChan->serChannel]);
        break;
    case TCTXDONE:
        status = OK;

        done = (int *)arg;
        *done = SER_REG( pChan->serChannel, STAT_REG) & XMITR_EMPTY ? 1 : 0;
        break;
#if NEED_ECHO
    case SIO_TERM_ECHO:
        // Pass into old ioctl layer
#endif
    case SIO_BAUD_ERROR_SET:
        // Pass into old ioctl layer
    case SIO_BAUD_ERROR_GET:
        // Pass into old ioctl layer
    default:
        assert(0);
        /* Return ENOSYS: unsupported request */
        break;
    }

    return (status);
}

/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccTxStartup()
 ***
 *** Called by:
 ***
 *** Comments: Call interrupt level character output routine and enable
 *** interrupt.  Function returns: OK on success, ENOSYS if the device is
 *** polled-only, or EIO on hardware error.
 ******************************************************************************
 *****************************************************************************/
static int onccTxStartup (SIO_CHAN * pSioChan)
{
    ONCC_CHAN *pChan = (ONCC_CHAN *) pSioChan;

    if (pChan->channelMode == SIO_MODE_INT
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
        || pChan->channelMode == SIO_MODE_FIQ
#endif
        )
    {
        //SER_REG (pChan->serChannel, CTRL_REGA) |= XMITR_RDY_IRQ;
        SER_REG (pChan->serChannel, CTRL_REGA) |= XMITR_HALF_FULL_IRQ;

        /* enable transmitter interrupts */
        NAEnableIsr (pChan->txlevel);
        return (OK);
    }

    return (ENOSYS);
}



/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccIntWr()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments: This routine handles write interrupts from the NCC.
 ******************************************************************************
 *****************************************************************************/
static void onccIntWr (ONCC_CHAN * pChan)
{
    char outChar;
    UINT32 reg;
    int prevInterruptState, status;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
    int bytesToWrite;

    status = SER_REG (pChan->serChannel, STAT_REG);

    if ( (XMITR_RDY_IRQ & status) == 0)
    {
        return;
    }

    /* acknowledge the interrupt */
    _DISAB_INTS (prevInterruptState, prevFIQState);
    SER_REG (pChan->serChannel, STAT_REG) = CLR_ALL_TX_CONDITIONS;

    bytesToWrite = _CB_NBUFFER (&tx_buffer[pChan->serChannel]);
    if (bytesToWrite > 0)
    {
        if ( !_TX_FLOW_CONTROLLED(pChan) ) /* tx_*_flow */
        {
            if ((pChan->opMode & OP_MODE_RTS_TX_ENCAPSULATION) 
                && (pChan->chars2Encapsuate == 0))
            {
                /*  Begin Transmission -- activeate the RTS signal */
                reg = (SER_REG (pChan->serChannel, CTRL_REGA) & (~RTS_ON)) 
                    + pChan->activeRtsState;
                SER_REG (pChan->serChannel, CTRL_REGA) = reg;

                /*  during a transmission - enable the remote loopback */
                SER_REG (pChan->serChannel, CTRL_REGA) |= LOCAL_LOOP;

                /*  set the flag    */
                pChan->opMode |= OP_MODE_RTS_TX_ACTIVE;
            }

            while (bytesToWrite > 0)
            {
                if (XMITR_RDY_IRQ & SER_REG (pChan->serChannel, STAT_REG))
                {
#if 0
                    bytesToWrite--;
                    /* remove a char from transmit queue */
                    _CB_REMOVE (&tx_buffer[pChan->serChannel], &outChar);

                    /* write char to transmitter FIFO */
                    SER_REG_BYTE (pChan->serChannel, DATA_REG) = outChar;
                    serial_counters[pChan->serChannel].tbytes++;

                    SER_REG (pChan->serChannel, STAT_REG) = XMITR_RDY_IRQ;

                    if (pChan->opMode & OP_MODE_RTS_TX_ACTIVE)
                    {
                        /*  This character should be looped back    */
                        pChan->chars2Encapsuate++;
                    }
#else
                    CBUFFER *q;
                    int tosend;
                    int sent = 0;

                    q = &tx_buffer[pChan->serChannel];
                    tosend = q->count;
                    if (tosend > 4) tosend = 4;
                    switch(tosend) {
                    case 4:
                        //Can do in one op?
                        if ( (((int)&q->data[q->fp] & 0x3) == 0) && //Aligned?
                             (q->fp + 4 <= sizeof(q->data)) ) 
                        {
                            UINT32 data = *(UINT32*)(&q->data[q->fp]);
                            q->fp += 4;
                            if (q->fp > SIZE_OF_BUF) {
                                q->fp = 0;
                            }
                            q->count -= 4;

                            SER_REG(pChan->serChannel, DATA_REG) = data;
                            sent = 4;
                            break;
                        }
                        //else fall through
                    case 2:
                        //Can do in one op?
                        if ( (((int)&q->data[q->fp] & 0x1) == 0) && //Aligned?
                             (q->fp + 2 <= sizeof(q->data)) ) 
                        {
                            UINT16 data = *(UINT16*)(&q->data[q->fp]);
                            q->fp += 2;
                            if (q->fp > SIZE_OF_BUF) {
                                q->fp = 0;
                            }
                            q->count -= 2;

                            SER_REG_SHORT(pChan->serChannel, DATA_REG) = data;
                            sent = 2;
                            break;

                        }
                        //else fall through
                    case 3: /* Send one, next round will use 2 above */
                    case 1:
                        /* remove a char from transmit queue */
                        _CB_REMOVE (&tx_buffer[pChan->serChannel], &outChar);
                        
                        /* write char to transmitter FIFO */
                        SER_REG_BYTE (pChan->serChannel, DATA_REG) = outChar;
                        sent = 1;
                        break;
                    default:
                        break;
                    }
                    serial_counters[pChan->serChannel].tbytes += sent;
                    bytesToWrite -= sent;
                    
                    if (pChan->opMode & OP_MODE_RTS_TX_ACTIVE)
                    {
                                /*  This character should be looped back    */
                        pChan->chars2Encapsuate += sent;
                    }
                    SER_REG (pChan->serChannel, STAT_REG) 
                        = XMITR_RDY_IRQ | XMITR_HALF_FULL_IRQ;
#endif
                }
                else
                {
                    break;
                }
            }
        }
    }

    _RESTOR_INT (prevInterruptState, prevFIQState); 

    if ((_CB_NBUFFER (&tx_buffer[pChan->serChannel]) == 0) 
        || _TX_FLOW_CONTROLLED(pChan) ) 
    {
        NADisableIsr (pChan->txlevel);

        /* disable transmitter interrupt */
        _DISAB_INTS (prevInterruptState, prevFIQState);
        reg = SER_REG (pChan->serChannel, CTRL_REGA) & 
            ~(XMITR_RDY_IRQ | XMITR_HALF_FULL_IRQ);

        SER_REG (pChan->serChannel, CTRL_REGA) = reg;
        _RESTOR_INT (prevInterruptState, prevFIQState); 
    }
}


/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccIntRd()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments: This routine handles read interrupts from the NCC.  
 ******************************************************************************
 *****************************************************************************/
static void onccIntRd (ONCC_CHAN * pChan)
{
    UINT32 reg, regSoft, ack = 0;
    UINT32 inchar;
    UINT8  *ptr;
    int nread, i
        , flowdiff = 0;
    UINT8 skip_ch;
    int prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
    int linecount = 0;
    int datalinecount = 0;

    _DISAB_INTS (prevInterruptState, prevFIQState);

    if (_CB_NBUFFER(&rx_buffer[pChan->serChannel]) 
        > rx_buf_maxcount1[pChan->serChannel] ) 
    {
        rx_buf_maxcount1[pChan->serChannel] = 
            _CB_NBUFFER(&rx_buffer[pChan->serChannel]);
    }


    regSoft = (SER_REG(pChan->serChannel, STAT_REG));

    //else
    {
        /* We have buffer space for receiving */
        reg =  (SER_REG(pChan->serChannel, STAT_REG));

#if 1
        if (reg & RECV_OVRRUN_ERR)
        {
            ack |= RECV_OVRRUN_ERR;
            serial_counters[pChan->serChannel].norun++;
        }
        if (reg & RECV_FRAME_ERR)
        {
            ack |= RECV_FRAME_ERR;
            serial_counters[pChan->serChannel].nframe++;
        }
        if (reg & RECV_PARITY_ERR)
        {
            ack |= RECV_PARITY_ERR;
            serial_counters[pChan->serChannel].nparity++;
        }
        if (ack) {
            SER_REG(pChan->serChannel, STAT_REG) = ack;
            ack = 0;
        }
#endif

        if ((!(reg & RECV_RDY)) 
            && ((reg & RECV_HALF_FULL)|(reg & RECV_CLOSED)))
        {
            SER_REG(pChan->serChannel, STAT_REG) = RECV_CLOSED;   
#ifdef DEBUG_SERIAL
            rbcc++;
#endif
            reg =  (SER_REG(pChan->serChannel, STAT_REG));
        }

        do
        {
            SER_REG(pChan->serChannel, STAT_REG) = RECV_CLOSED;

            reg = SER_REG(pChan->serChannel, STAT_REG);

            linecount++;
            nread = 0;
            skip_ch = 0;
            if ((reg & RECV_RDY) || (reg & RECV_HALF_FULL))
            {
                nread = (SER_REG(pChan->serChannel, STAT_REG) 
                         & NUM_DATA_BYTES) >> 20;
                if (nread == 0)
                {
                    nread = 4;   //RXFDB returns 0 when 4 bytes are available
                }

                // Signal a RX_DATA EVENT
                rx_note[pChan->serChannel]++;

                datalinecount++;
            }

            if ((pChan->sw_flowc) && ((MATCH1_TRUE | MATCH2_TRUE) & (regSoft)))
            {
                /* probably never use this line hardware bug */
                nread = 1;
            }

            if (nread)
                inchar = SER_REG(pChan->serChannel, DATA_REG);
            ptr = (UINT8*) &inchar;

            nreads[pChan->serChannel][nread]++;

            serial_counters[pChan->serChannel].rbytes += nread;


            for (i = 0; i < nread; i++)
            {
                if (pChan->sw_flowc)
                {
                    skip_ch = 0;
                    /* skip all xon and xoff if its  */
                    /* under software flow control */
                    if (*(ptr+i) == pChan->xoff_ch) 
                    {                               
                        //pChan->flow_controled= TRUE;
                        pChan->tx_sw_flow = TRUE;
                        serial_status[pChan->serChannel].estat |= EV_OPS;
                        serl_cb_event(pChan->serChannel, OPS_EVENT);

                        /* clear the Match reg */
                        SER_REG (pChan->serChannel, STAT_REG) 
                            = (MATCH1_TRUE | MATCH2_TRUE);  
                        skip_ch = 1;
                    }
                    else if (*(ptr+i) == pChan->xon_ch)
                    {
                        //pChan->flow_controled= FALSE;
                        pChan->tx_sw_flow = FALSE;
                        serial_status[pChan->serChannel].estat &= ~EV_OPS;
                        serl_cb_event(pChan->serChannel, OPS_EVENT);

                        SER_REG (pChan->serChannel, STAT_REG) 
                            = (MATCH1_TRUE | MATCH2_TRUE);
                        skip_ch = 2;
                    }
                }

                if ( ((current_termios[pChan->serChannel].c_iflag 
                       & (PARMRK | IGNPAR | ISTRIP) ) == (PARMRK) )
                     && ( *(ptr + i) == 0xff )
                     && (!_CB_FULL(&rx_buffer[pChan->serChannel]) ) )
                {
                    _CB_ADD(&rx_buffer[pChan->serChannel], 0xff);
                }

                if ((pChan->opMode & OP_MODE_RTS_TX_ENCAPSULATION)
                    && (pChan->opMode & OP_MODE_RTS_TX_ACTIVE))
                {
                    /*  in this mode we are just digesting data */
                    pChan->chars2Encapsuate--;

                    if (pChan->chars2Encapsuate == 0)
                    {
                        /*  Complete Transmission - deactivate RTS signal */
                        reg = (SER_REG (pChan->serChannel, CTRL_REGA) 
                               & (~RTS_ON)) + pChan->inactiveRtsState;
                        SER_REG (pChan->serChannel, CTRL_REGA) = reg;

                        /*  clear the flag    */
                        pChan->opMode &= ~OP_MODE_RTS_TX_ACTIVE;

                        /*  during  transmission - enable remote loopback */
                        SER_REG (pChan->serChannel, CTRL_REGA) &= ~LOCAL_LOOP;
                    }
                }
                else if ( (!_CB_FULL (&rx_buffer[pChan->serChannel])) && 
                          (!skip_ch) 
                          )
                {
                    _CB_ADD(&rx_buffer[pChan->serChannel], *(ptr+i));
                }
                else if (!skip_ch) /* skips should not cause an overflow */
                {
                    pChan->rx_overflow++; /* just gather some statistics */
                    serial_counters[pChan->serChannel].noflow++;
                }

            }       /* End of for */

            SER_REG(pChan->serChannel, STAT_REG) = CLR_ALL_RX_CONDITIONS;    
        } while (SER_REG(pChan->serChannel, STAT_REG) 
                 & (RECV_RDY | RECV_CLOSED | RECV_HALF_FULL | RECV_FULL ));

    }

    if (_CB_NBUFFER(&rx_buffer[pChan->serChannel])  
        > rx_buf_maxcount2[pChan->serChannel] ) 
    {
        rx_buf_maxcount2[pChan->serChannel] = 
            _CB_NBUFFER(&rx_buffer[pChan->serChannel]);
    }

    /*  when buffer almost full and hardware interrupt used, disable int. */
    if ((pChan->hw_flowc) && (_CB_NBUFFER(&rx_buffer[pChan->serChannel]) 
                              > (SIZE_OF_BUF-256)))
    {
#ifdef DEBUG_SERIAL
        printf("Disable int \n");
#endif

        //pChan->flow_controled= TRUE;
        pChan->rx_hw_flow = TRUE; 
        //serial_status[pChan->serChannel].estat |= EV_IPH;
        //serl_cb_event(pChan->serChannel, IPH_EVENT);
        iph_up_ctr[pChan->serChannel]++;

        flowdiff = 1;
    }


    i= _CB_NBUFFER(&rx_buffer[pChan->serChannel]);

    if (( pChan->sw_flowc) &&(i > (SIZE_OF_BUF-SW_BUF_THRESH_HOLD)) )
    {
        /* too much in the buffer */
        pChan->rx_sw_flow = TRUE;  
        serial_status[pChan->serChannel].estat |= EV_IPS;
        serl_cb_event(pChan->serChannel, IPS_EVENT);

        flowdiff = 1;
    }

    if (flowdiff) {
        change_rx_flow(pChan);
    }

    if ( ( pChan->sw_flowc)&& (skip_ch ==2))
    {
        //SER_REG (pChan->serChannel, CTRL_REGA) |= (XMITR_RDY_IRQ);
        SER_REG (pChan->serChannel, CTRL_REGA) |= (XMITR_HALF_FULL_IRQ);
        NAEnableIsr (pChan->txlevel);
#ifdef DEBUG_SERIAL
        xonc++;
#endif
        skip_ch = 0;
    }


    lines[pChan->serChannel][linecount]++;
    datalines[pChan->serChannel][datalinecount]++;

    if (linecount > 8) {
        morelines[pChan->serChannel]++;
    }

    _RESTOR_INT (prevInterruptState, prevFIQState); 
}

/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccSioDevInit()
 ***
 *** Called by:
 ***
 *** Comments: This routine initializeS some SIO_CHAN function pointers and
 *** then resets the serial port in a quiescent state.  Before this routine is
 *** called, the BSP must already have initialized all the device addresses,
 *** etc. in the ONCC_CHAN structure.  (Reference WRS template & example.)
 ******************************************************************************
 *****************************************************************************/
static void onccSioDevInit (ONCC_CHAN * pChan)
{
    int prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
    
    _DISAB_INTS (prevInterruptState, prevFIQState);

    //pChan->channelMode = SIO_MODE_INT;            /* undefined */
    onccInitChannel (pChan);

    _RESTOR_INT (prevInterruptState, prevFIQState);
}

#if REALLY_OLD_DMA
/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccDmaRd7()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments: This routine handles Dma read interrupts from the NCC.
 ***           Its almost idential to onccDmaRd7.
 ******************************************************************************
 *****************************************************************************/
static void onccDmaRd7 (void)
{
    const int channel = 0;
    int bytesinbuf;
    int last_DMA_bd_count;

    *NARM_DSIER7_ADDR = 0xf0d00000;
    last_DMA_bd_count       = ser_DMA_bd_count[channel];

    while (ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.full)
    {
        bytesinbuf = ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.buflen;
        if (bytesinbuf)
        {
            /* save the count to the end of m_qhead array   */
            m_qhead[channel][m_qhead_tail[channel]].bd_id = 
                ser_DMA_bd_count[channel];  

            m_qhead[channel][m_qhead_tail[channel]].count = bytesinbuf;
            m_qhead_tail[channel]++;  
            if (m_qhead_tail[channel] == M_QHEAD_SIZE)
            {
                m_qhead_tail[channel] = 0;
            }
        }
        else
        {
            bytesinbuf = ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.buflen = SIZE_OF_DMA_BUF;
            ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.status  = 0;
            ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.full  = 0;
        }

        /* keep track of the current DMA buffer descriptor */
        ser_DMA_bd_count[channel]++;  
        if ( ser_DMA_bd_count[channel] == NUM_DMA_RX_BUF)
        {
            ser_DMA_bd_count[channel] = 0;
        }

        if (ser_DMA_bd_count[channel] == last_DMA_bd_count)
        {
            break;
        }
    }

    return;
}



/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccDmaRd9()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments: This routine handles Dma read interrupts from the NCC.
 ******************************************************************************
 *****************************************************************************/
static void onccDmaRd9 (void) /* interrupt function for DMA channel 9, com1. */
{
    const int channel = 1;
    int bytesinbuf;
    int last_DMA_bd_count;

    *NARM_DSIER9_ADDR |= 0xf0d00000;
    last_DMA_bd_count = ser_DMA_bd_count[channel];
    while(ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.full )  /* check for data */
    {
        bytesinbuf = ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.buflen;
        if (bytesinbuf)
        {
            m_qhead[channel][m_qhead_tail[channel]].bd_id = 
                ser_DMA_bd_count[channel];

            m_qhead[channel][m_qhead_tail[channel]].count = bytesinbuf;
            m_qhead_tail[channel]++;
            if (m_qhead_tail[channel] == M_QHEAD_SIZE)
            {
                m_qhead_tail[channel] = 0;
            }
        }
        else
        {
            bytesinbuf = ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.buflen = SIZE_OF_DMA_BUF;
            ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.status = 0;
            ser_rx_buffer_des[channel][ser_DMA_bd_count[channel]].upper_word.bits.full = 0;
        }
        
        ser_DMA_bd_count[channel]++;
        if ( ser_DMA_bd_count[channel] == NUM_DMA_RX_BUF)
        {
            ser_DMA_bd_count[channel] = 0;
        }

        if (ser_DMA_bd_count[channel] == last_DMA_bd_count)
        {
            break;
        }
    }
}




/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  onccDmaWr()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments: This routine handles Dma write interrupts from the NCC.
 ******************************************************************************
 *****************************************************************************/
static void onccDmaWr8 (void)
{
    if (*NARM_DSIER8_ADDR & 0x80000000)
    {
        *NARM_DSIER8_ADDR |= 0xf0000000;
    }
}


static void onccDmaWr10 (void)
{
    if (*NARM_DSIER10_ADDR & 0x80000000)
    {
        *NARM_DSIER10_ADDR |= 0xf0000000;
    }
}
#endif /* REALLY_OLD_DMA */



/******************************************************************************
 ******************************************************************************
 ***
 *** Function Name:  sysSerialChanGet()
 ***
 *** Called by:
 *** Function Calls: None
 ***
 *** Comments:
 *** routine returns a pointer to the SIO_CHAN device associated with a
 *** specified serial channel.  It is called by usrRoot() to obtain pointers
 *** when creating the system serial devices '/tyCo/x'.  It is also used by the
 *** WDB agent to locate its serial channel.
 ******************************************************************************
 *****************************************************************************/

static ONCC_CHAN *sysSerialChanGet (int channel)
{
    int i;

    for (i = 0; i < indexOfAvailableOnccStructures; i++)
    {
        if (onccChan[i].port == channel)
        {
            return &onccChan[i];
        }
    }

    return NULL;
}



/* global device table, defined in ind_os.c */
static unsigned RWOPTIONS = (O_RDONLY|O_WRONLY|O_RDWR
#if REALLY_OLD_DMA || NEW_DMA /* Will need to keep */
                             |O_DMA
#endif
                             );



#if REALLY_OLD_DMA
/*
 *  Function: ser_init_dma_bd
 *
 *  Description: initialized dma buffer descriptor
 *
 *
 *  called by:
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
static int ser_init_dma_bd(int channel)  /* set up the dma buffer descriptor */
{
    int i;
    ONCC_CHAN  *pChannel;

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }

    if (pChannel->opened)  /* no need to open if its already open */
    {
        return EBUSY;
    }

    if ((pChannel->openmode & RWOPTIONS) == 0)
    {
        return EINVAL;
    }

    if ((pChannel->openmode & O_RDONLY) || (pChannel->openmode & O_RDWR))
    {
        for ( i = 0; i < NUM_DMA_RX_BUF; i++)
        {
            /* The following allocate 2 buffer descriptors per channel - 0/1 */
            ser_rx_buffer_des[pChannel->serChannel][i].lower_word.bits.wrap 
                = 0;
            ser_rx_buffer_des[pChannel->serChannel][i].lower_word.bits.intr 
                = 0;
            ser_rx_buffer_des[pChannel->serChannel][i].lower_word.bits.last 
                = 0;
            ser_rx_buffer_des[pChannel->serChannel][i].lower_word.bits.sbp =  
                (unsigned long )&dma_ser_rx_buf[pChannel->serChannel][i][0];
            ser_rx_buffer_des[pChannel->serChannel][i].upper_word.bits.status 
                = 0;
            ser_rx_buffer_des[pChannel->serChannel][i].upper_word.bits.full 
                = 0;
            ser_rx_buffer_des[pChannel->serChannel][i].upper_word.bits.buflen 
                = (int)SIZE_OF_DMA_BUF;
        }

        ser_rx_buffer_des[pChannel->serChannel][i-1].lower_word.bits.wrap = 1;
    }

    /*  set up the ser_tx_buffer_des  */
    if ((pChannel->openmode & O_WRONLY) || (pChannel->openmode & O_RDWR))
    {
        ser_tx_buffer_des[pChannel->serChannel].lower_word.bits.wrap = 1;
        ser_tx_buffer_des[pChannel->serChannel].lower_word.bits.intr = 0;
        ser_tx_buffer_des[pChannel->serChannel].lower_word.bits.last = 1;
        ser_tx_buffer_des[pChannel->serChannel].lower_word.bits.sbp = 
            (unsigned long)&tx_buffer[pChannel->serChannel].data[0];
        ser_tx_buffer_des[pChannel->serChannel].upper_word.bits.status = 0;
        ser_tx_buffer_des[pChannel->serChannel].upper_word.bits.full = 0;
        ser_tx_buffer_des[pChannel->serChannel].upper_word.bits.buflen = 
            SIZE_OF_DMA_BUF;
    }

    ser_DMA_bd_count[pChannel->serChannel] = 0;

    /*  now assign buffer descriptor to dma control register    */
    if (pChannel->serChannel == 0)
    {
        *NARM_DCR7_ADDR  = 0;
        *NARM_DBDP7_ADDR = 
            (unsigned long)ser_rx_buffer_des[pChannel->serChannel];

        /* Clear int. pend. and enable DMA interrupts */
        *NARM_DSIER7_ADDR  = 0xf0d00000;  
        *NARM_DCR8_ADDR  = 0;

        *NARM_DBDP8_ADDR 
            = (unsigned long)&ser_tx_buffer_des[pChannel->serChannel];

        /* Clear interrupt pending and enable DMA interrupts */
        *NARM_DSIER8_ADDR  = 0xf0d00000;   

        for ( i = 0; i < M_QHEAD_SIZE; i++)
        {
            m_qhead[pChannel->serChannel][i].count = 0;
            m_qhead[pChannel->serChannel][i].bd_id = 0;
        }
        m_qhead_head[pChannel->serChannel] = 0;
        m_qhead_tail[pChannel->serChannel] = 0;
    }
    else
    {
        *NARM_DCR9_ADDR  = 0;
        *NARM_DBDP9_ADDR 
            = (unsigned long)ser_rx_buffer_des[pChannel->serChannel];

        /* Clear interrupt pending and enable DMA interrupts */
        *NARM_DSIER9_ADDR  = 0xf0d00000;  

        *NARM_DCR10_ADDR  = 0;

        *NARM_DBDP10_ADDR 
            = (unsigned long)&ser_tx_buffer_des[pChannel->serChannel];

        /* Clear interrupt pending and enable DMA interrupts */
        *NARM_DSIER10_ADDR  = 0xf0d00000;   
        
        for ( i = 0; i < M_QHEAD_SIZE; i++)
        {
            m_qhead[pChannel->serChannel][i].count = 0;
            m_qhead[pChannel->serChannel][i].bd_id = 0;
        }
        m_qhead_head[pChannel->serChannel] = 0;
        m_qhead_tail[pChannel->serChannel] = 0;
    }

    return 0;
}

/*
 *  Function: ser_enable_dma
 *
 *  Description: enable serial dma interrupts
 *
 *
 *  called by:
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *      option       read, write or RW
 *  Return Values:
 *              none
 *
 */

static void ser_enable_dma(int channel, int option)  
{
    int reg;

    if (channel == 0)
    {
        switch ( option)
        {
        case O_RDONLY :
            reg = (*NARM_DCR7_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR7_ADDR = reg | FLY_BY_WRITE | DMA_CHANNEL_ENABLE;
            break;

        case O_WRONLY :
            reg = (*NARM_DCR8_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR8_ADDR = reg | FLY_BY_READ | DMA_CHANNEL_ENABLE;
            break;

        case O_RDWR :
            reg = (*NARM_DCR7_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR7_ADDR = reg | FLY_BY_WRITE | DMA_CHANNEL_ENABLE;
            reg = (*NARM_DCR8_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR8_ADDR = reg | FLY_BY_READ | DMA_CHANNEL_ENABLE;
            break;
        }
    }
    else
    {
        switch ( option)
        {
        case O_RDONLY :
            reg = (*NARM_DCR9_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR9_ADDR = reg | FLY_BY_WRITE | DMA_CHANNEL_ENABLE;
            break;

        case O_WRONLY :
            reg = (*NARM_DCR10_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR10_ADDR = reg | FLY_BY_READ | DMA_CHANNEL_ENABLE;
            break;

        case O_RDWR :
            reg = (*NARM_DCR9_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR9_ADDR = reg | FLY_BY_WRITE | DMA_CHANNEL_ENABLE;
            reg = (*NARM_DCR10_ADDR) 
                & ~(BURST_BANDWIDTH_MASK | BUS_BANDWIDTH_MASK);
            *NARM_DCR10_ADDR = reg | FLY_BY_READ | DMA_CHANNEL_ENABLE;
            break;
        }
    }
}

/*
 *  Function: ser_dma_write
 *
 *  Description: puts data in dma buffer descriptor
 *
 *
 *  called by:  netos_serlwrite
 *
 *  Parameter:
 *
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *      buffer       data to be sent
 *      length       size of data
 *      bytesWritten store actual size being sent

 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */

static int ser_dma_write(int channel, void * buffer, int length, 
                         int* bytesWritten)  
{
    int byteToWrite = 0;
    const int MAX_DMA_BUFFER_SIZE = 32700;   /* max DMA buffer size */

    /* check if buffer is full */
    if (ser_tx_buffer_des[channel].upper_word.bits.full == 0)  
    {
        if ( length > MAX_DMA_BUFFER_SIZE) 
        {
            byteToWrite = MAX_DMA_BUFFER_SIZE;
        }
        else
        {
            byteToWrite = length;
        }

        ser_tx_buffer_des[channel].lower_word.bits.sbp = (unsigned long)buffer;
        /* we are using the tx_buffer */
        /* as DMA transmit buffer to save space */
        ser_tx_buffer_des[channel].upper_word.bits.buflen   = byteToWrite;  
        ser_tx_buffer_des[channel].upper_word.bits.full     = 1;            
        SER_REG(channel, CTRL_REGA)                        |= XMITR_DMA_ON;
    }
    *bytesWritten = byteToWrite;  /* bytesWritten has number of type sent */

    ser_enable_dma(channel,O_RDWR);  /* enable write channel */
    return 0;
}
#endif /* REALLY_OLD_DMA */


/*
 *  Function: netos_serl_enter
 *
 *  Description: First level initialization. Called before
 *               kernel starts.
 *
 *  called by: ANSI layer _enter() (DDIFirstLevelInitalize())
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */

int netos_serlenter (int channel)
{
    int idx;

    if (indexOfAvailableOnccStructures < NUM_NCC_SER_CHAN)
    {
        /*  get the index of the next serial channel available 
            - increment for the next time   */
        idx                       = indexOfAvailableOnccStructures++;

        onccChan[idx].rxlevel     = devParas[channel].rxintLevel;
        onccChan[idx].txlevel     = devParas[channel].txintLevel;
#if REALLY_OLD_DMA
        onccChan[idx].rxDmalevel  = devDmaParas[channel].rxintLevel;
        onccChan[idx].txDmalevel  = devDmaParas[channel].txintLevel;
#endif

        /* Eventually, this will come from non-volatile storage */
        onccChan[idx].baudRate    = DEFAULT_BAUD_RATE;
        onccChan[idx].options     = CONSOLE_OPTIONS;

        /* System port address for this serial channel */
        onccChan[idx].port        = channel;
        onccChan[idx].serChannel  = channel;


        /*  initialize UART */
        onccSioDevInit (&onccChan[idx]);
        return EXIT_SUCCESS;
    }

    return EINVAL;
}

static void 
serl_monitor(unsigned long dummy) 
{
    ONCC_CHAN *pChan;
    SIO_CHAN *pSioChan;
    struct serial_status_t status;
    int i =  dummy; // Just to use dummy

    while (1) {
        for (i = 0 ; i < NUM_NCC_SER_CHAN ; i++) {
            unsigned char mprev;
            unsigned char mdiff;

            pChan = &onccChan[i];
            pSioChan = (SIO_CHAN*)pChan;

            mprev = serial_status[pChan->serChannel].mstat;

            digiIoctl(pSioChan, TCGETSTATS, (int)&status);

            mdiff = mprev ^ status.mstat;

            if (mdiff & MS_DSR) {
                serial_sigchange[pChan->serChannel].dsr_counter++;
                serl_cb_event(pChan->serChannel, DSR_DELTA);
            }
            if (mdiff & MS_DCD) {
                serial_sigchange[pChan->serChannel].dcd_counter++;
                serl_cb_event(pChan->serChannel, DCD_DELTA);
            }
            if (mdiff & MS_RI) {
                serial_sigchange[pChan->serChannel].ri_counter++;
                serl_cb_event(pChan->serChannel, RI_DELTA);
            }
            if (mdiff & MS_CTS) {
                serial_sigchange[pChan->serChannel].cts_counter++;
                serl_cb_event(pChan->serChannel, CTS_DELTA);
            }
            if (mdiff & MS_RTS) {
                serial_sigchange[pChan->serChannel].rts_counter++;
                serl_cb_event(pChan->serChannel, RTS_DELTA);
            }
            if (mdiff & MS_DTR) {
                serial_sigchange[pChan->serChannel].dtr_counter++;
                serl_cb_event(pChan->serChannel, DTR_DELTA);
            }

            if (rx_note[pChan->serChannel] != rx_noted[pChan->serChannel]) {
                rx_noted[pChan->serChannel] = rx_note[pChan->serChannel];
                serl_cb_event(pChan->serChannel, RX_DATA);
            }
        
            serial_status[pChan->serChannel].mstat = status.mstat;

        }
        tx_thread_sleep(4); /* 4ms should be good enough */
    }
}


#if 1 // No automatic allocation from threadx
#define MONITOR_STACK_SIZE 0x800
TX_THREAD serl_monitor_thread;
char serl_monitor_stack[MONITOR_STACK_SIZE];    
#endif

/*
 *  Function: netos_serl_init
 *
 *  Description: Second level initialization, after kernel is loaded.
 *               Complete any initialization needed by the driver.
 *
 *  called by: DDISecondLevelInitialize()
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
int netos_serlinit (int channel)
{
    static int thread_started = 0;
    ONCC_CHAN *pChannel;
    struct termios tios;

    pChannel = (ONCC_CHAN  *) sysSerialChanGet (channel);
    if (pChannel != NULL)
    {
        /*  install receive interrupt function  */
        NAInstallIsr (devParas[pChannel->serChannel].rxvector,
                      (NA_ISR_HANDLER)onccIntRd, 
                      (void*) pChannel);

        /*  install send interrupt function */
        NAInstallIsr (devParas[pChannel->serChannel].txvector, 
                      (NA_ISR_HANDLER) onccIntWr, 
                      (void*) pChannel);

        onccIoctl ((SIO_CHAN*)pChannel, SIO_MODE_SET, SIO_MODE_INT);

        memset( &tios, 0, sizeof(tios) );

        tios.c_iflag = IXON | IXOFF;
        tios.c_oflag = 0 ;
        tios.c_cflag = CS8 | CREAD ;
        tios.c_lflag = 0;
        tios.c_cc[VSTART] = XON;
        tios.c_cc[VSTOP] = XOFF;
        tios.baud = 9600 ;

        _CB_RESET (&rx_buffer[pChannel->serChannel]);      
        _CB_RESET (&tx_buffer[pChannel->serChannel]); 

        digiIoctl( (SIO_CHAN*)pChannel, TCSETA, (int) &tios );

        /* Start up monitoring thread, for signal deltas etc. */
        /* Only one thread for all ports, serl_monitor() */
        if (!thread_started) {
            
            tx_thread_create(
                             &serl_monitor_thread,
                             "Serial Monitor",
                             serl_monitor,
                             0,         //dummy
                             &serl_monitor_stack,
                             MONITOR_STACK_SIZE,        // stack size
                             15,        // arbitrary priority
                             15,        // arbitrary threshold
                             10,        // preemption from inetd_epoch
                             TX_AUTO_START );
            
            thread_started = 1;
        }

        return (EXIT_SUCCESS);
    }

    return EBADF;
}

#if REALLY_OLD_DMA
/*
 *  Function: netos_serl_DMA_init
 *
 *  Description: Serial DMA Channal initialization.
 *
 *  called by: netos_serlopen
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
static int netos_serl_DMA_init (int channel)
{
    ONCC_CHAN *pChannel;

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }
 
    if (pChannel->serChannel == 0)
    {
        /*  install receive interrupt function */
        NAInstallIsr (devDmaParas[0].rxvector, 
                      (NA_ISR_HANDLER)onccDmaRd7, 
                      (void*) SER1RX_DMA_LVL);

        /*  install send interrupt function */
        NAInstallIsr (devDmaParas[0].txvector, 
                      (NA_ISR_HANDLER)onccDmaWr8, 
                      (void*) SER1TX_DMA_LVL);  
    }
    else
    {
        /*  install receive interrupt function */
        NAInstallIsr (devDmaParas[1].rxvector, 
                      (NA_ISR_HANDLER)onccDmaRd9, 
                      (void*) SER2RX_DMA_LVL);

        /*  install send interrupt function */
        NAInstallIsr (devDmaParas[1].txvector, 
                      (NA_ISR_HANDLER)onccDmaWr10, 
                      (void*)SER2TX_DMA_LVL);  
    }
    return EXIT_SUCCESS;
}
#endif /* REALLY_OLD_DMA */

/*
 *  Function: netos_serlopen()
 *
 *  Description: open a new session on a channel
 *
 *  called by: open (), creat () in DDI
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *      flags        a bit field formed by orring one or moew
 *                   of O_RDONLY, O_WRONLY, O_RDWR, O_NONBLOCK
 *
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */

int netos_serlopen (int channel, unsigned flags)
{
    ONCC_CHAN  *pChannel;
    unsigned rwmode;
    UINT32 reg;
    //int ret;
    int openflowed = 0;

    /* REALLY_OLD_DMA: the flags line below WILL change */
#if 0
    flags &= ~O_DMA; /* This driver ignores O_DMA */
#endif
    rwmode = (flags & RWOPTIONS);

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }


    if (pChannel->opened) /* no need to open if its already open */
    {
        return EBUSY;
    }

#if 1 // Flags are different depending on build environment
    if (! (
           (rwmode == (O_RDONLY | O_DMA)) 
           || (rwmode == (O_RDWR | O_DMA)) 
           || (rwmode == (O_WRONLY | O_DMA)) 
           || (rwmode == O_RDONLY)
           || (rwmode == O_RDWR) 
           || (rwmode == O_WRONLY)
           ) )
    {
        return EINVAL;
    }
#else
    if ((rwmode == (O_RDONLY|O_WRONLY)) ||
        (rwmode == (O_RDONLY|O_RDWR)) ||
        (rwmode == (O_WRONLY|O_RDWR)))
    {
        return EINVAL;
    }
#endif

    /*  Lock serial channel resource, so HDLC can't open a channel */
    if (!GetResource(&serl_resource_lock[pChannel->serChannel], pChannel))
    {
        return EACCES;    /* HDLC channel open */
    }

#if NEW_DMA
    if (rwmode & O_DMA) {
	prevmode[pChannel->serChannel] = pChannel->channelMode;
        onccIoctl ((SIO_CHAN*)pChannel, 
		   SIO_MODE_SET, 
		   SIO_MODE_DMA);
    }
    else 
#endif
#if REALLY_OLD_DMA
    /* Turn on rx interrupt unless it is write only */
    if (rwmode & O_DMA)             /* if it is in DMA mode */
    {
        return EINVAL; /* This driver doesn't support DMA mode */
    }
    else
#endif
    {
        if (rwmode != O_WRONLY)
        {
            //onccIoctl ((SIO_CHAN*) pChannel, SIO_MODE_SET, SIO_MODE_INT);
            //Keep the mode we're configured for
        }
    }

    pChannel->opened         = 1;        /* Updated the opened flag */
    pChannel->openmode       = flags;    /* updated the opened mode */
#if NEED_ECHO
    /* initialized the echo, sendcr and overflow flag */
    pChannel->echo           = 0;        
#endif
    //pChannel->opMode         = 0;
    pChannel->rx_overflow    = 0;

    /* No flow control statuses active on a port open */

    if (pChannel->rx_hw_flow || pChannel->rx_sw_flow) {
        openflowed = 1;
    }

    pChannel->tx_hw_flow = FALSE;
    pChannel->rx_hw_flow = FALSE;
    pChannel->tx_sw_flow = FALSE;
    pChannel->rx_sw_flow = FALSE;
    pChannel->tx_user_flow = FALSE;
    pChannel->rx_user_flow = FALSE;
    pChannel->rx_alarm_flow = FALSE;

    /* Start a session with RTS and DTR on to match certain behaviors with 
     * the old driver */
    reg = SER_REG(pChannel->serChannel, CTRL_REGA);
    reg |= RTS_ON | DTR_ON;
    SER_REG (pChannel->serChannel, CTRL_REGA) = reg;

#if NEW_DMA
    if ( !(rwmode & O_DMA) ) 
#endif
    {
        _CB_RESET (&rx_buffer[pChannel->serChannel]);
        _CB_RESET (&tx_buffer[pChannel->serChannel]);
        if (openflowed)
            change_rx_flow(pChannel);
    }

    /* create semaphore here */
    if (rwmode & O_RDWR)
    {
        _OPEN_SEMA(&r_sem[pChannel->serChannel]);
        _OPEN_SEMA(&w_sem[pChannel->serChannel]);
    }
    else if (rwmode & O_RDONLY)
    {
        _OPEN_SEMA(&r_sem[pChannel->serChannel]);
    }
    else if (rwmode & O_WRONLY)
    {
        _OPEN_SEMA(&w_sem[pChannel->serChannel]);
    }

    return EXIT_SUCCESS;
}

/*
 *  Function: netos_serlclose()
 *
 *  Description: close session on a channel
 *
 *  called by: close () in DDI
 *
 *  Parameter:
 *
 *      channel      channel number as set in the channel's
 *                   device table entry
 *  Return Values:
 *
 *      EXIT_SUCCESS
 *      EINVAL
 *      EBUSY
 */
int netos_serlclose (int channel)
{
    unsigned rwmode;
    ONCC_CHAN  *pChannel;

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }

    if (!pChannel->opened) /* check to see if device is already closed */
    {
        return EBADF;
    }

    rwmode = pChannel->openmode & RWOPTIONS; /* reads the device mode */

#if NEW_DMA
    if (pChannel->openmode & O_DMA) {
        onccIoctl ((SIO_CHAN*)pChannel, 
		   SIO_MODE_SET, 
		   prevmode[pChannel->serChannel]);
    }
#endif

    if ( (rwmode & O_RDWR) || (rwmode & O_RDONLY) ) {
#if REALLY_OLD_DMA
        /* wait for receiving buffer ready */
        while (m_qhead[pChannel->serChannel]
	              [m_qhead_head[pChannel->serChannel]].count != 0)  
        {
            tx_thread_sleep (1);
        }
#endif

        /* resets receiving buffer to zero */
        while (!_CB_EMPTY(&rx_buffer[pChannel->serChannel]))  
        {
            _CB_RESET (&rx_buffer[pChannel->serChannel]);
            //check_rx_flow(pChannel);

            //TODO: Q? Should the close really be trying to wait for data to 
            //stop arriving?
            tx_thread_sleep (1);
        }

        _GET_SEMA(&r_sem[pChannel->serChannel]);

        /* no pending I/O, destroy semaphores */
        _CLOSE_SEMA(&r_sem[pChannel->serChannel]);
    }

    if ( (rwmode & O_RDWR) || (rwmode & O_WRONLY) ) {
        /* wait until transmitting buffer is empty */
        while (!_CB_EMPTY(&tx_buffer[pChannel->serChannel]))  
        {
            tx_thread_sleep (1);
        }

        _GET_SEMA(&w_sem[pChannel->serChannel]);

        /* no pending I/O, destroy semaphores */
        _CLOSE_SEMA(&w_sem[pChannel->serChannel]);
    }

    ReleaseResource(&serl_resource_lock[pChannel->serChannel], pChannel);
    pChannel->opened    = 0;    /* updates the opened flag */
    pChannel->openmode  = 0;    /* updates the openmode flag */
    pChannel->options   = CONSOLE_OPTIONS;
    
    serl_cb_event(pChannel->serChannel, CLOSE_EVENT);
    
    return EXIT_SUCCESS;
}

/*
 * Function: netos_serlread()
 *
 * Description: read data from driver
 *
 * Called by: DDI read
 *
 * Input : channel - 0 for port com1, 1 for port com2
 *         buffer - data to be stored
 *         length - size of data buffer
 *         bytesWritten - store actual size being sent
 *
 * Return: EBADF if channel does not exist
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */

/* TODO: Check if channel was opened with O_DATA{LOCK} */

int netos_serlread (int channel, void * buffer, int length, int *bytesRead)
{
    ONCC_CHAN  *pChannel;
    int err_code = EXIT_SUCCESS;
    int prevInterruptState;
#if (BSP_SERIAL_FAST_INTERRUPT == TRUE)
    int prevFIQState;
#endif
#if REALLY_OLD_DMA
    int dmaBufferCount = 0;
    UINT32 reg;
#endif

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }

    if (!pChannel->opened)      /* make sure device is opened */
    {
        return EBADF;
    }

    if (pChannel->openmode & O_WRONLY) /* make sure device is not write only */
    {
        return EINVAL;
    }

    *bytesRead = 0;
    _GET_SEMA(&r_sem[pChannel->serChannel]); /* get the read semaphore */

#if NEW_DMA
    if (pChannel->openmode & O_DMA) {
	err_code = serial_dma_read(pChannel, 
				   (char *)buffer, length, bytesRead);
	PUT_SEMA(&r_sem[pChannel->serChannel]);
	return err_code;
    }
#endif

#if REALLY_OLD_DMA
    if (pChannel->openmode & O_DMA)
    {
        if ( !pChannel->hw_flowc)
        {
            PUT_SEMA(&r_sem[pChannel->serChannel]);
            return EINVAL;
        }
        
        reg = SER_REG(pChannel->serChannel, CTRL_REGA);
        if (! (reg & RECV_DMA_ON))
        {
            reg |= RECV_DMA_ON;
            SER_REG(pChannel->serChannel, CTRL_REGA) = reg;
        }

        ser_enable_dma(pChannel->serChannel,O_RDWR);

        if (pChannel->openmode & O_NONBLOCK)
        {
            if (m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].count > 0)
            {
                if (ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.full)
                {
                    dmaBufferCount = ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.buflen;

                    fmemcpy(buffer, 
                            (char*)dma_ser_rx_buf[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id], 
                            dmaBufferCount);
                    
                    *bytesRead = dmaBufferCount;

                    ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.buflen = SIZE_OF_DMA_BUF;
                    ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.status = 0;
                    ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.full = 0;
                }
                
                m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].count = 0;
                m_qhead_head[pChannel->serChannel]++;
                if (m_qhead_head[pChannel->serChannel] == M_QHEAD_SIZE)
                {
                    m_qhead_head[pChannel->serChannel] = 0;
                }

            }
            
            reg = SER_REG(pChannel->serChannel, CTRL_REGA);
            if (! (reg & RECV_DMA_ON))
            {
                reg |= RECV_DMA_ON;
                SER_REG(pChannel->serChannel, CTRL_REGA) = reg;
            }
            
            PUT_SEMA(&r_sem[pChannel->serChannel]);
            if (*bytesRead)
            {
                return err_code;
            }
            else
            {
                return EAGAIN;
            }
        }

        while ((m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].count == 0) 
               && ((ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.full)==0))
        {
            reg = SER_REG(pChannel->serChannel, CTRL_REGA);
            if (! (reg & RECV_DMA_ON))
            {
                reg |= RECV_DMA_ON;
                SER_REG(pChannel->serChannel, CTRL_REGA) = reg;
            }
              
            tx_thread_sleep(1);  /* wait for the data */  
        }

        /*  Disable Interrupts  */
        _DISAB_INTS(prevInterruptState, prevFIQState);

        dmaBufferCount = ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.buflen;

        fmemcpy(buffer, 
                (char*)dma_ser_rx_buf[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id], 
                dmaBufferCount);

        *bytesRead = dmaBufferCount;

        ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.buflen = SIZE_OF_DMA_BUF;
        ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.status = 0;
        ser_rx_buffer_des[pChannel->serChannel][m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].bd_id].upper_word.bits.full = 0;
        m_qhead[pChannel->serChannel][m_qhead_head[pChannel->serChannel]].count = 0;
        m_qhead_head[pChannel->serChannel]++;
        if (m_qhead_head[pChannel->serChannel] == M_QHEAD_SIZE)
        {
            m_qhead_head[pChannel->serChannel] = 0;
        }
         
        _RESTOR_INT (prevInterruptState, prevFIQState);

        PUT_SEMA(&r_sem[pChannel->serChannel]);
        if (*bytesRead > 0)
        {
            return err_code;
        }
        else
        {
            return EBUSY;
        }
    }
#endif /* REALLY_OLD_DMA */

    /*  per PPP Ping problem -- reported by Softworks   */
    _DISAB_INTS (prevInterruptState, prevFIQState);

    if (pChannel->openmode & O_NONBLOCK)  
    {
        /* if buffer is empty return immediately */
        if (_CB_EMPTY(&rx_buffer[pChannel->serChannel])) 
        {
            PUT_SEMA(&r_sem[pChannel->serChannel]);
            
            /*  per PPP Ping problem -- reported by Softworks   */
            _RESTOR_INT (prevInterruptState, prevFIQState);
            return EAGAIN;
        }
    }
    else
    { /* in blocking mode */
        /*  per PPP Ping problem -- reported by Softworks   */
        _RESTOR_INT (prevInterruptState, prevFIQState);

        /* wait for buffer to have data */
        while (_CB_EMPTY(&rx_buffer[pChannel->serChannel]))  
        {
            tx_thread_sleep(1);  /* block */
        }

        /*  per PPP Ping problem -- reported by Softworks   */
        _DISAB_INTS (prevInterruptState, prevFIQState);
    }

    _RESTOR_INT (prevInterruptState, prevFIQState);
    *bytesRead = ExtractSerialDataIntoUsersBuffer(
                                &rx_buffer[pChannel->serChannel],
                                 buffer, 
                                 length);
    _DISAB_INTS (prevInterruptState, prevFIQState);

    if ((_CB_NBUFFER(&rx_buffer[pChannel->serChannel]) 
         < (SW_BUF_THRESH_HOLD)))
    {
        int flowdiff = 0;
        if (pChannel->rx_sw_flow) {
            pChannel->rx_sw_flow = FALSE;
            flowdiff = 1;
        }
        if (pChannel->rx_hw_flow) {
            pChannel->rx_hw_flow = FALSE;
            flowdiff = 1;
        }
        if (flowdiff) {
            change_rx_flow(pChannel);
        }
    }
    /*  per PPP Ping problem -- reported by Softworks   */
    _RESTOR_INT (prevInterruptState, prevFIQState);

#if NEED_ECHO
    /* echo back */
    if (pChannel->echo && (pChannel->openmode & O_RDWR))  
    {
        echo (pChannel->echo, pChannel->serChannel, buffer, *bytesRead);
    }
#endif

    PUT_SEMA(&r_sem[pChannel->serChannel]) ;
    return err_code;
}


/*
 * Function: netos_serlwrite()
 *
 * Description: write data to driver
 *
 * Called by: DDI write
 *
 * Input : channel - 0 for port com1, 1 for port com2
 *         buffer - data to be sent
 *         length - size of data
 *         bytesWritten - store actual size being sent
 *
 * Return: EBADF if channel does not exist
 *         EINVAL
 *         EBUSY
 *         EAGAIN
 *         EXIT_SUCCESS
 *
 */

/* TODO: Check if port was opened with O_DATA{LOCK} */

int netos_serlwrite (int channel, void * buffer, int length, int *bytesWritten)
{
    ONCC_CHAN  *pChannel;
    int err_code = EXIT_SUCCESS;
    int remain = length; /* remain has number of bytes that needs to be sent */
    int pos=0, nbyte, max_free_space;

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }

    if (!pChannel->opened)
    {
        return EBADF;
    }

    *bytesWritten = 0;

    _GET_SEMA(&w_sem[pChannel->serChannel]); 
#if NEW_DMA
    if (pChannel->openmode & O_DMA) {
	err_code = serial_dma_write(pChannel, 
				    buffer, length, bytesWritten);
	PUT_SEMA(&w_sem[pChannel->serChannel]);
	return err_code;
    }
#endif /* NEW_DMA */
#if REALLY_OLD_DMA
    if (pChannel->openmode & O_DMA)
    {
        ser_dma_write(pChannel->serChannel, buffer, length, bytesWritten);
        PUT_SEMA(&w_sem[pChannel->serChannel]);
        if (bytesWritten)
        {
            return err_code;
        }
        else
        {
            return EBUSY;
        }
    }
#endif /* REALLY_OLD_DMA */

    while (remain > 0)    /* while there are still chars to be sent */
    {
        /* we have at least max_free_space at this point */
        max_free_space = 
            SIZE_OF_BUF - _CB_NBUFFER (&tx_buffer[pChannel->serChannel]);

        if (max_free_space == 0)
        {
            if (pChannel->openmode & O_NONBLOCK) /* non-blocking */
            {
                /* starts sending and returns immediately  */
                onccTxStartup ((SIO_CHAN*) pChannel);  
                *bytesWritten = length - remain;  /* calculate bytesWritten */
                PUT_SEMA(&w_sem[pChannel->serChannel]);  

                /* This cannot return EAGAIN because nonBlocking will
                   not work with that.  The application programmer has
                   to use the number of byte written to find out if
                   the write operation is successful. */
                return EXIT_SUCCESS;  
            }
            else
            {                            /* blocking mode */
                /* better put a delay here */
                while (_CB_FULL (&tx_buffer[pChannel->serChannel]))
                {
                    onccTxStartup ((SIO_CHAN*) pChannel);
                    tx_thread_sleep (1);
                }

                /* we have at least max_free_space at this point */
                max_free_space = SIZE_OF_BUF 
                    - _CB_NBUFFER (&tx_buffer[pChannel->serChannel]);
            }
        }

        /* figure out how many bytes we should put into the buffer */
        nbyte = (remain <= max_free_space)? remain : max_free_space;
        remain -= nbyte;

        /* copy data into buffer */
        if (pChannel->opMode & OP_MODE_INSERT_CR) 
        {
            while (nbyte)
            {
                if (((char*)buffer)[pos] == '\n')
                {
                    /* we need an extra space on tx_buffer */
                    max_free_space = SIZE_OF_BUF 
                        - _CB_NBUFFER (&tx_buffer[pChannel->serChannel]);

                    /* it is OK to put the extra char */
                    if (max_free_space > nbyte) 
                    {
                        _CB_ADD (&tx_buffer[pChannel->serChannel], '\r');
                    }
                    else
                    {
                        if (pChannel->openmode & O_NONBLOCK) /* non-blocking */
                        {
                            onccTxStartup ((SIO_CHAN*) pChannel);
                            remain += nbyte;
                            *bytesWritten = length - remain;
                            PUT_SEMA(&w_sem[pChannel->serChannel]);
                            return EAGAIN;
                        }
                        else
                        {
                            /* better put a delay here */
                            while (max_free_space <= nbyte)
                            {
                                onccTxStartup ((SIO_CHAN*) pChannel);
                                tx_thread_sleep (1);
                                max_free_space = SIZE_OF_BUF 
                                    - _CB_NBUFFER(&tx_buffer[pChannel->serChannel]);
                            }
                            
                            _CB_ADD (&tx_buffer[pChannel->serChannel], '\r');
                        }
                    }
                }

                _CB_ADD(&tx_buffer[pChannel->serChannel], 
                        ((char*)buffer)[pos++]);
                nbyte--;
            }
        }
        else
        {
            int result;

            result  = InsertUsersDataBuffer(
                                (CBUFFER *)&tx_buffer[pChannel->serChannel], 
                                (char *)buffer + pos, 
                                nbyte);
            nbyte                      -= result;
            pos                        += result;
        }

        /* turn on the transmitter */
        onccTxStartup ((SIO_CHAN*) pChannel);
    }
    
    /* if it is blocking, we got here because all the data is sent */
    *bytesWritten = length;

    PUT_SEMA(&w_sem[pChannel->serChannel]);

    return err_code;
}


/*
 * Function: netos_serlioctl()
 *
 * Description This function calls onccIoctl to sets the serial parameters
 * returns
 *              EINVAL when channel is not valid
 *              EBADF when channel is not opened
 *              err_code returned from onccIoctl
 */

/*TODO: Check if port was opened with O_CONFIG */

int netos_serlioctl (int channel, int request, char *args)
{
    ONCC_CHAN  *pChannel;

    pChannel = sysSerialChanGet (channel);
    if (pChannel == NULL)
    {
        return EBADF;
    }

#if 0 /* Can no longer make this check, non-data opens won't set 'opened' */
    if (!pChannel->opened )/*make sure device is opened*/
    {
        return EBADF;
    }
#endif

    return digiIoctl ((SIO_CHAN *) pChannel, request, (int) args);
}





#if NEED_ECHO
/*
 * Function: echo()
 *
 * Description: whenever a byte is receive, write it back.
 *
 * Called by: netos_serlread()
 *
 * Input : arg - TERM_ECHO_ON or TERM_ECHO_OFF, TERM_ECHO_TRANSLATE
 *         channel - 0 for port com1, 1 for port com2
 *         buffer - data to be sent
 *         length - size of data
 *
 * Return: None
 *
 */

static void echo (int arg, int channel, void *buffer, int len)
{
    int dontcare;
    int pos, i;
    char *ptr;
    static int backslashRstate = 0;

    if (arg == TERM_ECHO_TRANSLATE)
    {
        ptr = (char*) buffer;
        pos = 0;
        i   = 0;

        if ((len > 0) && (backslashRstate == 1))
        {
            backslashRstate = 0;

            if (*ptr != '\n')
            {
                /*  send the \r */
                netos_serlwrite (channel, "\r", 1, &dontcare);
            }
        }

        while (i < len)
        {
            i = pos;

            while (i < (len - 1))
            {
                /*  point to an instance of "\r\n"  */
                if ((ptr[i] == '\r') && (ptr[i + 1] == '\n'))
                {
                    break;
                }
                else
                {
                    i++;
                }
            }
            
            if (i < (len - 1))
            {
                if ((i - pos) > 0)
                {
                    /* i points to an instance of "\r\n" -- echo
                        before the i-th character */
                    netos_serlwrite (channel, ptr + pos, (i - pos), &dontcare);
                }

                /*  jump in front of the /r character */
                i++;
                pos = i;
            }
            else 
            {
                if (ptr[i] == '\r')
                {
                    /* the last character is a \r -- save this state
                        to detect a \n */
                    backslashRstate = 1;

                    /*  echo all but the \r */
                    netos_serlwrite (channel, ptr+pos, (i - pos), &dontcare);
                }
                else
                {
                    /*  this is the last one    */
                    netos_serlwrite (channel, ptr+pos, 
                                     (i - pos) + 1, 
                                     &dontcare);
                }
                return;
            }
        }
    }
    else
    {
        netos_serlwrite (channel, buffer, len, &dontcare);
    }
}
#endif /* NEED_ECHO */


/*
 * Function: isatty()
 *
 * Returns: 1 - if the device is an open serial port, 0 - otherwise
 *
 */
int isatty(int desc)
{
    extern int NAserialComPortFds[];

    if (desc == NAserialComPortFds[0])
    {
        /*  yes -- it's /com/0   */
        if (onccChan[0].opened)
        {
            return 1;
        }
    }
    else if (desc == NAserialComPortFds[1])
    {
        /*  yes -- it's /com/1  */
        if (onccChan[1].opened)
        {
            return 1;
        }
    }

    return 0;
}

int
serl_port_index(int channel)
{
    ONCC_CHAN *pChan = sysSerialChanGet(channel);
    if (pChan == NULL) 
    {
        return -1;
    }
    return pChan->serChannel;
}
