/***************************************************************************** * 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 * * * 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 * *****************************************************************************/ /***************************************************************************** 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 #include #include #include #include #include #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; }