/***************************************************************************** * 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 #include #include #include #include #include #include #include "sci.h" #include /*#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 */ uint8_t inline SciReadCharWait( void ); /* hardware routine */ 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_cflag & CBAUD; /* 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; uint16_t 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 */ 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 */ uint16_t unused; /* maybe later */ uint16_t *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(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 ****************************************************************************/ 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 ****************************************************************************/ 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 #define O_RDWR LIBIO_FLAGS_READ_WRITE /* dont like this but... */ 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 %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; }