/*****************************************************************************
* File:     sci.c
*
* Desc:     This file contains the console IO routines for the SCI port.
*           There are two interfaces in this module. One is for the rtems
*           termios/console code and the other is a device driver interface.
*           This module works together with the termio module which is
*           sometimes referred to as the "line disciplines" which implements
*           terminal i/o processing like tabs, backspaces, and newlines.
*           The rtems printf uses interrupt io and the rtems printk routine
*           uses polled io which is better for debugging.
*
* Index:    Documentation
*           Section A  - Include Files
*           Section B  - Manifest Constants
*           Section C  - External Data
*           Section D  - External Functions
*           Section E  - Local Functions
*           Section F  - Local Variables
*           Section G  - A circular data buffer for rcv chars
*           Section H  - RTEMS termios callbacks for the interrupt api
*           Section I  - RTEMS termios callbacks for the polled api

*           Section 0  - Miscellaneous routines
*           Section 1  - Routines to manipulate the circular buffer
*           Section 2  - Interrupt based entry points for the termios module
*           Section 3  - Polling based entry points for the termios module
*           Section 4  - Device driver public api entry points
*           Section 5  - Hardware level routines
*           Section 6  - Testing and debugging code
*
* Refer:    Motorola QSM Reference Manual - Chapter 5 - SCI sub-module
*
* Note:     See bsp.h,confdefs.h,system.h for installing drivers into RTEMS.
*
*****************************************************************************/

/*****************************************************************************
  Overview of serial port console terminal input/output
*****************************************************************************/

/*
   +-----------+                               +---------+
   |    app    |                               |   app   |
   +-----------+                               +---------+
         |                                          |
         | (printf,scanf,etc.)                      |
         v                                          |
   +-----------+                                    |
   |    libc   |                                    |
   +-----------+                                    |
         |                                          |
         |                                          |
         |     (open,close,read,write,ioctl)        |
   ======|==========================================|========================
         | /dev/console                             | /dev/sci
         | (stdin,stdout,stderr)                    |
   ======|==========================================|========================
         |                                          |
         |                                          |
         v                                          v
   +-----------+         +-----------+         +---------+
   |  console  |  <--->  |  termios  |  <--->  |   sci   |
   |  driver   |         |  module   |         |  driver |
   +-----------+         +-----------+         +---------+
                                                    |
                                                    |
                                                    v
                                               +---------+
                                               |         |
                                               |  uart   |
                                               |         |
                                               +---------+
*/


/*****************************************************************************
  Section A - Include Files
*****************************************************************************/

#include <rtems.h>
#include <bsp.h>
#include <rtems/bspIo.h>
#include <stdio.h>
#include <rtems/libio.h>
#include <libchip/serial.h>
#include <libchip/sersupp.h>
#include "sci.h"
#include <rtems/m68k/qsm.h>
#include <inttypes.h>
/*#include "../misc/include/cpu332.h" */

/*****************************************************************************
  Section B - Manifest Constants
*****************************************************************************/

#define SCI_MINOR       0                   /* minor device number */

/* IMPORTANT - if the device driver api is opened, it means the sci is being
 * used for direct hardware access, so other users (like termios) get ignored
 */
#define DRIVER_CLOSED   0                   /* the device driver api is closed */
#define DRIVER_OPENED   1                   /* the device driver api is opened */

/* system clock definitions, i dont have documentation on this... */

#if 0 /* Not needed, this is provided in mrm332.h */
#define XTAL            32768.0    /* crystal frequency in Hz */
#define NUMB_W          0          /* system clock parameters */
#define NUMB_X          1
#define NUMB_Y          0x38       /* for 14.942 Mhz */
#define NUMB_Y          0x3F       /* for 16.777 Mhz */

#define SYS_CLOCK       (XTAL * 4.0 * (NUMB_Y+1) * (1 << (2 * NUMB_W + NUMB_X)))

#endif


/*****************************************************************************
  Section C - External Data
*****************************************************************************/



/*****************************************************************************
  Section D - External Functions
*****************************************************************************/



/*****************************************************************************
  Section E - Local Functions
*****************************************************************************/

void SCI_output_char(char c);

/*rtems_isr SciIsr( rtems_vector_number vector );   interrupt handler */

const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t   polled );

rtems_device_driver SciInitialize(                     /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciOpen(                           /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciClose(                          /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciRead(                           /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciWrite(                          /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciControl(                        /* device driver api */
    rtems_device_major_number, rtems_device_minor_number, void *);
rtems_device_driver SciRead (
    rtems_device_major_number, rtems_device_minor_number, void *);

rtems_isr SciIsr( rtems_vector_number vector );

int     SciInterruptOpen(int, int, void *);               /* termios api */
int     SciInterruptClose(int, int, void *);              /* termios api */
ssize_t SciInterruptWrite(int, const char *, size_t);     /* termios api */

int     SciSetAttributes(int, const struct termios*);     /* termios api */
int     SciPolledOpen(int, int, void *);                  /* termios api */
int     SciPolledClose(int, int, void *);                 /* termios api */
int     SciPolledRead(int);                               /* termios api */
ssize_t SciPolledWrite(int, const char *, size_t);        /* termios api */

static void SciSetBaud(uint32_t   rate);                /* hardware routine */
static void SciSetDataBits(uint16_t   bits);            /* hardware routine */
static void SciSetParity(uint16_t   parity);            /* hardware routine */

static void inline SciDisableAllInterrupts( void );     /* hardware routine */
static void inline SciDisableTransmitInterrupts( void );/* hardware routine */
static void inline SciDisableReceiveInterrupts( void ); /* hardware routine */

static void inline SciEnableTransmitInterrupts( void ); /* hardware routine */
static void inline SciEnableReceiveInterrupts( void );  /* hardware routine */

static void inline SciDisableReceiver( void );          /* hardware routine */
static void inline SciDisableTransmitter( void );       /* hardware routine */

static void inline SciEnableReceiver( void );           /* hardware routine */
static void inline SciEnableTransmitter( void );        /* hardware routine */

void SciWriteCharWait  ( uint8_t );                     /* hardware routine */
void SciWriteCharNoWait( uint8_t );                     /* hardware routine */

uint8_t   inline SciCharAvailable( void );              /* hardware routine */

static uint8_t   inline SciReadCharWait( void );        /* hardware routine */
static uint8_t   inline SciReadCharNoWait( void );      /* hardware routine */

void SciSendBreak( void );                              /* test routine */

static int8_t   SciRcvBufGetChar(void);                 /* circular rcv buf */
static void    SciRcvBufPutChar( uint8_t);              /* circular rcv buf */
#if 0
static void    SciRcvBufFlush( void );                  /* unused routine */
#endif

void SciUnitTest(void);                                 /* test routine */
void SciPrintStats(void);                               /* test routine */


/*****************************************************************************
  Section F - Local Variables
*****************************************************************************/

static struct rtems_termios_tty *SciTermioTty;

static uint8_t   SciInited = 0;             /* has the driver been inited */

static uint8_t   SciOpened;                 /* has the driver been opened */

static uint8_t   SciMajor;                  /* major device number */

static uint16_t   SciBaud;                  /* current value in baud register */

static uint32_t   SciBytesIn  = 0;          /* bytes received */
static uint32_t   SciBytesOut = 0;          /* bytes transmitted */

static uint32_t   SciErrorsParity  = 0;     /* error counter */
static uint32_t   SciErrorsNoise   = 0;     /* error counter */
static uint32_t   SciErrorsFraming = 0;     /* error counter */
static uint32_t   SciErrorsOverrun = 0;     /* error counter */

#if defined(CONSOLE_SCI)

/* this is what rtems printk uses to do polling based output */

BSP_output_char_function_type      BSP_output_char = SCI_output_char;
BSP_polling_getchar_function_type  BSP_poll_char   = NULL;

#endif

/*****************************************************************************
  Section G - A circular buffer for rcv chars when the driver interface is used.
*****************************************************************************/

/* it is trivial to wrap your buffer pointers when size is a power of two */

#define SCI_RCV_BUF_SIZE        256         /* must be a power of 2 !!! */

/* if someone opens the sci device using the device driver interface,
 * then the receive data interrupt handler will put characters in this buffer
 * instead of sending them up to the termios module for the console
 */
static uint8_t   SciRcvBuffer[SCI_RCV_BUF_SIZE];

static uint8_t   SciRcvBufPutIndex = 0; /* array index to put in next char */

static uint8_t   SciRcvBufGetIndex = 0; /* array index to take out next char */

static uint16_t  SciRcvBufCount = 0;   /* how many bytes are in the buffer */



/*****************************************************************************
  Section H - RTEMS termios callbacks for the interrupt version of the driver
*****************************************************************************/

static const rtems_termios_callbacks SciInterruptCallbacks =
{
    SciInterruptOpen,                       /* first open */
    SciInterruptClose,                      /* last close */
    NULL,                                   /* polled read (not required) */
    SciInterruptWrite,                      /* write */
    SciSetAttributes,                       /* set attributes */
    NULL,                                   /* stop remote xmit */
    NULL,                                   /* start remote xmit */
    TRUE                                    /* output uses interrupts */
};

/*****************************************************************************
  Section I - RTEMS termios callbacks for the polled version of the driver
*****************************************************************************/

static const rtems_termios_callbacks SciPolledCallbacks =
{
    SciPolledOpen,                          /* first open */
    SciPolledClose,                         /* last close */
    SciPolledRead,                          /* polled read */
    SciPolledWrite,                         /* write */
    SciSetAttributes,                       /* set attributes */
    NULL,                                   /* stop remote xmit */
    NULL,                                   /* start remote xmit */
    FALSE                                   /* output uses interrupts */
};


/*
 *                              SECTION 0
 *                        MISCELLANEOUS ROUTINES
 */

/****************************************************************************
 * Func:     SCI_output_char
 * Desc:     used by rtems printk function to send a char to the uart
 * Inputs:   the character to transmit
 * Outputs:  none
 * Errors:   none
 * Scope:    public
 ****************************************************************************/

void SCI_output_char(char c)
{
/*  ( minor device number, pointer to the character, length ) */

    SciPolledWrite( SCI_MINOR, &c, 1);

    return;
}


/****************************************************************************
* Func:     SciGetTermiosHandlers
* Desc:     returns a pointer to the table of serial io functions
*           this is called from console_open with polled set to false
* Inputs:   flag indicating whether we want polled or interrupt driven io
* Outputs:  pointer to function table
* Errors:   none
* Scope:    public
****************************************************************************/

const rtems_termios_callbacks * SciGetTermiosHandlers( int32_t   polled )
{
    if ( polled )
    {
        return &SciPolledCallbacks;             /* polling based */
    }
    else
    {
        return &SciInterruptCallbacks;          /* interrupt driven */
    }
}


/****************************************************************************
* Func:     SciIsr
* Desc:     interrupt handler for serial communications interface
* Inputs:   vector number - unused
* Outputs:  none
* Errors:   none
* Scope:    public API
****************************************************************************/

rtems_isr SciIsr( rtems_vector_number vector )
{
    uint8_t   ch;

    if ( (*SCSR) & SCI_ERROR_PARITY  )   SciErrorsParity  ++;
    if ( (*SCSR) & SCI_ERROR_FRAMING )   SciErrorsFraming ++;
    if ( (*SCSR) & SCI_ERROR_NOISE   )   SciErrorsNoise   ++;
    if ( (*SCSR) & SCI_ERROR_OVERRUN )   SciErrorsOverrun ++;

    /* see if it was a transmit interrupt */
    /* data reg empty, xmt complete */
    if ( ( *SCCR1 & SCI_ENABLE_INT_TX ) && ( (*SCSR) & SCI_XMTR_AVAILABLE ) )
    {
        SciDisableTransmitInterrupts();

        /* tell termios module that the charcter was sent */
        /* he will call us later to transmit more if there are any */

        if (rtems_termios_dequeue_characters( SciTermioTty, 1 ))
        {
            /* there are more bytes to transmit so enable TX interrupt */

            SciEnableTransmitInterrupts();
        }
    }

    /* see if it was a receive interrupt */
    /* on the sci uart we just get one character per interrupt */

    while (  SciCharAvailable() )               /* char in data register? */
    {
        ch = SciReadCharNoWait();               /* get the char from the uart */

        /* IMPORTANT!!! */
        /* either send it to the termios module or keep it locally */

        if ( SciOpened == DRIVER_OPENED )       /* the driver is open */
        {
            SciRcvBufPutChar(ch);               /* keep it locally */
        }
        else                                    /* put in termios buffer */
        {
            char c = (char) ch;
            rtems_termios_enqueue_raw_characters( SciTermioTty, &c, 1 );
        }

        *SCSR &= SCI_CLEAR_RX_INT;              /* clear the interrupt */
    }
}


/*
 *                              SECTION 1
 *                ROUTINES TO MANIPULATE THE CIRCULAR BUFFER
 */

/****************************************************************************
* Func:     SciRcvBufGetChar
* Desc:     read a character from the circular buffer
*           make sure there is data before you call this!
* Inputs:   none
* Outputs:  the character or -1
* Errors:   none
* Scope:    private
****************************************************************************/

static int8_t   SciRcvBufGetChar(void)
{
    rtems_interrupt_level level;
    uint8_t   ch;

    if ( SciRcvBufCount == 0 )
    {
        rtems_fatal_error_occurred(0xDEAD);     /* check the count first! */
    }

    rtems_interrupt_disable( level );           /* disable interrupts */

    ch = SciRcvBuffer[SciRcvBufGetIndex];       /* get next byte */

    SciRcvBufGetIndex++;                        /* bump the index */

    SciRcvBufGetIndex &= SCI_RCV_BUF_SIZE - 1;  /* and wrap it */

    SciRcvBufCount--;                           /* decrement counter */

    rtems_interrupt_enable( level );            /* restore interrupts */

    return ch;                                  /* return the char */
}


/****************************************************************************
* Func:     SciRcvBufPutChar
* Desc:     put a character into the rcv data circular buffer
* Inputs:   the character
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void SciRcvBufPutChar( uint8_t   ch )
{
    rtems_interrupt_level level;

    if ( SciRcvBufCount == SCI_RCV_BUF_SIZE )   /* is there room? */
    {
        return;                                 /* no, throw it away */
    }

    rtems_interrupt_disable( level );           /* disable interrupts */

    SciRcvBuffer[SciRcvBufPutIndex] = ch;       /* put it in the buf */

    SciRcvBufPutIndex++;                        /* bump the index */

    SciRcvBufPutIndex &= SCI_RCV_BUF_SIZE - 1;  /* and wrap it */

    SciRcvBufCount++;                           /* increment counter */

    rtems_interrupt_enable( level );            /* restore interrupts */

    return;                                     /* return */
}


/****************************************************************************
* Func:     SciRcvBufFlush
* Desc:     completely reset and clear the rcv buffer
* Inputs:   none
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

#if 0                                           /* prevents compiler warning */
static void SciRcvBufFlush( void )
{
    rtems_interrupt_level level;

    rtems_interrupt_disable( level );           /* disable interrupts */

    memset( SciRcvBuffer, 0, sizeof(SciRcvBuffer) );

    SciRcvBufPutIndex = 0;                      /* clear */

    SciRcvBufGetIndex = 0;                      /* clear */

    SciRcvBufCount = 0;                         /* clear */

    rtems_interrupt_enable( level );            /* restore interrupts */

    return;                                     /* return */
}
#endif


/*
 *
 *                              SECTION 2
 *            INTERRUPT BASED ENTRY POINTS FOR THE TERMIOS MODULE
 */

/****************************************************************************
* Func:     SciInterruptOpen
* Desc:     open routine for the interrupt based device driver
*           Default state is 9600 baud, 8 bits, No parity, and 1 stop bit. ??
**CHANGED** Default baud rate is now 19200, 8N1
*           called from rtems_termios_open which is called from console_open
* Inputs:   major - device number
*           minor - device number
*           args - points to terminal info
* Outputs:  success/fail
* Errors:   none
* Scope:    public API
****************************************************************************/

int   SciInterruptOpen(
    int    major,
    int    minor,
    void  *arg
)
{
    rtems_libio_open_close_args_t * args = arg;
    rtems_isr_entry old_vector;

    if ( minor != SCI_MINOR )                   /* check minor device num */
    {
        return -1;
    }

    if ( !args )                                /* must have args */
    {
        return -1;
    }

    SciTermioTty = args->iop->data1;            /* save address of struct */

    SciDisableAllInterrupts();                  /* turn off sci interrupts */

    /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */
    /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */

/*  SciSetBaud(115200);                         set the baud rate */
/*  SciSetBaud( 57600);                         set the baud rate */
/*  SciSetBaud( 38400);                         set the baud rate */
/*  SciSetBaud( 19200);                         set the baud rate */
    SciSetBaud(  9600);                      /* set the baud rate */

    SciSetParity(SCI_PARITY_NONE);              /* set parity to none */

    SciSetDataBits(SCI_8_DATA_BITS);            /* set data bits to 8 */

    /* Install our interrupt handler into RTEMS. */
    /* 68 is an unused user-defined vector.  Note that the vector must be */
    /* even - it sets the low bit for SPI interrupts, and clears it for */
    /* SCI interrupts.  Also note that vector 66 is used by CPU32bug on */
    /* the mrm332. */

    rtems_interrupt_catch( SciIsr, 68, &old_vector );

    *QSMCR = (*QSMCR & ~IARB) | 1; // Is 1 a good value for qsm iarb?
    *QIVR  = 68;
    *QILR &= 0xf8;
    *QILR |= 0x06 & 0x07;

    SciEnableTransmitter();                     /* enable the transmitter */

    SciEnableReceiver();                        /* enable the receiver */

    SciEnableReceiveInterrupts();               /* enable rcv interrupts */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciInterruptClose
* Desc:     close routine called by the termios module
* Inputs:   major - device number
*           minor - device number
*           args - unused
* Outputs:  success/fail
* Errors:   none
* Scope:    public - termio entry point
****************************************************************************/

int   SciInterruptClose(
    int    major,
    int    minor,
    void  *arg
)
{
    SciDisableAllInterrupts();

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciInterruptWrite
* Desc:     writes data to the uart using transmit interrupts
* Inputs:   minor - device number
*           buf - points to the data
*           len - number of bytes to send
* Outputs:  success/fail
* Errors:   none
* Scope:    public API
****************************************************************************/

ssize_t   SciInterruptWrite(
    int         minor,
    const char *buf,
    size_t      len
)
{
    /* We are using interrupt driven output so termios only sends us */
    /* one character at a time. The sci does not have a fifo. */

    if ( !len )                                 /* no data? */
    {
        return -1;                              /* return error */
    }

    if ( minor != SCI_MINOR )                   /* check the minor dev num */
    {
        return -1;                              /* return error */
    }

    if ( SciOpened == DRIVER_OPENED )           /* is the driver api open? */
    {
        return -1;                              /* yep, throw this away */
    }

    SciWriteCharNoWait(*buf);                   /* try to send a char */

    *SCSR &= SCI_CLEAR_TDRE;                    /* clear tx data reg empty flag */

    SciEnableTransmitInterrupts();              /* enable the tx interrupt */

    return 0;                                   /* return success */
}


/****************************************************************************
* Func:     SciSetAttributes
* Desc:     setup the uart based on the termios modules requests
* Inputs:   minor - device number
*           t - pointer to the termios info struct
* Outputs:  none
* Errors:   none
* Scope:    public API
****************************************************************************/

int   SciSetAttributes(
    int                   minor,
    const struct termios *t
)
{
    uint32_t    baud_requested;
    uint32_t    sci_rate = 0;
    uint16_t    sci_parity = 0;
    uint16_t    sci_databits = 0;

    if ( minor != SCI_MINOR )                   /* check the minor dev num */
    {
        return -1;                              /* return error */
    }

    /* if you look closely you will see this is the only thing we use */
    /* set the baud rate */

    baud_requested = t->c_ospeed;               /* baud rate */

    if (!baud_requested)
    {
        baud_requested = B9600;                /* default to 9600 baud */
       /* baud_requested = B19200;                default to 19200 baud */
    }

    sci_rate = rtems_termios_baud_to_number( baud_requested );

    /* parity error detection */

    if (t->c_cflag & PARENB)                    /* enable parity detection? */
    {
        if (t->c_cflag & PARODD)
        {
            sci_parity = SCI_PARITY_ODD;        /* select odd parity */
        }
        else
        {
            sci_parity = SCI_PARITY_EVEN;       /* select even parity */
        }
    }
    else
    {
        sci_parity = SCI_PARITY_NONE;           /* no parity, most common */
    }

    /*  set the number of data bits, 8 is most common */

    if (t->c_cflag & CSIZE)                     /* was it specified? */
    {
        switch (t->c_cflag & CSIZE)
        {
            case CS8:   sci_databits = SCI_8_DATA_BITS;   break;
            default :   sci_databits = SCI_9_DATA_BITS;   break;
        }
    }
    else
    {
        sci_databits = SCI_8_DATA_BITS;         /* default to 8 data bits */
    }

    /*  the number of stop bits; always 1 for SCI */

    if (t->c_cflag & CSTOPB)
    {
        /* do nothing */
    }

    /* setup the hardware with these serial port parameters */

    SciSetBaud(sci_rate);                       /* set the baud rate */
    SciSetParity(sci_parity);                   /* set the parity type */
    SciSetDataBits(sci_databits);               /* set the data bits */

    return RTEMS_SUCCESSFUL;
}


/*
 *
 *                              SECTION 3
 *            POLLING BASED ENTRY POINTS FOR THE TERMIOS MODULE
 */

/****************************************************************************
* Func:     SciPolledOpen
* Desc:     open routine for the polled i/o version of the driver
*           called from rtems_termios_open which is called from console_open
* Inputs:   major - device number
*           minor - device number
*           args - points to terminal info struct
* Outputs:  success/fail
* Errors:   none
* Scope:    public - termios entry point
****************************************************************************/

int   SciPolledOpen(
    int   major,
    int   minor,
    void *arg
)
{
    rtems_libio_open_close_args_t * args = arg;

    if ( minor != SCI_MINOR )                   /* check minor device num */
    {
        return -1;
    }

    if ( !args )                                /* must have args */
    {
        return -1;
    }

    SciTermioTty = args->iop->data1;            /* Store tty pointer */

    SciDisableAllInterrupts();                  /* don't generate interrupts */

    /* THIS IS ACTUALLY A BAD THING - SETTING LINE PARAMETERS HERE */
    /* IT SHOULD BE DONE THROUGH TCSETATTR() WHEN THE CONSOLE IS OPENED!!! */

/*  SciSetBaud(115200);                            set the baud rate */
/*  SciSetBaud( 57600);                            set the baud rate */
/*  SciSetBaud( 38400);                            set the baud rate */
/*  SciSetBaud( 19200);                          * set the baud rate */
    SciSetBaud(  9600);                         /* set the baud rate */

    SciSetParity(SCI_PARITY_NONE);              /* set no parity */

    SciSetDataBits(SCI_8_DATA_BITS);            /* set 8 data bits */

    SciEnableTransmitter();                     /* enable the xmitter */

    SciEnableReceiver();                        /* enable the rcvr */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciPolledClose
* Desc:     close routine for the device driver, same for both
* Inputs:   major - device number
*           minor - device number
*           args - unused
* Outputs:  success/fail
* Errors:   none
* Scope:    public termios API
****************************************************************************/

int   SciPolledClose(
    int    major,
    int    minor,
    void  *arg
)
{
    SciDisableAllInterrupts();

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciPolledRead
* Desc:     polling based read routine for the uart
* Inputs:   minor - device number
* Outputs:  error or the character read
* Errors:   none
* Scope:    public API
****************************************************************************/

int   SciPolledRead(
    int   minor
)
{
    if ( minor != SCI_MINOR )               /* check the type-punned dev num */
    {
        return -1;                          /* return error */
    }

    if ( SciCharAvailable() )               /* if a char is available */
    {
        return SciReadCharNoWait();         /* read the rx data register */
    }

    return -1;                              /* return error */
}


/****************************************************************************
* Func:     SciPolledWrite
* Desc:     writes out characters in polled mode, waiting for the uart
*           check in console_open, but we only seem to use interrupt mode
* Inputs:   minor - device number
*           buf - points to the data
*           len - how many bytes
* Outputs:  error or number of bytes written
* Errors:   none
* Scope:    public termios API
****************************************************************************/

ssize_t SciPolledWrite(
    int         minor,
    const char *buf,
    size_t      len
)
{
    ssize_t written = 0;

    if ( minor != SCI_MINOR )                   /* check minor device num */
    {
        return -1;
    }

    if ( SciOpened == DRIVER_OPENED )           /* is the driver api open? */
    {
        return -1;                              /* toss the data */
    }

    /* send each byte in the string out the port */

    while ( written < len )
    {
        SciWriteCharWait(*buf++);               /* send a byte */

        written++;                              /* increment counter */
    }

    return written;                             /* return count */
}


/*
 *
 *                              SECTION 4
 *                 DEVICE DRIVER PUBLIC API ENTRY POINTS
 */

/****************************************************************************
* Func:     SciInit
* Desc:     Initialize the lasers device driver and hardware
* Inputs:   major - the major device number which is assigned by rtems
*           minor - the minor device number which is undefined at this point
*           arg - ?????
* Outputs:  RTEMS_SUCCESSFUL
* Errors:   None.
* Scope:    public API
****************************************************************************/

rtems_device_driver SciInitialize (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void * arg
)
{
/*     rtems_status_code status; */

/*printk("%s\r\n", __FUNCTION__); */

    /* register the SCI device name for termios console i/o
     * this is done over in console.c which doesn't seem exactly right
     * but there were problems doing it here...
     */

/*  status = rtems_io_register_name( "/dev/sci", major, 0 ); */

/*  if (status != RTEMS_SUCCESSFUL) */
/*      rtems_fatal_error_occurred(status); */

    SciMajor = major;                           /* save the rtems major number */

    SciOpened = DRIVER_CLOSED;                  /* initial state is closed */

    /* if you have an interrupt handler, install it here */

    SciInited = 1;                              /* set the inited flag */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciOpen
* Desc:     device driver open routine
*           you must open a device before you can anything else
*           only one process can have the device opened at a time
*           you could look at the task id to restrict access if you want
* Inputs:   major - the major device number assigned by rtems
*           minor - the minor device number assigned by us
*           arg - ?????
* Outputs:  see below
* Errors:   none
* Scope:    public API
****************************************************************************/

rtems_device_driver SciOpen (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void * arg
)
{
/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */

    if (SciInited == 0)                         /* must be initialized first! */
    {
        return RTEMS_NOT_CONFIGURED;
    }

    if (minor != SCI_MINOR)
    {
        return RTEMS_INVALID_NAME;              /* verify minor number */
    }

    if (SciOpened == DRIVER_OPENED)
    {
        return RTEMS_RESOURCE_IN_USE;           /* already opened! */
    }

    SciOpened = DRIVER_OPENED;                  /* set the opened flag */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciClose
* Desc:     device driver close routine
*           the device must be opened before you can close it
*           the device must be closed before someone (else) can open it
* Inputs:   major - the major device number
*           minor - the minor device number
*           arg - ?????
* Outputs:  see below
* Errors:   none
* Scope:    public API
****************************************************************************/

rtems_device_driver SciClose (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void * arg
)
{
/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */

    if (minor != SCI_MINOR)
    {
        return RTEMS_INVALID_NAME;              /* check the minor number */
    }

    if (SciOpened != DRIVER_OPENED)
    {
        return RTEMS_INCORRECT_STATE;           /* must be opened first */
    }

    SciOpened = DRIVER_CLOSED;                  /* set the flag */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciRead
* Desc:     device driver read routine
*           this function is not meaningful for the laser devices
* Inputs:   major - the major device number
*           minor - the minor device number
*           arg - read/write arguments
* Outputs:  see below
* Errors:   none
* Scope:    public API
****************************************************************************/

rtems_device_driver SciRead (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void *arg
)
{
    rtems_libio_rw_args_t *rw_args;             /* ptr to argument struct */
    char      *buffer;

    rw_args = (rtems_libio_rw_args_t *) arg;    /* arguments to read() */

    if (minor != SCI_MINOR)
    {
        return RTEMS_INVALID_NAME;              /* check the minor number */
    }

    if (SciOpened == DRIVER_CLOSED)
    {
        return RTEMS_INCORRECT_STATE;           /* must be opened first */
    }

    buffer = rw_args->buffer;                   /* points to user's buffer */

/*  *buffer = SciReadCharWait();                   wait for a character */

    /* if there isn't a character available, wait until one shows up */
    /* or the timeout period expires, which ever happens first */

    if ( SciRcvBufCount == 0 )                  /* no chars */
    {
        /* wait for someone to wake me up... */
        /*rtems_task_wake_after(SciReadTimeout); */
    }

    if ( SciRcvBufCount )                       /* any characters locally? */
    {
        *buffer = SciRcvBufGetChar();           /* get the character */

        rw_args->bytes_moved = 1;               /* how many we actually read */
    }

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciWrite
* Desc:     device driver write routine
*           this function is not meaningful for the laser devices
* Inputs:   major - the major device number
*           minor - the minor device number
*           arg - read/write arguments
* Outputs:  see below
* Errors:   non3
* Scope:    public API
****************************************************************************/

rtems_device_driver SciWrite (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void * arg
)
{
    rtems_libio_rw_args_t *rw_args;             /* ptr to argument struct */
    uint8_t   *buffer;
    size_t     length;

    rw_args = (rtems_libio_rw_args_t *) arg;

    if (minor != SCI_MINOR)
    {
        return RTEMS_INVALID_NAME;              /* check the minor number */
    }

    if (SciOpened == DRIVER_CLOSED)
    {
        return RTEMS_INCORRECT_STATE;           /* must be opened first */
    }

    buffer = (uint8_t*)rw_args->buffer;       /* points to data */

    length = rw_args->count;                    /* how many bytes */

    while (length--)
    {
        SciWriteCharWait(*buffer++);            /* send the bytes out */
    }

    rw_args->bytes_moved = rw_args->count;      /* how many we wrote */

    return RTEMS_SUCCESSFUL;
}


/****************************************************************************
* Func:     SciControl
* Desc:     device driver control routine
*           see below for an example of how to use the ioctl interface
* Inputs:   major - the major device number
*           minor - the minor device number
*           arg - io control args
* Outputs:  see below
* Errors:   none
* Scope:    public API
****************************************************************************/

rtems_device_driver SciControl (
    rtems_device_major_number major,
    rtems_device_minor_number minor,
    void * arg
)
{
    rtems_libio_ioctl_args_t *args = arg;       /* rtems arg struct */
    uint16_t   command;                         /* the cmd to execute */

/*printk("%s major=%d minor=%d\r\n", __FUNCTION__,major,minor); */

    /* do some sanity checking */

    if (minor != SCI_MINOR)
    {
        return RTEMS_INVALID_NAME;              /* check the minor number */
    }

    if (SciOpened == DRIVER_CLOSED)
    {
        return RTEMS_INCORRECT_STATE;           /* must be open first */
    }

    if (args == 0)
    {
        return RTEMS_INVALID_ADDRESS;           /* must have args */
    }

    args->ioctl_return = -1;                    /* assume an error */

    command = args->command;                    /* get the command */

    if (command == SCI_SEND_BREAK)              /* process the command */
    {
        SciSendBreak();                         /* send break char */
    }

    args->ioctl_return = 0;                     /* return status */

    return RTEMS_SUCCESSFUL;
}


/*
 *
 *                              SECTION 5
 *                       HARDWARE LEVEL ROUTINES
 */

/****************************************************************************
* Func:     SciSetBaud
* Desc:     setup the uart based on the termios modules requests
* Inputs:   baud rate
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void SciSetBaud(uint32_t   rate)
{
    uint16_t   value;
    uint16_t   save_sccr1;

/* when you open the console you need to set the termio struct baud rate */
/* it has a default value of 9600, when someone calls tcsetattr it reverts! */

    SciBaud = rate;                             /* save the rate */

    /* calculate the register value as a float and convert to an int */
    /* set baud rate - you must define the system clock constant */
    /* see mrm332.h for an example */

    value = ( (uint16_t) ( SYS_CLOCK / rate / 32.0 + 0.5 ) & 0x1fff );

    save_sccr1 = *SCCR1;                        /* save register */

    /* also turns off the xmtr and rcvr */

    *SCCR1 &= SCI_DISABLE_INT_ALL;              /* disable interrupts */

    *SCCR0 = value;                             /* write the register */

    *SCCR1 = save_sccr1;                        /* restore register */

    return;
}


/****************************************************************************
* Func:     SciSetParity
* Desc:     setup the uart based on the termios modules requests
* Inputs:   parity
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void SciSetParity(uint16_t   parity)
{
    uint16_t   value;

    value = *SCCR1;                             /* get the register */

    if (parity == SCI_PARITY_ODD)
    {
        value |= SCI_PARITY_ENABLE;             /* parity enabled */
        value |= SCI_PARITY_ODD;                /* parity odd */
    }

    else if (parity == SCI_PARITY_EVEN)
    {
        value |= SCI_PARITY_ENABLE;             /* parity enabled */
        value &= ~SCI_PARITY_ODD;               /* parity even */
    }

    else if (parity == SCI_PARITY_NONE)
    {
        value &= ~SCI_PARITY_ENABLE;            /* disabled, most common */
    }

    /* else no changes */

    *SCCR1 = value;                             /* write the register */

    return;
}


/****************************************************************************
* Func:     SciSetDataBits
* Desc:     setup the uart based on the termios modules requests
* Inputs:   data bits
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void SciSetDataBits(uint16_t   bits)
{
    uint16_t   value;

    value = *SCCR1;                             /* get the register */

    /* note - the parity setting affects the number of data bits */

    if (bits == SCI_9_DATA_BITS)
    {
        value |= SCI_9_DATA_BITS;               /* 9 data bits */
    }

    else if (bits == SCI_8_DATA_BITS)
    {
        value &= SCI_8_DATA_BITS;               /* 8 data bits */
    }

    /* else no changes */

    *SCCR1 = value;                             /* write the register */

    return;
}


/****************************************************************************
* Func:     SciDisableAllInterrupts
* Func:     SciEnableTransmitInterrupts
* Func:     SciEnableReceiveInterrupts
* Desc:     handles generation of interrupts by the sci module
* Inputs:   none
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void inline SciDisableAllInterrupts( void )
{
    /* this also turns off the xmtr and rcvr */

    *SCCR1 &= SCI_DISABLE_INT_ALL;
}

static void inline SciEnableReceiveInterrupts( void )
{
    *SCCR1 |= SCI_ENABLE_INT_RX;
}

static void inline SciDisableReceiveInterrupts( void )
{
    *SCCR1 &= SCI_DISABLE_INT_RX;
}

static void inline SciEnableTransmitInterrupts( void )
{
    *SCCR1 |= SCI_ENABLE_INT_TX;
}

static void inline SciDisableTransmitInterrupts( void )
{
    *SCCR1 &= SCI_DISABLE_INT_TX;
}


/****************************************************************************
* Func:     SciEnableTransmitter, SciDisableTransmitter
* Func:     SciEnableReceiver,    SciDisableReceiver
* Desc:     turns the transmitter and receiver on and off
* Inputs:   none
* Outputs:  none
* Errors:   none
* Scope:    private
****************************************************************************/

static void inline SciEnableTransmitter( void )
{
    *SCCR1 |= SCI_ENABLE_XMTR;
}

static void inline SciDisableTransmitter( void )
{
    *SCCR1 &= SCI_DISABLE_XMTR;
}

static void inline SciEnableReceiver( void )
{
    *SCCR1 |= SCI_ENABLE_RCVR;
}

static void inline SciDisableReceiver( void )
{
    *SCCR1 &= SCI_DISABLE_RCVR;
}


/****************************************************************************
* Func:     SciWriteCharWait
* Desc:     wait for room in the fifo and then put a char in
* Inputs:   a byte to send
* Outputs:  none
* Errors:   none
* Scope:    public
****************************************************************************/

void SciWriteCharWait(uint8_t   c)
{
    /* poll the fifo, waiting for room for another character */

    while ( ( *SCSR & SCI_XMTR_AVAILABLE ) != SCI_XMTR_AVAILABLE )
    {
        /* Either we are writing to the fifo faster than
         * the uart can clock bytes out onto the cable,
         * or we are in flow control (actually no, we
         * are ignoring flow control from the other end).
         * In the first case, higher baud rates will help.
         */
      /* relinquish processor while waiting */
      rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    }

    *SCDR = c;                                  /* send the charcter */

    SciBytesOut++;                              /* increment the counter */

    return;
}

/****************************************************************************
* Func:     SciWriteCharNoWait
* Desc:     if no room in the fifo throw the char on the floor
* Inputs:   a byte to send
* Outputs:  none
* Errors:   none
* Scope:    public
****************************************************************************/

void SciWriteCharNoWait(uint8_t   c)
{
    if ( ( *SCSR & SCI_XMTR_AVAILABLE ) == 0 )
    {
        return;                                 /* no room, throw it away */
    }

    *SCDR = c;                                  /* put the char in the fifo */

    SciBytesOut++;                              /* increment the counter */

    return;
}


/****************************************************************************
* Func:     SciReadCharWait
* Desc:     read a character, waiting for one to show up, if need be
* Inputs:   none
* Outputs:  a character
* Errors:   none
* Scope:    public
****************************************************************************/

static uint8_t   inline SciReadCharWait( void )
{
    uint8_t   ch;

    while ( SciCharAvailable() == 0 )           /* anything there? */
    {
      /* relinquish processor while waiting */
      rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    }

    /* if you have rcv ints enabled, then the isr will probably */
    /* get the character before you will unless you turn off ints */
    /* ie polling and ints don't mix that well */

    ch = *SCDR;                                 /* get the charcter */

    SciBytesIn++;                               /* increment the counter */

    return ch;                                  /* return the char */
}

/****************************************************************************
* Func:     SciReadCharNoWait
* Desc:     try to get a char but dont wait for one
* Inputs:   none
* Outputs:  a character or -1 if none
* Errors:   none
* Scope:    public
****************************************************************************/

static uint8_t   inline SciReadCharNoWait( void )
{
    uint8_t   ch;

    if ( SciCharAvailable() == 0 )              /* anything there? */
        return -1;

    ch = *SCDR;                                 /* get the character */

    SciBytesIn++;                               /* increment the count */

    return ch;                                  /* return the char */
}


/****************************************************************************
* Func:     SciCharAvailable
* Desc:     is there a receive character in the data register
* Inputs:   none
* Outputs:  false if no char available, else true
* Errors:   none
* Scope:    public
****************************************************************************/

uint8_t   inline SciCharAvailable( void )
{
    return ( *SCSR & SCI_RCVR_READY );          /* char in data register? */
}


/****************************************************************************
* Func:     SciSendBreak
* Desc:     send 1 or tow breaks (all zero bits)
* Inputs:   none
* Outputs:  none
* Errors:   none
* Scope:    public
****************************************************************************/

void SciSendBreak( void )
{
    /* From the Motorola QSM reference manual - */

    /* "if SBK is toggled by writing it first to a one and then immediately */
    /* to a zero (in less than one serial frame interval), the transmitter */
    /* sends only one or two break frames before reverting to mark (idle) */
    /* or before commencing to send more data" */

    *SCCR1 |=  SCI_SEND_BREAK;                  /* set the bit */

    *SCCR1 &= ~SCI_SEND_BREAK;                  /* clear the bit */

    return;
}


/*
 *
 *                             SECTION 6
 *                             TEST CODE
 */

/****************************************************************************
* Func:     SciUnitTest
* Desc:     test the device driver
* Inputs:   nothing
* Outputs:  nothing
* Scope:    public
****************************************************************************/

#if 0
void SciUnitTest()
{
    uint8_t   byte;                             /* a character */
    uint16_t   fd;                              /* file descriptor for device */
    uint16_t   result;                          /* result of ioctl */

    fd = open("/dev/sci",O_RDWR);               /* open the device */

printk("SCI open fd=%d\r\n",fd);

    result = write(fd, "abcd\r\n", 6);          /* send a string */

printk("SCI write result=%d\r\n",result);

    result = read(fd, &byte, 1);                /* read a byte */

printk("SCI read result=%d,byte=%x\r\n",result,byte);

    return;
}
#endif


/****************************************************************************
* Func:     SciPrintStats
* Desc:     print out some driver information
* Inputs:   nothing
* Outputs:  nothing
* Scope:    public
****************************************************************************/

void SciPrintStats ( void )
{
    printk("\r\n");

    printk( "SYS_CLOCK is %2.6f Mhz\r\n\n", SYS_CLOCK / 1000000.0 );

    printk( "Current baud rate is %d bps or %d cps\r\n\n", SciBaud, SciBaud / 10 );

    printk( "SCI Uart chars in       %8" PRIu32 "\r\n", SciBytesIn       );
    printk( "SCI Uart chars out      %8" PRIu32 "\r\n", SciBytesOut      );
    printk( "SCI Uart framing errors %8" PRIu32 "\r\n", SciErrorsFraming );
    printk( "SCI Uart parity  errors %8" PRIu32 "\r\n", SciErrorsParity  );
    printk( "SCI Uart overrun errors %8" PRIu32 "\r\n", SciErrorsOverrun );
    printk( "SCI Uart noise   errors %8" PRIu32 "\r\n", SciErrorsNoise   );

    return;
}
