/*****************************************************************************
* 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.
*
* $Id$
*
* $Log$
* Revision 1.1 2002/02/28 23:10:39 joel
* 2002-02-28 Mike Panetta <ahuitzot@mindspring.com>
*
* * console/sci.c, console/sci.h,
* console/console.c: Added new SCI driver.
* * start/start.c: Removed file.
* * start/start.S: New file, the asm portion of the updated start code.
* * start/configure.am: Added start.S, removed start.c
* * startup/start_c.c: New file, the C portion of the updated start code. Contains most of the code that was in the old start.c.
* * startup/configure.am: Added start_c.c to C_FILES.
* * include/bsp.h: Added include <rtems/bspIo.h>
*
*****************************************************************************/
/*****************************************************************************
Compiler Options for the incurably curious
*****************************************************************************/
/*
/opt/rtems/bin/m68k-rtems-gcc
--pipe # use pipes, not tmp files
-B../../../../../../../../opti/lib/ # where the library is
-specs bsp_specs # ???
-qrtems # ???
-g # add debugging info
-Wall # issue all warnings
-fasm # allow inline asm???
-DCONSOLE_SCI # for opti-r box/rev b proto
-mcpu32 # machine = motorola cpu 32
-c # compile, don't link
-O4 # max optimization
-fomit-frame-pointer # stack frames are optional
-o o-optimize/sci.o # the object file
../../../../../../../../../rtems/c/src/lib/libbsp/m68k/opti/console/sci.c
*/
/*****************************************************************************
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 <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 "../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
//efine 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( signed32 polled );
rtems_device_driver SciInitialize (); // device driver api
rtems_device_driver SciOpen (); // device driver api
rtems_device_driver SciClose (); // device driver api
rtems_device_driver SciRead (); // device driver api
rtems_device_driver SciWrite (); // device driver api
rtems_device_driver SciControl (); // device driver api
signed32 SciInterruptOpen(); // termios api
signed32 SciInterruptClose(); // termios api
signed32 SciInterruptWrite(); // termios api
signed32 SciSetAttributes(); // termios api
signed32 SciPolledOpen(); // termios api
signed32 SciPolledClose(); // termios api
signed32 SciPolledRead(); // termios api
signed32 SciPolledWrite(); // termios api
static void SciSetBaud(unsigned32 rate); // hardware routine
static void SciSetDataBits(unsigned16 bits); // hardware routine
static void SciSetParity(unsigned16 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 ( unsigned8 ); // hardware routine
void SciWriteCharNoWait( unsigned8 ); // hardware routine
unsigned8 inline SciCharAvailable( void ); // hardware routine
unsigned8 inline SciReadCharWait( void ); // hardware routine
unsigned8 inline SciReadCharNoWait( void ); // hardware routine
void SciSendBreak( void ); // test routine
static signed8 SciRcvBufGetChar(); // circular rcv buf
static void SciRcvBufPutChar( unsigned8 ); // circular rcv buf
//atic void SciRcvBufFlush( void ); // circular rcv buf
void SciUnitTest(); // test routine
void SciPrintStats(); // test routine
/*****************************************************************************
Section F - Local Variables
*****************************************************************************/
static struct rtems_termios_tty *SciTermioTty;
static unsigned8 SciInited = 0; // has the driver been inited
static unsigned8 SciOpened; // has the driver been opened
static unsigned8 SciMajor; // major device number
static unsigned16 SciBaud; // current value in baud register
static unsigned32 SciBytesIn = 0; // bytes received
static unsigned32 SciBytesOut = 0; // bytes transmitted
static unsigned32 SciErrorsParity = 0; // error counter
static unsigned32 SciErrorsNoise = 0; // error counter
static unsigned32 SciErrorsFraming = 0; // error counter
static unsigned32 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
// cvs id string so you can use the unix ident command on the object
#ifdef ID_STRINGS
static const char SciIdent[]="$Id$";
#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 unsigned8 SciRcvBuffer[SCI_RCV_BUF_SIZE];
static unsigned8 SciRcvBufPutIndex = 0; // array index to put in next char
static unsigned8 SciRcvBufGetIndex = 0; // array index to take out next char
static unsigned8 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( signed32 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 )
{
unsigned8 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
if ( (*SCSR) & SCI_XMTR_AVAILABLE ) // data reg empty, xmt complete
{
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
{
rtems_termios_enqueue_raw_characters( SciTermioTty, &ch, 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 signed8 SciRcvBufGetChar()
{
rtems_interrupt_level level;
unsigned8 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( unsigned8 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. ??
* 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
****************************************************************************/
signed32 SciInterruptOpen(
signed32 major,
signed32 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, where does 66 come from?
rtems_interrupt_catch( SciIsr, 66, &old_vector );
*QIVR = 66;
*QIVR &= 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
****************************************************************************/
signed32 SciInterruptClose(
signed32 major,
signed32 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
****************************************************************************/
signed32 SciInterruptWrite(
signed32 minor,
const char *buf,
signed32 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 0; // return error
}
if ( minor != SCI_MINOR ) // check the minor dev num
{
return 0; // 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 1; // 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
****************************************************************************/
signed32 SciSetAttributes(
signed32 minor,
const struct termios *t
)
{
unsigned32 baud_requested;
unsigned32 sci_rate = 0;
unsigned16 sci_parity = 0;
unsigned16 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_cflag & CBAUD; // baud rate
if (!baud_requested)
{
baud_requested = B9600; // default to 9600 baud
}
sci_rate = 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
****************************************************************************/
signed32 SciPolledOpen(
signed32 major,
signed32 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
****************************************************************************/
signed32 SciPolledClose(
signed32 major,
signed32 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
****************************************************************************/
signed32 SciPolledRead(
signed32 minor
)
{
if ( minor != SCI_MINOR ) // check the minor 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
****************************************************************************/
signed32 SciPolledWrite(
signed32 minor,
const char *buf,
signed32 len
)
{
signed32 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
unsigned8 *buffer;
unsigned16 length;
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
length = rw_args->count; // how many bytes they want
// *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
unsigned8 *buffer;
unsigned16 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 = (unsigned8*)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
unsigned16 command; // the cmd to execute
unsigned16 unused; // maybe later
unsigned16 *ptr; // ptr to user data
//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
ptr = args->buffer; // this is an address
unused = *ptr; // brightness
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(unsigned32 rate)
{
unsigned16 value;
unsigned16 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 efi332.h for an example
value = ( (unsigned16) ( 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(unsigned16 parity)
{
unsigned16 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(unsigned16 bits)
{
unsigned16 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(unsigned8 c)
{
// poll the fifo, waiting for room for another character
while ( ( *SCSR & SCI_XMTR_AVAILABLE ) == 0 )
{
/* 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.
*/
}
*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(unsigned8 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
****************************************************************************/
unsigned8 inline SciReadCharWait( void )
{
unsigned8 ch;
while ( SciCharAvailable() == 0 ) // anything there?
{
// do nothing
}
// 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
****************************************************************************/
unsigned8 inline SciReadCharNoWait( void )
{
unsigned8 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
****************************************************************************/
unsigned8 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
#define O_RDWR LIBIO_FLAGS_READ_WRITE // dont like this but...
void SciUnitTest()
{
unsigned8 byte; // a character
unsigned16 fd; // file descriptor for device
unsigned16 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 %8d\r\n", SciBytesIn );
printk( "SCI Uart chars out %8d\r\n", SciBytesOut );
printk( "SCI Uart framing errors %8d\r\n", SciErrorsFraming );
printk( "SCI Uart parity errors %8d\r\n", SciErrorsParity );
printk( "SCI Uart overrun errors %8d\r\n", SciErrorsOverrun );
printk( "SCI Uart noise errors %8d\r\n", SciErrorsNoise );
return;
}