diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-19 06:28:01 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:08:32 +0200 |
commit | d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc (patch) | |
tree | caa54b4229e86a68c84ab5961af34e087dce5302 /bsps/m68k/mvme167 | |
parent | bsps/powerpc: Move shared btimer support (diff) | |
download | rtems-d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc.tar.bz2 |
bsps: Move console drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/m68k/mvme167')
-rw-r--r-- | bsps/m68k/mvme167/console/console-recording.h | 572 | ||||
-rw-r--r-- | bsps/m68k/mvme167/console/console.c | 1676 |
2 files changed, 2248 insertions, 0 deletions
diff --git a/bsps/m68k/mvme167/console/console-recording.h b/bsps/m68k/mvme167/console/console-recording.h new file mode 100644 index 0000000000..4d8d3fc66c --- /dev/null +++ b/bsps/m68k/mvme167/console/console-recording.h @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2000, National Research Council of Canada + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +/* CD2401 CONSOLE DRIVER DEBUG INFO RECORDING */ + +#ifdef CD2401_RECORD_DEBUG_INFO + +/* Control individual recording here. That way, we don't clutter console.c */ +#define CD2401_RECORD_WRITE +#define CD2401_RECORD_TX_ISR +#define CD2401_RECORD_RX_ISR +#define CD2401_RECORD_RE_ISR +#define CD2401_RECORD_MODEM_ISR +#define CD2401_RECORD_SET_ATTRIBUTE +#define CD2401_RECORD_FIRST_OPEN +#define CD2401_RECORD_LAST_CLOSE +#define CD2401_RECORD_START_REMOTE_TX +#define CD2401_RECORD_STOP_REMOTE_TX +#define CD2401_RECORD_DRAIN_OUTPUT +#define CD2401_RECORD_DELAY + +/* Call the data recording functions */ +#ifdef CD2401_RECORD_WRITE +#define CD2401_RECORD_WRITE_INFO( args ) cd2401_record_write_info args +#else +#define CD2401_RECORD_WRITE_INFO( args ) +#endif + +#ifdef CD2401_RECORD_TX_ISR +#define CD2401_RECORD_TX_ISR_INFO( args ) cd2401_record_tx_isr_info args +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) cd2401_record_tx_isr_spurious_info args +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) cd2401_record_tx_isr_buserr_info args +#else +#define CD2401_RECORD_TX_ISR_INFO( args ) +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) +#endif + +#ifdef CD2401_RECORD_RX_ISR +#define CD2401_RECORD_RX_ISR_INFO( args ) cd2401_record_rx_isr_info args +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) cd2401_record_rx_isr_spurious_info args +#else +#define CD2401_RECORD_RX_ISR_INFO( args ) +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_RE_ISR +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) cd2401_record_re_isr_spurious_info args +#else +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_MODEM_ISR +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) cd2401_record_modem_isr_spurious_info args +#else +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) +#endif + +#ifdef CD2401_RECORD_SET_ATTRIBUTES +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) cd2401_record_set_attributes_info args +#else +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) +#endif + +#ifdef CD2401_RECORD_FIRST_OPEN +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) cd2401_record_first_open_info args +#else +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) +#endif + +#ifdef CD2401_RECORD_LAST_CLOSE +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) cd2401_record_last_close_info args +#else +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) +#endif + +#ifdef CD2401_RECORD_START_REMOTE_TX +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) cd2401_record_start_remote_tx_info args +#else +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) +#endif + +#ifdef CD2401_RECORD_STOP_REMOTE_TX +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) cd2401_record_stop_remote_tx_info args +#else +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) +#endif + +#ifdef CD2401_RECORD_DRAIN_OUTPUT +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) cd2401_record_drain_output_info args +#else +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) +#endif + +#ifdef CD2401_RECORD_DELAY +#define CD2401_RECORD_DELAY_INFO( args ) cd2401_record_delay_info args +#else +#define CD2401_RECORD_DELAY_INFO( args ) +#endif + +/* Define the data and the recording functions */ +#define CD2401_DEBUG_BUFFER_SIZE 256 +#define CD2401_DEBUG_CHAR_BUFSIZE 64 +#define CD2401_WRITE_INFO 1 +#define CD2401_TX_ISR_INFO 2 +#define CD2401_TX_ISR_SPURIOUS_INFO 3 +#define CD2401_TX_ISR_BUSERR_INFO 4 +#define CD2401_RX_ISR_INFO 5 +#define CD2401_RX_ISR_SPURIOUS_INFO 6 +#define CD2401_RE_ISR_SPURIOUS_INFO 7 +#define CD2401_MODEM_ISR_SPURIOUS_INFO 8 +#define CD2401_FIRST_OPEN_INFO 9 +#define CD2401_LAST_CLOSE_INFO 10 +#define CD2401_START_REMOTE_TX_INFO 11 +#define CD2401_STOP_REMOTE_TX_INFO 12 +#define CD2401_SET_ATTRIBUTE_INFO 13 +#define CD2401_DRAIN_OUTPUT_INFO 14 +#define CD2401_DELAY_INFO 15 + +struct cd2401_debug_info { + short discriminant; + short record_size; + union { + struct cd2401_write_info { + int length; + char buffer[CD2401_DEBUG_CHAR_BUFSIZE]; + char dmabuf; + } write_info; + struct cd2401_tx_isr_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char final_ier; + uint8_t txEmpty; + } tx_isr_info; + struct cd2401_tx_isr_spurious_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char final_ier; + unsigned long spurdev; + unsigned long spurcount; + } tx_isr_spurious_info; + struct cd2401_tx_isr_buserr_info { + unsigned char channel; + unsigned char status; + unsigned char initial_ier; + unsigned char buserr; + unsigned long type; + unsigned long addr; + } tx_isr_buserr_info; + struct cd2401_rx_isr_info { + unsigned char channel; + int length; + char buffer[CD2401_DEBUG_CHAR_BUFSIZE]; + } rx_isr_info; + struct cd2401_rx_isr_spurious_info { + unsigned char channel; + unsigned char status; + unsigned long spurdev; + unsigned long spurcount; + } rx_isr_spurious_info; + struct cd2401_re_isr_spurious_info { + unsigned char channel; + unsigned long spurdev; + unsigned long spurcount; + } re_isr_spurious_info; + struct cd2401_modem_isr_spurious_info { + unsigned char channel; + unsigned long spurdev; + unsigned long spurcount; + } modem_isr_spurious_info; + struct cd2401_first_open_info { + unsigned char channel; + uint8_t init_count; + } first_open_info; + struct cd2401_last_close_info { + unsigned char channel; + uint8_t init_count; + } last_close_info; + struct cd2401_start_remote_tx_info { + unsigned char channel; + } start_remote_tx_info; + struct cd2401_stop_remote_tx_info { + unsigned char channel; + } stop_remote_tx_info; + struct cd2401_set_attribute_info { + int minor; + uint8_t need_reinit; + uint8_t txEmpty; + uint8_t csize; + uint8_t cstopb; + uint8_t parodd; + uint8_t parenb; + uint8_t ignpar; + uint8_t inpck; + uint8_t hw_flow_ctl; + uint8_t sw_flow_ctl; + uint8_t extra_flow_ctl; + uint8_t icrnl; + uint8_t igncr; + uint8_t inlcr; + uint8_t brkint; + uint8_t ignbrk; + uint8_t parmrk; + uint8_t istrip; + uint16_t tx_period; + uint16_t rx_period; + uint32_t out_baud; + uint32_t in_baud; + } set_attribute_info; + struct cd2401_drain_output_info { + uint8_t txEmpty; + uint8_t own_buf_A; + uint8_t own_buf_B; + } drain_output_info; + struct cd2401_delay_info { + rtems_interval start; + rtems_interval end; + rtems_interval current; + unsigned long loop_count; + } delay_info; + } u; +}; + +struct cd2401_debug_info cd2401_debug_buffer[CD2401_DEBUG_BUFFER_SIZE]; +int cd2401_debug_index = 0; + +#include <string.h> + +int cd2401_get_record_size( + int size +) +{ + /* Not the best way to do this */ + return size + 4; +} + +void cd2401_record_write_info( + int len, + const char * buf, + char dmabuf +) +{ + int max_length; + + max_length = (len < CD2401_DEBUG_CHAR_BUFSIZE ) ? len : CD2401_DEBUG_CHAR_BUFSIZE; + + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_WRITE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_write_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.write_info.length = len; + memcpy ( &(cd2401_debug_buffer[cd2401_debug_index].u.write_info.buffer), buf, max_length ); + cd2401_debug_buffer[cd2401_debug_index].u.write_info.dmabuf = dmabuf; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char final_ier, + uint8_t txEmpty +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.final_ier = final_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_info.txEmpty = txEmpty; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_spurious_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char final_ier, + unsigned char spur_dev, + unsigned char spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.final_ier = final_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_tx_isr_buserr_info( + unsigned char ch, + unsigned char status, + unsigned char initial_ier, + unsigned char buserr, + unsigned long buserr_type, + unsigned long buserr_addr +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_TX_ISR_BUSERR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_tx_isr_buserr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.initial_ier = initial_ier; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.buserr = buserr; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.type = buserr_type; + cd2401_debug_buffer[cd2401_debug_index].u.tx_isr_buserr_info.addr = buserr_addr; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_rx_isr_info( + unsigned char ch, + unsigned char total, + char * buffer +) +{ + int max_length; + + max_length = (total < CD2401_DEBUG_CHAR_BUFSIZE ) ? total : CD2401_DEBUG_CHAR_BUFSIZE; + + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RX_ISR_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_rx_isr_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_info.length = max_length; + memcpy ( &(cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_info.buffer), buffer, max_length ); + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_rx_isr_spurious_info( + unsigned char ch, + unsigned char status, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RX_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_rx_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.status = status; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.rx_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_re_isr_spurious_info( + unsigned char ch, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_RE_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_re_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.re_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_modem_isr_spurious_info( + unsigned char ch, + uint32_t spur_dev, + uint32_t spur_cnt +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_MODEM_ISR_SPURIOUS_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_modem_isr_spurious_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.spurdev = spur_dev; + cd2401_debug_buffer[cd2401_debug_index].u.modem_isr_spurious_info.spurcount = spur_cnt; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_first_open_info( + unsigned char ch, + uint8_t init_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_FIRST_OPEN_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_first_open_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.first_open_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.first_open_info.init_count = init_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_last_close_info( + unsigned char ch, + uint8_t init_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_LAST_CLOSE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_last_close_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.last_close_info.channel = ch; + cd2401_debug_buffer[cd2401_debug_index].u.last_close_info.init_count = init_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_start_remote_tx_info( + unsigned char ch +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_START_REMOTE_TX_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_start_remote_tx_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.start_remote_tx_info.channel = ch; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_stop_remote_tx_info( + unsigned char ch +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_STOP_REMOTE_TX_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_stop_remote_tx_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.stop_remote_tx_info.channel = ch; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_set_attributes_info( + int minor, + uint8_t need_reinit, + uint8_t csize, + uint8_t cstopb, + uint8_t parodd, + uint8_t parenb, + uint8_t ignpar, + uint8_t inpck, + uint8_t hw_flow_ctl, + uint8_t sw_flow_ctl, + uint8_t extra_flow_ctl, + uint8_t icrnl, + uint8_t igncr, + uint8_t inlcr, + uint8_t brkint, + uint8_t ignbrk, + uint8_t parmrk, + uint8_t istrip, + uint16_t tx_period, + uint16_t rx_period, + uint32_t out_baud, + uint32_t in_baud +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_SET_ATTRIBUTE_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_set_attribute_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.minor = minor; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.need_reinit = need_reinit; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.txEmpty = CD2401_Channel_Info[minor].txEmpty; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.csize = csize; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.cstopb = cstopb; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parodd = parodd; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parenb = parenb; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.ignpar = ignpar; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.inpck = inpck; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.hw_flow_ctl = hw_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.sw_flow_ctl = sw_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.extra_flow_ctl = extra_flow_ctl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.icrnl = icrnl; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.igncr = igncr; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.inlcr = inlcr; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.brkint = brkint; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.ignbrk = ignbrk; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.parmrk = parmrk; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.istrip = istrip; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.tx_period = tx_period; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.rx_period = rx_period; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.out_baud = out_baud; + cd2401_debug_buffer[cd2401_debug_index].u.set_attribute_info.in_baud = in_baud; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_drain_output_info( + uint8_t txEmpty, + uint8_t own_buf_A, + uint8_t own_buf_B +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_DRAIN_OUTPUT_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_drain_output_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.txEmpty = txEmpty; + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.own_buf_A = own_buf_A; + cd2401_debug_buffer[cd2401_debug_index].u.drain_output_info.own_buf_B = own_buf_B; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +void cd2401_record_delay_info( + rtems_interval start, + rtems_interval end, + rtems_interval current, + unsigned long loop_count +) +{ + memset( &(cd2401_debug_buffer[cd2401_debug_index]), '\0', sizeof( struct cd2401_debug_info ) ); + cd2401_debug_buffer[cd2401_debug_index].discriminant = CD2401_DELAY_INFO; + cd2401_debug_buffer[cd2401_debug_index].record_size = + cd2401_get_record_size( sizeof( struct cd2401_delay_info ) ); + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.start = start; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.end = end; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.current = current; + cd2401_debug_buffer[cd2401_debug_index].u.delay_info.loop_count = loop_count; + + cd2401_debug_index = (cd2401_debug_index + 1 ) % CD2401_DEBUG_BUFFER_SIZE; +} + +#else + +/* Do not call the data recording functions */ +#define CD2401_RECORD_WRITE_INFO( args ) +#define CD2401_RECORD_TX_ISR_INFO( args ) +#define CD2401_RECORD_TX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_TX_ISR_BUSERR_INFO( args ) +#define CD2401_RECORD_RX_ISR_INFO( args ) +#define CD2401_RECORD_RX_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_RE_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO( args ) +#define CD2401_RECORD_FIRST_OPEN_INFO( args ) +#define CD2401_RECORD_LAST_CLOSE_INFO( args ) +#define CD2401_RECORD_START_REMOTE_TX_INFO( args ) +#define CD2401_RECORD_STOP_REMOTE_TX_INFO( args ) +#define CD2401_RECORD_SET_ATTRIBUTES_INFO( args ) +#define CD2401_RECORD_DRAIN_OUTPUT_INFO( args ) +#define CD2401_RECORD_DELAY_INFO( args ) + +#endif diff --git a/bsps/m68k/mvme167/console/console.c b/bsps/m68k/mvme167/console/console.c new file mode 100644 index 0000000000..0499ac46b3 --- /dev/null +++ b/bsps/m68k/mvme167/console/console.c @@ -0,0 +1,1676 @@ +/* + * This file contains the MVME167 termios console package. Only asynchronous + * I/O is supported. + * + * /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M. + * /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M. + * /dev/tty2 is channel 2, Serial Port 3 on the MVME712M. + * /dev/tty3 is channel 3, Serial Port 4 on the MVME712M. + * + * Normal I/O uses DMA for output, interrupts for input. /dev/console is + * fixed to be /dev/tty01, Serial Port 2. Very limited support is provided + * for polled I/O. Polled I/O is intended only for running the RTEMS test + * suites. In all cases, Serial Port 1/Console is allocated to 167Bug and + * is the dedicated debugger port. We configure GDB to use 167Bug for + * debugging. When debugging with GDB or 167Bug, do not open /dev/tty00. + * + * Modern I/O chips often contain a number of I/O devices that can operate + * almost independently of each other. Typically, in RTEMS, all devices in + * an I/O chip are handled by a single device driver, but that need not be + * always the case. Each device driver must supply six entry points in the + * Device Driver Table: a device initialization function, as well as an open, + * close, read, write and a control function. RTEMS assigns a device major + * number to each device driver. This major device number is the index of the + * device driver entries in the Device Driver Table, and it used to identify + * a particular device driver. To distinguish multiple I/O sub-devices within + * an I/O chip, RTEMS supports device minor numbers. When a I/O device is + * initialized, the major number is supplied to the initialization function. + * That function must register each sub-device with a separate name and minor + * number (as well as the supplied major number). When an application opens a + * device by name, the corresponding major and minor numbers are returned to + * the caller to be used in subsequent I/O operations (although these details + * are typically hidden within the library functions). + * + * Such a scheme recognizes that the initialization of the individual + * sub-devices is generally not completely independent. For example, the + * four serial ports of the CD2401 can be configured almost independently + * from each other. One port could be configured to operate in asynchronous + * mode with interrupt-driven I/O, while another port could be configured to + * operate in HDLC mode with DMA I/O. However, a device reset command will + * reset all four channels, and the width of DMA transfers and the number of + * retries following bus errors selected applies to all four channels. + * Consequently, when initializing one channel, one must be careful not to + * destroy the configuration of other channels that are already configured. + * + * One problem with the RTEMS I/O initialization model is that no information + * other than a device major number is passed to the initialization function. + * Consequently, the sub-devices must be initialized with some pre-determined + * configuration. To change the configuration of a sub-device, it is + * necessary to either rewrite the initialization function, or to make a + * series of rtems_io_control() calls after initialization. The first + * approach is not very elegant. The second approach is acceptable if an + * application is simply changing baud rates, parity or other such + * asynchronous parameters (as supplied by the termios package). But what if + * an application requires one channel to run in HDLC or Bisync mode and + * another in async mode? With a single driver per I/O chip approach, the + * device driver must support multiple protocols. This is feasible, but it + * often means that an application that only does asynchronous I/O now links + * in code for other unused protocols, thus wasting precious ROM space. + * Worse, it requires that the sub-devices be initialized in some + * configuration, and that configuration then changed through a series of + * device driver control calls. There is no standard API in RTEMS to switch + * a serial line to some synchronous protocol. + * + * A better approach is to treat each channel as a separate device, each with + * its own device device driver. The application then supplies its own device + * driver table with only the required protocols (drivers) on each line. The + * problem with this approach is that the device drivers are not really + * independent, given that the I/O sub-devices within a common chip are not + * independent themselves. Consequently, the related device drivers must + * share some information. In RTEMS, there is no standard location in which + * to share information. + * + * This driver handles all four channels, i.e. it distinguishes the + * sub-devices using minor device numbers. Only asynchronous I/O is + * supported. The console is currently fixed to be channel 1 on the CD2401, + * which corresponds to the TTY01 port (Serial Port 2) on the MVME712M + * Transition Module. + * + * The CD2401 does either interrupt-driven or DMA I/O; it does not support + * polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401 + * are routed to the MC68040, and the processor generates an interrupt + * acknowledge cycle directly to the CD2401 to obtain an interrupt vector. + * The PCCchip2 supports a pseudo-polling mode in which interrupts from the + * CD2401 are not routed to the MC68040, but can be detected by the processor + * by reading the appropriate CD2401 registers. In this mode, interrupt + * acknowledge cycles must be generated to the CD2401 by reading the + * appropriate PCCchip2 registers. + * + * Interrupts from the four channels cannot be routed independently; either + * all channels are used in the pseudo-polling mode, or all channels are used + * in interrupt-driven/DMA mode. There is no advantage in using the speudo- + * polling mode. Consenquently, this driver performs DMA input and output. + * Output is performed directly from the termios raw output buffer, while + * input is accumulated into a separate buffer. + * + * THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from + * multiple tasks is likely to cause significant problems! Concurrency + * control is implemented in the termios package. + * + * THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS. + * If the CD2401 is to be used for high speed synchronous serial I/O, the + * interrupt priority might need to be increased. + * + * ALL INTERRUPT HANDLERS ARE SHARED. + * When adding extra device drivers, either rewrite the interrupt handlers + * to demultiplex the interrupts, or install separate vectors. Common vectors + * are currently used to catch spurious interrupts. We could already have + * installed separate vectors for each channel and used the spurious + * interrupt handler defined in some other BSPs, but handling spurious + * interrupts from the CD2401 in this device driver allows us to record more + * information on the source of the interrupts. Furthermore, we have observed + * the occasional spurious interrupt from channel 0. We definitely do not + * to call a debugger for those. + * + * All page references are to the MVME166/MVME167/MVME187 Single Board + * Computer Programmer's Reference Guide (MVME187PG/D2) with the April + * 1993 supplements/addenda (MVME187PG/D2A1). + */ + +/* + * Copyright (c) 1998, National Research Council of Canada + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#define M167_INIT + +#include <stdarg.h> +#include <stdio.h> +#include <termios.h> + +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <bsp.h> /* Must be before libio.h */ + +/* Utility functions */ +void cd2401_udelay( unsigned long delay ); +void cd2401_chan_cmd( uint8_t channel, uint8_t cmd, uint8_t wait ); +uint16_t cd2401_bitrate_divisor( uint32_t clkrate, uint32_t * bitrate ); +void cd2401_initialize( void ); +void cd2401_interrupts_initialize( bool enable ); + +/* ISRs */ +rtems_isr cd2401_modem_isr( rtems_vector_number vector ); +rtems_isr cd2401_re_isr( rtems_vector_number vector ); +rtems_isr cd2401_rx_isr( rtems_vector_number vector ); +rtems_isr cd2401_tx_isr( rtems_vector_number vector ); + +/* Termios callbacks */ +int cd2401_firstOpen( int major, int minor, void *arg ); +int cd2401_lastClose( int major, int minor, void *arg ); +int cd2401_setAttributes( int minor, const struct termios *t ); +int cd2401_startRemoteTx( int minor ); +int cd2401_stopRemoteTx( int minor ); +ssize_t cd2401_write( int minor, const char *buf, size_t len ); +int cd2401_drainOutput( int minor ); +int _167Bug_pollRead( int minor ); +ssize_t _167Bug_pollWrite( int minor, const char *buf, size_t len ); + +/* Printk function */ +static void _BSP_output_char( char c ); +BSP_output_char_function_type BSP_output_char = _BSP_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +/* '\r' character in memory. This used to live on + * the stack but storing the '\r' character is + * optimized away by gcc-4.3.2 (since it seems to + * be unused [only referenced from inline assembly + * code in _167Bug_pollWrite()]). + * Hence we make it a global constant. + */ +static const char cr_char = '\r'; + +/* Channel info */ +/* static */ volatile struct { + void *tty; /* Really a struct rtems_termios_tty * */ + int len; /* Record nb of chars being TX'ed */ + const char *buf; /* Record where DMA is coming from */ + uint32_t spur_cnt; /* Nb of spurious ints so far */ + uint32_t spur_dev; /* Indo on last spurious int */ + uint32_t buserr_addr; /* Faulting address */ + uint32_t buserr_type; /* Reason of bus error during DMA */ + uint8_t own_buf_A; /* If true, buffer A belongs to the driver */ + uint8_t own_buf_B; /* If true, buffer B belongs to the driver */ + uint8_t txEmpty; /* If true, the output FIFO should be empty */ +} CD2401_Channel_Info[4]; + +/* + * The number of channels already opened. If zero, enable the interrupts. The + * initial value must be 0. If initialized explicitly, the variable ends up + * in the .data section. Its value is not re-initialized on system restart. + * Furthermore, because the variable is changed, the .data section would not + * be ROMable. We thus leave the variable uninitialized, which causes it to + * be allocated in the .bss section, and rely on RTEMS to zero the .bss + * section on every startup. + */ +uint8_t Init_count; + +/* Record previous handlers */ +rtems_isr_entry Prev_re_isr; /* Previous rx exception isr */ +rtems_isr_entry Prev_rx_isr; /* Previous rx isr */ +rtems_isr_entry Prev_tx_isr; /* Previous tx isr */ +rtems_isr_entry Prev_modem_isr; /* Previous modem/timer isr */ + +/* Define the following symbol to trace the calls to this driver */ +/* #define CD2401_RECORD_DEBUG_INFO */ +#include "console-recording.h" + +/* + * Utility functions. + */ + +/* + * Assumes that clock ticks 1 million times per second. + * + * MAXIMUM DELAY IS ABOUT 20 ms + * + * Input parameters: + * delay: Number of microseconds to delay. + * + * Output parameters: NONE + * + * Return values: NONE + */ + void cd2401_udelay +( + unsigned long delay +) +{ + unsigned long i = 20000; /* In case clock is off */ + rtems_interval start_ticks, end_ticks, current_ticks; + + start_ticks = rtems_clock_get_ticks_since_boot(); + end_ticks = start_ticks + delay; + + do { + current_ticks = rtems_clock_get_ticks_since_boot(); + } while ( --i && (current_ticks <= end_ticks) ); + + CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i )); +} + +/* + * cd2401_chan_cmd + * + * Sends a CCR command to the specified channel. Waits for any unfinished + * previous command to complete, then sends the specified command. Optionally + * wait for the current command to finish before returning. + * + * Input parameters: + * channel - CD2401 channel number + * cmd - command byte + * wait - if non-zero, wait for specified command to complete before + * returning. + * + * Output parameters: NONE + * + * Return values: NONE + */ +void cd2401_chan_cmd( + uint8_t channel, + uint8_t cmd, + uint8_t wait +) +{ + if ( channel < 4 ) { + cd2401->car = channel; /* Select channel */ + + while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */ + cd2401->ccr = cmd; /* Send command */ + if ( wait ) + while( cd2401->ccr != 0 );/* Wait for completion */ + } + else { + /* This may not be the best error message */ + rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER ); + } +} + +/* + * cd2401_bitrate_divisor + * + * Compute the divisor and clock source to use to obtain the desired bitrate. + * + * Input parameters: + * clkrate - system clock rate (CLK input frequency) + * bitrate - the desired bitrate + * + * Output parameters: + * bitrate - The actual bitrate achievable, to the nearest bps. + * + * Return values: + * Returns divisor in lower byte and clock source in upper byte for the + * specified bitrate. + */ +uint16_t cd2401_bitrate_divisor( + uint32_t clkrate, + uint32_t * bitrate +) +{ + uint32_t divisor; + uint16_t clksource; + + divisor = *bitrate << 3; /* temporary; multiply by 8 for CLK/8 */ + divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */ + + /* Use highest speed clock source for best precision - try clk0 to clk4 */ + for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 ) + divisor >>= 2; + divisor--; /* adjustment, see specs */ + if( divisor < 1 ) + divisor = 1; + else if( divisor > 0xFF ) + divisor = 0xFF; + *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1); + return( clksource | divisor ); +} + +/* + * cd2401_initialize + * + * Initializes the CD2401 device. Individual channels on the chip are left in + * their default reset state, and should be subsequently configured. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * Return values: NONE + */ +void cd2401_initialize( void ) +{ + int i; + + for ( i = 3; i >= 0; i-- ) { + CD2401_Channel_Info[i].tty = NULL; + CD2401_Channel_Info[i].len = 0; + CD2401_Channel_Info[i].buf = NULL; + CD2401_Channel_Info[i].spur_cnt = 0; + CD2401_Channel_Info[i].spur_dev = 0; + CD2401_Channel_Info[i].buserr_type = 0; + CD2401_Channel_Info[i].buserr_addr = 0; + CD2401_Channel_Info[i].own_buf_A = TRUE; + CD2401_Channel_Info[i].own_buf_B = TRUE; + CD2401_Channel_Info[i].txEmpty = TRUE; + } + + /* + * Normally, do a device reset here. If we do it, we will most likely clober + * the port settings for 167Bug on channel 0. So we just shut up all the + * ports by disabling their interrupts. + */ +#if 0 + cd2401->gfrcr = 0; /* So we can detect that device init is done */ + cd2401_chan_cmd( 0x10, 0); /* Reset all */ + while(cd2401->gfrcr == 0); /* Wait for reset all */ +#endif + + /* + * The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that + * the LICR "contains the number of the interrupting channel being served. + * The channel number is always that of the current acknowledged interrupt." + * THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically + * by the hardware, as suggested by the manual. + * + * The updated manual (part no 542400-007) has the story straight. The + * CD2401 automatically initializes the LICR to contain the channel number + * in bits 2 and 3. However, these bits are not preserved when the user + * defined bits are written. + * + * The same vector number is used for all four channels. Different vector + * numbers could be programmed for each channel, thus avoiding the need to + * demultiplex the interrupts in the ISR. + */ + for ( i = 0; i < 4; i++ ) { + cd2401->car = i; /* Select channel */ + cd2401->livr = 0x5C; /* Motorola suggested value p. 3-15 */ + cd2401->licr = i << 2; /* Don't rely on reset value */ + cd2401->ier = 0; /* Disable all interrupts */ + } + + /* + * The content of the CD2401 xpilr registers must match the A7-A0 addresses + * generated by the PCCchip2 during interrupt acknowledge cycles in order + * for the CD2401 to recognize the IACK cycle and clear its interrupt + * request. + */ + cd2401->mpilr = 0x01; /* Match pccchip2->modem_piack p. 3-27 */ + cd2401->tpilr = 0x02; /* Match pccchip2->tx_piack p. 3-28 */ + cd2401->rpilr = 0x03; /* Match pccchip2->rx_piack p. 3-29 */ + + /* Global CD2401 registers */ + cd2401->dmr = 0; /* 16-bit DMA transfers when possible */ + cd2401->bercnt = 0; /* Do not retry DMA upon bus errors */ + + /* + * Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout + * and tx delay). The prescaler is clocked by the system clock) / 2048. The + * register must be in the range 0x0A..0xFF, ie. a rescaler period range of + * about 1ms..26ms for a nominal system clock rate of 20MHz. + */ + cd2401->tpr = 0x0A; /* Same value as 167Bug */ +} + +/* + * cd2401_interrupts_initialize + * + * This routine enables or disables the CD2401 interrupts to the MC68040. + * Interrupts cannot be enabled/disabled on a per-channel basis. + * + * Input parameters: + * enable - if true, enable the interrupts, else disable them. + * + * Output parameters: NONE + * + * Return values: NONE + * + * THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS. + * THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS. + */ +void cd2401_interrupts_initialize( + bool enable +) +{ + if ( enable ) { + /* + * Enable interrupts from the CD2401 in the PCCchip2. + * During DMA transfers, the MC68040 supplies dirty data during read cycles + * from the CD2401 and leaves the data dirty in its data cache if there is + * a cache hit. The MC68040 updates the data cache during write cycles from + * the CD2401 if there is a cache hit. + */ + pccchip2->SCC_error = 0x01; + pccchip2->SCC_modem_int_ctl = 0x10 | CD2401_INT_LEVEL; + pccchip2->SCC_tx_int_ctl = 0x10 | CD2401_INT_LEVEL; + pccchip2->SCC_rx_int_ctl = 0x50 | CD2401_INT_LEVEL; + + pccchip2->gen_control |= 0x02; /* Enable pccchip2 interrupts */ + } + else { + /* Disable interrupts */ + pccchip2->SCC_modem_int_ctl &= 0xEF; + pccchip2->SCC_tx_int_ctl &= 0xEF; + pccchip2->SCC_rx_int_ctl &= 0xEF; + } +} + +/* ISRs */ + +/* + * cd2401_modem_isr + * + * Modem/timer interrupt (group 1) from CD2401. These are not used, and not + * expected. Record as spurious and clear. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_modem_isr( + rtems_vector_number vector +) +{ + uint8_t ch; + + /* Get interrupting channel ID */ + ch = cd2401->licr >> 2; + + /* Record interrupt info for debugging */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->mir << 8) | cd2401->misr; + CD2401_Channel_Info[ch].spur_cnt++; + + cd2401->meoir = 0; /* EOI */ + CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO(( ch, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); +} + +/* + * cd2401_re_isr + * + * RX exception interrupt (group 3, receiver exception) from CD2401. These are + * not used, and not expected. Record as spurious and clear. + * + * FIX THIS ISR TO DETECT BREAK CONDITIONS AND RAISE SIGINT + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_re_isr( + rtems_vector_number vector +) +{ + uint8_t ch; + + /* Get interrupting channel ID */ + ch = cd2401->licr >> 2; + + /* Record interrupt info for debugging */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl; + CD2401_Channel_Info[ch].spur_cnt++; + + if ( cd2401->u5.b.risrl & 0x80 ) /* Timeout interrupt? */ + cd2401->ier &= 0xDF; /* Disable rx timeout interrupt */ + cd2401->reoir = 0x08; /* EOI; exception char not read */ + CD2401_RECORD_RE_ISR_SPURIOUS_INFO(( ch, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); +} + +/* + * cd2401_rx_isr + * + * RX interrupt (group 3, receiver data) from CD2401. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_rx_isr( + rtems_vector_number vector +) +{ + char c; + uint8_t ch, status, nchars, total; + #ifdef CD2401_RECORD_DEBUG_INFO + uint8_t i = 0; + char buffer[256]; + #endif + + (void) total; /* avoid set but not used warnings when not recording info */ + + status = cd2401->u5.b.risrl; + ch = cd2401->licr >> 2; + + /* Has this channel been initialized or is it a condition we ignore? */ + if ( CD2401_Channel_Info[ch].tty && !status ) { + /* Normal Rx Int, read chars, enqueue them, and issue EOI */ + total = nchars = cd2401->rfoc; /* Nb of chars to retrieve from rx FIFO */ + while ( nchars-- > 0 ) { + c = (char)cd2401->dr; /* Next char in rx FIFO */ + rtems_termios_enqueue_raw_characters( CD2401_Channel_Info[ch].tty ,&c, 1 ); + #ifdef CD2401_RECORD_DEBUG_INFO + buffer[i++] = c; + #endif + } + cd2401->reoir = 0; /* EOI */ + CD2401_RECORD_RX_ISR_INFO(( ch, total, buffer )); + } else { + /* No, record as spurious interrupt */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl; + CD2401_Channel_Info[ch].spur_cnt++; + cd2401->reoir = 0x04; /* EOI - character not read */ + CD2401_RECORD_RX_ISR_SPURIOUS_INFO(( ch, status, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + } +} + +/* + * cd2401_tx_isr + * + * TX interrupt (group 2) from CD2401. + * + * Input parameters: + * vector - vector number + * + * Output parameters: NONE + * + * Return values: NONE + */ +rtems_isr cd2401_tx_isr( + rtems_vector_number vector +) +{ + uint8_t ch, status, buserr, initial_ier, final_ier; + + status = cd2401->tisr; + ch = cd2401->licr >> 2; + initial_ier = cd2401->ier; + + #ifndef CD2401_RECORD_DEBUG_INFO + /* + * When the debug is disabled, these variables are really not read. + * But when debug is enabled, they are. + */ + (void) initial_ier; /* avoid set but not used warning */ + (void) final_ier; /* avoid set but not used warning */ + #endif + + /* Has this channel been initialized? */ + if ( !CD2401_Channel_Info[ch].tty ) { + /* No, record as spurious interrupt */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].spur_cnt++; + final_ier = cd2401->ier &= 0xFC;/* Shut up, whoever you are */ + + cd2401->teoir = 0x88; /* EOI - Terminate buffer and no transfer */ + CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + return; + } + + if ( status & 0x80 ) { + /* + * Bus error occurred during DMA transfer. For now, just record. + * Get reason for DMA bus error and clear the report for the next + * occurrence + */ + buserr = pccchip2->SCC_error; + pccchip2->SCC_error = 0x01; + CD2401_Channel_Info[ch].buserr_type = + (vector << 24) | (buserr << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].buserr_addr = + (((uint32_t)cd2401->tcbadru) << 16) | cd2401->tcbadrl; + + cd2401->teoir = 0x80; /* EOI - terminate bad buffer */ + CD2401_RECORD_TX_ISR_BUSERR_INFO(( ch, status, initial_ier, buserr, + CD2401_Channel_Info[ch].buserr_type, + CD2401_Channel_Info[ch].buserr_addr )); + return; + } + + if ( status & 0x20 ) { + /* DMA done -- Turn off TxD int, turn on TxMpty */ + final_ier = cd2401->ier = (cd2401->ier & 0xFE) | 0x02; + if( status & 0x08 ) { + /* Transmit buffer B was released */ + CD2401_Channel_Info[ch].own_buf_B = TRUE; + } + else { + /* Transmit buffer A was released */ + CD2401_Channel_Info[ch].own_buf_A = TRUE; + } + CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].txEmpty )); + + /* This call can result in a call to cd2401_write() */ + rtems_termios_dequeue_characters ( + CD2401_Channel_Info[ch].tty, + CD2401_Channel_Info[ch].len ); + cd2401->teoir = 0x08; /* EOI - no data transfered */ + } + else if ( status & 0x02 ) { + /* TxEmpty */ + CD2401_Channel_Info[ch].txEmpty = TRUE; + final_ier = cd2401->ier &= 0xFD;/* Shut up the interrupts */ + cd2401->teoir = 0x08; /* EOI - no data transfered */ + CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier, + CD2401_Channel_Info[ch].txEmpty )); + } + else { + /* Why did we get a Tx interrupt? */ + CD2401_Channel_Info[ch].spur_dev = + (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr; + CD2401_Channel_Info[ch].spur_cnt++; + cd2401->teoir = 0x08; /* EOI - no data transfered */ + CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, 0xFF, + CD2401_Channel_Info[ch].spur_dev, + CD2401_Channel_Info[ch].spur_cnt )); + } +} + +/* + * termios callbacks + */ + +/* + * cd2401_firstOpen + * + * This is the first time that this minor device (channel) is opened. + * Complete the asynchronous initialization. + * + * Input parameters: + * major - device major number + * minor - channel number + * arg - pointer to a struct rtems_libio_open_close_args_t + * + * Output parameters: NONE + * + * Return value: IGNORED + */ +int cd2401_firstOpen( + int major, + int minor, + void *arg +) +{ + rtems_libio_open_close_args_t *args = arg; + rtems_libio_ioctl_args_t newarg; + struct termios termios; + rtems_status_code sc; + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* + * Set up the line with the specified parameters. The difficulty is that + * the line parameters are stored in the struct termios field of a + * struct rtems_termios_tty that is not defined in a public header file. + * Therefore, we do not have direct access to the termios passed in with + * arg. So we make a rtems_termios_ioctl() call to get a pointer to the + * termios structure. + * + * THIS KLUDGE MAY BREAK IN THE FUTURE! + * + * We could have made a tcgetattr() call if we had our fd. + */ + newarg.iop = args->iop; + newarg.command = TIOCGETA; + newarg.buffer = &termios; + sc = rtems_termios_ioctl (&newarg); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + + /* + * Turn off hardware flow control. It is a pain with 3-wire cables. + * The rtems_termios_ioctl() call below results in a call to + * cd2401_setAttributes to initialize the line. The caller will "wait" + * on the ttyMutex that it already owns; this is safe in RTEMS. + */ + termios.c_cflag |= CLOCAL; /* Ignore modem status lines */ + newarg.command = TIOCGETA; + sc = rtems_termios_ioctl (&newarg); + if (sc != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (sc); + + /* Mark that the channel as initialized */ + CD2401_Channel_Info[minor].tty = args->iop->data1; + + /* If the first of the four channels to open, set up the interrupts */ + if ( !Init_count++ ) { + /* Install the interrupt handlers */ + Prev_re_isr = (rtems_isr_entry) set_vector( cd2401_re_isr, 0x5C, 1 ); + Prev_modem_isr = (rtems_isr_entry) set_vector( cd2401_modem_isr, 0x5D, 1 ); + Prev_tx_isr = (rtems_isr_entry) set_vector( cd2401_tx_isr, 0x5E, 1 ); + Prev_rx_isr = (rtems_isr_entry) set_vector( cd2401_rx_isr, 0x5F, 1 ); + + cd2401_interrupts_initialize( TRUE ); + } + + CD2401_RECORD_FIRST_OPEN_INFO(( minor, Init_count )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_lastClose + * + * There are no more opened file descriptors to this device. Close it down. + * + * Input parameters: + * major - device major number + * minor - channel number + * arg - pointer to a struct rtems_libio_open_close_args_t + */ +int cd2401_lastClose( + int major, + int minor, + void *arg +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* Mark that the channel is no longer is use */ + CD2401_Channel_Info[minor].tty = NULL; + + /* If the last of the four channels to close, disable the interrupts */ + if ( !--Init_count ) { + cd2401_interrupts_initialize( FALSE ); + + /* De-install the interrupt handlers */ + set_vector( Prev_re_isr, 0x5C, 1 ); + set_vector( Prev_modem_isr, 0x5D, 1 ); + set_vector( Prev_tx_isr, 0x5E, 1 ); + set_vector( Prev_rx_isr, 0x5F, 1 ); + } + + CD2401_RECORD_LAST_CLOSE_INFO(( minor, Init_count )); + + rtems_interrupt_enable (level); + + /* return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_setAttributes + * + * Set up the selected channel of the CD2401 chip for doing asynchronous + * I/O with DMA. + * + * The chip must already have been initialized by cd2401_initialize(). + * + * This code was written for clarity. The code space it occupies could be + * reduced. The code could also be compiled with aggressive optimization + * turned on. + * + * Input parameters: + * minor - the selected channel + * t - the termios parameters + * + * Output parameters: NONE + * + * Return value: IGNORED + */ +int cd2401_setAttributes( + int minor, + const struct termios *t +) +{ + uint8_t csize, cstopb, parodd, parenb, ignpar, inpck; + uint8_t hw_flow_ctl, sw_flow_ctl, extra_flow_ctl; + uint8_t icrnl, igncr, inlcr, brkint, ignbrk, parmrk, istrip; + uint8_t need_reinitialization = FALSE; + uint8_t read_enabled; + uint16_t tx_period, rx_period; + uint32_t out_baud, in_baud; + rtems_interrupt_level level; + + /* Determine what the line parameters should be */ + + /* baud rates */ + out_baud = rtems_termios_baud_to_number(t->c_ospeed); + in_baud = rtems_termios_baud_to_number(t->c_ispeed); + + /* Number of bits per char */ + csize = 0x07; /* to avoid a warning */ + switch ( t->c_cflag & CSIZE ) { + case CS5: csize = 0x04; break; + case CS6: csize = 0x05; break; + case CS7: csize = 0x06; break; + case CS8: csize = 0x07; break; + } + + /* Parity */ + if ( t->c_cflag & PARODD ) + parodd = 0x80; /* Odd parity */ + else + parodd = 0; + + if ( t->c_cflag & PARENB ) + parenb = 0x40; /* Parity enabled on Tx and Rx */ + else + parenb = 0x00; /* No parity on Tx and Rx */ + + /* CD2401 IGNPAR and INPCK bits are inverted wrt POSIX standard? */ + if ( t->c_iflag & INPCK ) + ignpar = 0; /* Check parity on input */ + else + ignpar = 0x10; /* Do not check parity on input */ + if ( t->c_iflag & IGNPAR ) { + inpck = 0x03; /* Discard error character */ + parmrk = 0; + } else { + if ( t->c_iflag & PARMRK ) { + inpck = 0x01; /* Translate to 0xFF 0x00 <char> */ + parmrk = 0x04; + } else { + inpck = 0x01; /* Translate to 0x00 */ + parmrk = 0; + } + } + + /* Stop bits */ + if ( t->c_cflag & CSTOPB ) + cstopb = 0x04; /* Two stop bits */ + else + cstopb = 0x02; /* One stop bit */ + + /* Modem flow control */ + if ( t->c_cflag & CLOCAL ) + hw_flow_ctl = 0x04; /* Always assert RTS before Tx */ + else + hw_flow_ctl = 0x07; /* Always assert RTS before Tx, + wait for CTS and DSR */ + + /* XON/XOFF Tx flow control */ + if ( t->c_iflag & IXON ) { + sw_flow_ctl = 0x40; /* Tx in-band flow ctl enabled, wait for XON */ + extra_flow_ctl = 0x30; /* Eat XON/XOFF, XON/XOFF in SCHR1, SCHR2 */ + } + else { + sw_flow_ctl = 0; /* Tx in-band flow ctl disabled */ + extra_flow_ctl = 0; /* Pass on XON/XOFF */ + } + + /* CL/LF translation */ + if ( t->c_iflag & ICRNL ) + icrnl = 0x40; /* Map CR to NL on input */ + else + icrnl = 0; /* Pass on CR */ + if ( t->c_iflag & INLCR ) + inlcr = 0x20; /* Map NL to CR on input */ + else + inlcr = 0; /* Pass on NL */ + if ( t->c_iflag & IGNCR ) + igncr = 0x80; /* CR discarded on input */ + else + igncr = 0; + + /* Break handling */ + if ( t->c_iflag & IGNBRK ) { + ignbrk = 0x10; /* Ignore break on input */ + brkint = 0x08; + } else { + if ( t->c_iflag & BRKINT ) { + ignbrk = 0; /* Generate SIGINT (interrupt ) */ + brkint = 0; + } else { + ignbrk = 0; /* Convert to 0x00 */ + brkint = 0x08; + } + } + + /* Stripping */ + if ( t->c_iflag & ISTRIP ) + istrip = 0x80; /* Strip to 7 bits */ + else + istrip = 0; /* Leave as 8 bits */ + + rx_period = cd2401_bitrate_divisor( 20000000Ul, &in_baud ); + tx_period = cd2401_bitrate_divisor( 20000000Ul, &out_baud ); + + /* + * If this is the first time that the line characteristics are set up, then + * the device must be re-initialized. + * Also check if we need to change anything. It is preferable to not touch + * the device if nothing changes. As soon as we touch it, it tends to + * glitch. If anything changes, we reprogram all registers. This is + * harmless. + */ + if ( ( CD2401_Channel_Info[minor].tty == 0 ) || + ( cd2401->cor1 != (parodd | parenb | ignpar | csize) ) || + ( cd2401->cor2 != (sw_flow_ctl | hw_flow_ctl) ) || + ( cd2401->cor3 != (extra_flow_ctl | cstopb) ) || + ( cd2401->cor6 != (igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck) ) || + ( cd2401->cor7 != istrip ) || + ( cd2401->u1.async.schr1 != t->c_cc[VSTART] ) || + ( cd2401->u1.async.schr2 != t->c_cc[VSTOP] ) || + ( cd2401->rbpr != (unsigned char)rx_period ) || + ( cd2401->rcor != (unsigned char)(rx_period >> 8) ) || + ( cd2401->tbpr != (unsigned char)tx_period ) || + ( cd2401->tcor != ( (tx_period >> 3) & 0xE0 ) ) ) + need_reinitialization = TRUE; + + /* Write to the ports */ + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + read_enabled = cd2401->csr & 0x80 ? TRUE : FALSE; + + if ( (t->c_cflag & CREAD ? TRUE : FALSE ) != read_enabled ) { + /* Read enable status is changing */ + need_reinitialization = TRUE; + } + + if ( need_reinitialization ) { + /* + * Could not find a way to test whether the CD2401 was done transmitting. + * The TxEmpty interrupt does not seem to indicate that the FIFO is empty + * in DMA mode. So, just wait a while for output to drain. May not be + * enough, but it will have to do (should be long enough for 1 char at + * 9600 bsp)... + */ + cd2401_udelay( 2000L ); + + /* Clear channel */ + cd2401_chan_cmd (minor, 0x40, 1); + + cd2401->car = minor; /* Select channel */ + cd2401->cmr = 0x42; /* Interrupt Rx, DMA Tx, async mode */ + cd2401->cor1 = parodd | parenb | ignpar | csize; + cd2401->cor2 = sw_flow_ctl | hw_flow_ctl; + cd2401->cor3 = extra_flow_ctl | cstopb; + cd2401->cor4 = 0x0A; /* No DSR/DCD/CTS detect; FIFO threshold of 10 */ + cd2401->cor5 = 0x0A; /* No DSR/DCD/CTS detect; DTR threshold of 10 */ + cd2401->cor6 = igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck; + cd2401->cor7 = istrip; /* No LNext; ignore XON/XOFF if frame error; no tx translations */ + /* Special char 1: XON character */ + cd2401->u1.async.schr1 = t->c_cc[VSTART]; + /* special char 2: XOFF character */ + cd2401->u1.async.schr2 = t->c_cc[VSTOP]; + + /* + * Special chars 3 and 4, char range, LNext, RFAR[1..4] and CRC + * are unused, left as is. + */ + + /* Set baudrates for receiver and transmitter */ + cd2401->rbpr = (unsigned char)rx_period; + cd2401->rcor = (unsigned char)(rx_period >> 8); /* no DPLL */ + cd2401->tbpr = (unsigned char)tx_period; + cd2401->tcor = (tx_period >> 3) & 0xE0; /* no x1 ext clk, no loopback */ + + /* Timeout for 4 chars at 9600, 8 bits per char, 1 stop bit */ + cd2401->u2.w.rtpr = 0x04; /* NEED TO LOOK AT THIS LINE! */ + + if ( t->c_cflag & CREAD ) { + /* Re-initialize channel, enable rx and tx */ + cd2401_chan_cmd (minor, 0x2A, 1); + /* Enable rx data ints */ + cd2401->ier = 0x08; + } else { + /* Re-initialize channel, enable tx, disable rx */ + cd2401_chan_cmd (minor, 0x29, 1); + } + } + + CD2401_RECORD_SET_ATTRIBUTES_INFO(( minor, need_reinitialization, csize, + cstopb, parodd, parenb, ignpar, inpck, + hw_flow_ctl, sw_flow_ctl, extra_flow_ctl, + icrnl, igncr, inlcr, brkint, ignbrk, + parmrk, istrip, tx_period, rx_period, + out_baud, in_baud )); + + rtems_interrupt_enable (level); + + /* + * Looks like the CD2401 needs time to settle after initialization. Give it + * 10 ms. I don't really believe it, but if output resumes to quickly after + * this call, the first few characters are not right. + */ + if ( need_reinitialization ) + cd2401_udelay( 10000L ); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_startRemoreTx + * + * Defined as a callback, but it would appear that it is never called. The + * POSIX standard states that when the tcflow() function is called with the + * TCION action, the system wall transmit a START character. Presumably, + * tcflow() is called internally when IXOFF is set in the termios c_iflag + * field when the input buffer can accomodate enough characters. It should + * probably be called from fillBufferQueue(). Clearly, the function is also + * explicitly callable by user code. The action is clearly to send the START + * character, regardless of whether START/STOP flow control is in effect. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * PROPER START CHARACTER MUST BE PROGRAMMED IN SCHR1. + */ +int cd2401_startRemoteTx( + int minor +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + cd2401->stcr = 0x01; /* Send SCHR1 ahead of chars in FIFO */ + + CD2401_RECORD_START_REMOTE_TX_INFO(( minor )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_stopRemoteTx + * + * Defined as a callback, but it would appear that it is never called. The + * POSIX standard states that when the tcflow() function is called with the + * TCIOFF function, the system wall transmit a STOP character. Presumably, + * tcflow() is called internally when IXOFF is set in the termios c_iflag + * field as the input buffer is about to overflow. It should probably be + * called from rtems_termios_enqueue_raw_characters(). Clearly, the function + * is also explicitly callable by user code. The action is clearly to send + * the STOP character, regardless of whether START/STOP flow control is in + * effect. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * PROPER STOP CHARACTER MUST BE PROGRAMMED IN SCHR2. + */ +int cd2401_stopRemoteTx( + int minor +) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + cd2401->car = minor; /* Select channel */ + cd2401->stcr = 0x02; /* Send SCHR2 ahead of chars in FIFO */ + + CD2401_RECORD_STOP_REMOTE_TX_INFO(( minor )); + + rtems_interrupt_enable (level); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} + +/* + * cd2401_write + * + * Initiate DMA output. Termios guarantees that the buffer does not wrap + * around, so we can do DMA strait from the supplied buffer. + * + * Input parameters: + * minor - selected channel + * buf - output buffer + * len - number of chars to output + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * MUST BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED! + * The processor is placed at interrupt level CD2401_INT_LEVEL explicitly in + * console_write(). The processor is necessarily at interrupt level 1 in + * cd2401_tx_isr(). + */ +ssize_t cd2401_write( + int minor, + const char *buf, + size_t len +) +{ + if (len > 0) { + cd2401->car = minor; /* Select channel */ + + if ( (cd2401->dmabsts & 0x08) == 0 ) { + /* Next buffer is A. Wait for it to be ours. */ + while ( cd2401->atbsts & 0x01 ); + + CD2401_Channel_Info[minor].own_buf_A = FALSE; + CD2401_Channel_Info[minor].len = len; + CD2401_Channel_Info[minor].buf = buf; + cd2401->atbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 ); + cd2401->atbadrl = (uint16_t)( (uint32_t) buf ); + cd2401->atbcnt = len; + CD2401_RECORD_WRITE_INFO(( len, buf, 'A' )); + cd2401->atbsts = 0x03; /* CD2401 owns buffer, int when empty */ + } + else { + /* Next buffer is B. Wait for it to be ours. */ + while ( cd2401->btbsts & 0x01 ); + + CD2401_Channel_Info[minor].own_buf_B = FALSE; + CD2401_Channel_Info[minor].len = len; + CD2401_Channel_Info[minor].buf = buf; + cd2401->btbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 ); + cd2401->btbadrl = (uint16_t)( (uint32_t) buf ); + cd2401->btbcnt = len; + CD2401_RECORD_WRITE_INFO(( len, buf, 'B' )); + cd2401->btbsts = 0x03; /* CD2401 owns buffer, int when empty */ + } + /* Nuts -- Need TxD ints */ + CD2401_Channel_Info[minor].txEmpty = FALSE; + cd2401->ier |= 0x01; + } + + /* Return something */ + return len; +} + +#if 0 +/* + * cd2401_drainOutput + * + * Wait for the txEmpty indication on the specified channel. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * MUST NOT BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED! + * The txEmpty flag is set by the tx ISR. + * + * DOES NOT WORK! DO NOT ENABLE THIS CODE. THE CD2401 DOES NOT COOPERATE! + * The code is here to document that the output FIFO is NOT empty when + * the CD2401 reports that the Tx buffer is empty. + */ +int cd2401_drainOutput( + int minor +) +{ + CD2401_RECORD_DRAIN_OUTPUT_INFO(( CD2401_Channel_Info[minor].txEmpty, + CD2401_Channel_Info[minor].own_buf_A, + CD2401_Channel_Info[minor].own_buf_B )); + + while( ! (CD2401_Channel_Info[minor].txEmpty && + CD2401_Channel_Info[minor].own_buf_A && + CD2401_Channel_Info[minor].own_buf_B) ); + + /* Return something */ + return RTEMS_SUCCESSFUL; +} +#endif + +/* + * _167Bug_pollRead + * + * Read a character from the 167Bug console, and return it. Return -1 + * if there is no character in the input FIFO. + * + * Input parameters: + * minor - selected channel + * + * Output parameters: NONE + * + * Return value: char returned as positive signed int + * -1 if no character is present in the input FIFO. + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +int _167Bug_pollRead( + int minor +) +{ + int char_not_available; + unsigned char c; + rtems_interrupt_level previous_level; + + /* + * Redirection of .INSTAT does not work: 167-Bug crashes. + * Switch the input stream to the specified port. + * Make sure this is atomic code. + */ + rtems_interrupt_disable( previous_level ); + + __asm__ volatile( "movew %1, -(%%sp)\n\t"/* Channel */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x61\n\t" /* Code for .REDIR_I */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x01\n\t" /* Code for .INSTAT */ + "move %%cc, %0\n\t" /* Get condition codes */ + "andil #4, %0" /* Keep the Zero bit */ + : "=d" (char_not_available) : "d" (minor): "%%cc" ); + + if (char_not_available) { + rtems_interrupt_enable( previous_level ); + return -1; + } + + /* Read the char and return it */ + __asm__ volatile( "subq.l #2,%%a7\n\t" /* Space for result */ + "trap #15\n\t" /* Trap to 167 Bug */ + ".short 0x00\n\t" /* Code for .INCHR */ + "moveb (%%a7)+, %0" /* Pop char into c */ + : "=d" (c) : ); + + rtems_interrupt_enable( previous_level ); + + return (int)c; +} + +/* + * _167Bug_pollWrite + * + * Output buffer through 167Bug. Returns only once every character has been + * sent (polled output). + * + * Input parameters: + * minor - selected channel + * buf - output buffer + * len - number of chars to output + * + * Output parameters: NONE + * + * Return value: IGNORED + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +ssize_t _167Bug_pollWrite( + int minor, + const char *buf, + size_t len +) +{ + const char *endbuf = buf + len; + + __asm__ volatile( "pea (%0)\n\t" /* endbuf */ + "pea (%1)\n\t" /* buf */ + "movew #0x21, -(%%sp)\n\t" /* Code for .OUTSTR */ + "movew %2, -(%%sp)\n\t" /* Channel */ + "trap #15\n\t" /* Trap to 167Bug */ + ".short 0x60" /* Code for .REDIR */ + :: "a" (endbuf), "a" (buf), "d" (minor) ); + + /* Return something */ + return len; +} + +/* + * do_poll_read + * + * Input characters through 167Bug. Returns has soon as a character has been + * received. Otherwise, if we wait for the number of requested characters, we + * could be here forever! + * + * CR is converted to LF on input. The terminal should not send a CR/LF pair + * when the return or enter key is pressed. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel. + * arg->buffer - where to put the received characters. + * arg->count - number of characters to receive before returning--Ignored. + * + * Output parameters: + * arg->bytes_moved - the number of characters read. Always 1. + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + int c; + + while( (c = _167Bug_pollRead (minor)) == -1 ); + rw_args->buffer[0] = (uint8_t)c; + if( rw_args->buffer[0] == '\r' ) + rw_args->buffer[0] = '\n'; + rw_args->bytes_moved = 1; + return RTEMS_SUCCESSFUL; +} + +/* + * do_poll_write + * + * Output characters through 167Bug. Returns only once every character has + * been sent. + * + * CR is transmitted AFTER a LF on output. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel + * arg->buffer - where to get the characters to transmit. + * arg->count - the number of characters to transmit before returning. + * + * Output parameters: + * arg->bytes_moved - the number of characters read + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + uint32_t i; + + for( i = 0; i < rw_args->count; i++ ) { + _167Bug_pollWrite(minor, &(rw_args->buffer[i]), 1); + if ( rw_args->buffer[i] == '\n' ) + _167Bug_pollWrite(minor, &cr_char, 1); + } + rw_args->bytes_moved = i; + return RTEMS_SUCCESSFUL; +} + +/* + * _BSP_output_char + * + * printk() function prototyped in bspIo.h. Does not use termios. + */ +void _BSP_output_char(char c) +{ + rtems_device_minor_number printk_minor; + + /* + * Can't rely on console_initialize having been called before this function + * is used. + */ + if ( NVRAM_CONFIGURE ) + /* J1-4 is on, use NVRAM info for configuration */ + printk_minor = (nvram->console_printk_port & 0x30) >> 4; + else + printk_minor = PRINTK_MINOR; + + _167Bug_pollWrite(printk_minor, &c, 1); +} + +/* + *************** + * BOILERPLATE * + *************** + * + * All these functions are prototyped in rtems/c/src/lib/include/console.h. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + rtems_device_minor_number console_minor; + + /* + * Set up TERMIOS if needed + */ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + console_minor = nvram->console_printk_port & 0x03; + + if ( nvram->console_mode & 0x01 ) + /* termios */ + rtems_termios_initialize (); + } + else { + console_minor = CONSOLE_MINOR; +#if CD2401_USE_TERMIOS == 1 + rtems_termios_initialize (); +#endif + } + + /* + * Do device-specific initialization + * Does not affect 167-Bug. + */ + cd2401_initialize (); + + /* + * Register the devices + */ + status = rtems_io_register_name ("/dev/tty0", major, 0); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty1", major, 1); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/console", major, console_minor); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty2", major, 2); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty3", major, 3); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + _167Bug_pollRead, /* pollRead */ + _167Bug_pollWrite, /* write */ + NULL, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + static const rtems_termios_callbacks intrCallbacks = { + cd2401_firstOpen, /* firstOpen */ + cd2401_lastClose, /* lastClose */ + NULL, /* pollRead */ + cd2401_write, /* write */ + cd2401_setAttributes, /* setAttributes */ + cd2401_stopRemoteTx, /* stopRemoteTx */ + cd2401_startRemoteTx, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; + + if ( NVRAM_CONFIGURE ) + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + if ( nvram->console_mode & 0x02 ) + /* interrupt-driven I/O */ + return rtems_termios_open (major, minor, arg, &intrCallbacks); + else + /* polled I/O */ + return rtems_termios_open (major, minor, arg, &pollCallbacks); + else + /* no termios -- default to polled I/O */ + return RTEMS_SUCCESSFUL; +#if CD2401_USE_TERMIOS == 1 +#if CD2401_IO_MODE != 1 + else + /* termios & polled I/O*/ + return rtems_termios_open (major, minor, arg, &pollCallbacks); +#else + else + /* termios & interrupt-driven I/O*/ + return rtems_termios_open (major, minor, arg, &intrCallbacks); +#endif +#else + else + /* no termios -- default to polled I/O */ + return RTEMS_SUCCESSFUL; +#endif +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_close (arg); + else + /* no termios */ + return RTEMS_SUCCESSFUL; + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_close (arg); +#else + else + /* no termios */ + return RTEMS_SUCCESSFUL; +#endif +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_read (arg); + else + /* no termios -- default to polled */ + return do_poll_read (major, minor, arg); + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_read (arg); +#else + else + /* no termios -- default to polled */ + return do_poll_read (major, minor, arg); +#endif +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_write (arg); + else + /* no termios -- default to polled */ + return do_poll_write (major, minor, arg); + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_write (arg); +#else + else + /* no termios -- default to polled */ + return do_poll_write (major, minor, arg); +#endif +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + if ( NVRAM_CONFIGURE ) { + /* J1-4 is on, use NVRAM info for configuration */ + if ( nvram->console_mode & 0x01 ) + /* termios */ + return rtems_termios_ioctl (arg); + else + /* no termios -- default to polled */ + return RTEMS_SUCCESSFUL; + } +#if CD2401_USE_TERMIOS == 1 + else + /* termios */ + return rtems_termios_ioctl (arg); +#else + else + /* no termios -- default to polled */ + return RTEMS_SUCCESSFUL; +#endif +} |