Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:48

0001 /*
0002  *  This file contains the MVME167 termios console package. Only asynchronous
0003  *  I/O is supported.
0004  *
0005  *  /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M.
0006  *  /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M.
0007  *  /dev/tty2 is channel 2, Serial Port 3 on the MVME712M.
0008  *  /dev/tty3 is channel 3, Serial Port 4 on the MVME712M.
0009  *
0010  *  Normal I/O uses DMA for output, interrupts for input. /dev/console is
0011  *  fixed to be /dev/tty01, Serial Port 2. Very limited support is provided
0012  *  for polled I/O. Polled I/O is intended only for running the RTEMS test
0013  *  suites. In all cases, Serial Port 1/Console is allocated to 167Bug and
0014  *  is the dedicated debugger port. We configure GDB to use 167Bug for
0015  *  debugging. When debugging with GDB or 167Bug, do not open /dev/tty00.
0016  *
0017  *  Modern I/O chips often contain a number of I/O devices that can operate
0018  *  almost independently of each other. Typically, in RTEMS, all devices in
0019  *  an I/O chip are handled by a single device driver, but that need not be
0020  *  always the case. Each device driver must supply six entry points in the
0021  *  Device Driver Table: a device initialization function, as well as an open,
0022  *  close, read, write and a control function. RTEMS assigns a device major
0023  *  number to each device driver. This major device number is the index of the
0024  *  device driver entries in the Device Driver Table, and it used to identify
0025  *  a particular device driver. To distinguish multiple I/O sub-devices within
0026  *  an I/O chip, RTEMS supports device minor numbers. When a I/O device is
0027  *  initialized, the major number is supplied to the initialization function.
0028  *  That function must register each sub-device with a separate name and minor
0029  *  number (as well as the supplied major number). When an application opens a
0030  *  device by name, the corresponding major and minor numbers are returned to
0031  *  the caller to be used in subsequent I/O operations (although these details
0032  *  are typically hidden within the library functions).
0033  *
0034  *  Such a scheme recognizes that the initialization of the individual
0035  *  sub-devices is generally not completely independent. For example, the
0036  *  four serial ports of the CD2401 can be configured almost independently
0037  *  from each other. One port could be configured to operate in asynchronous
0038  *  mode with interrupt-driven I/O, while another port could be configured to
0039  *  operate in HDLC mode with DMA I/O. However, a device reset command will
0040  *  reset all four channels, and the width of DMA transfers and the number of
0041  *  retries following bus errors selected applies to all four channels.
0042  *  Consequently, when initializing one channel, one must be careful not to
0043  *  destroy the configuration of other channels that are already configured.
0044  *
0045  *  One problem with the RTEMS I/O initialization model is that no information
0046  *  other than a device major number is passed to the initialization function.
0047  *  Consequently, the sub-devices must be initialized with some pre-determined
0048  *  configuration. To change the configuration of a sub-device, it is
0049  *  necessary to either rewrite the initialization function, or to make a
0050  *  series of rtems_io_control() calls after initialization. The first
0051  *  approach is not very elegant. The second approach is acceptable if an
0052  *  application is simply changing baud rates, parity or other such
0053  *  asynchronous parameters (as supplied by the termios package). But what if
0054  *  an application requires one channel to run in HDLC or Bisync mode and
0055  *  another in async mode? With a single driver per I/O chip approach, the
0056  *  device driver must support multiple protocols. This is feasible, but it
0057  *  often means that an application that only does asynchronous I/O now links
0058  *  in code for other unused protocols, thus wasting precious ROM space.
0059  *  Worse, it requires that the sub-devices be initialized in some
0060  *  configuration, and that configuration then changed through a series of
0061  *  device driver control calls. There is no standard API in RTEMS to switch
0062  *  a serial line to some synchronous protocol.
0063  *
0064  *  A better approach is to treat each channel as a separate device, each with
0065  *  its own device device driver. The application then supplies its own device
0066  *  driver table with only the required protocols (drivers) on each line. The
0067  *  problem with this approach is that the device drivers are not really
0068  *  independent, given that the I/O sub-devices within a common chip are not
0069  *  independent themselves. Consequently, the related device drivers must
0070  *  share some information. In RTEMS, there is no standard location in which
0071  *  to share information.
0072  *
0073  *  This driver handles all four channels, i.e. it distinguishes the
0074  *  sub-devices using minor device numbers. Only asynchronous I/O is
0075  *  supported. The console is currently fixed to be channel 1 on the CD2401,
0076  *  which corresponds to the TTY01 port (Serial Port 2) on the MVME712M
0077  *  Transition Module.
0078  *
0079  *  The CD2401 does either interrupt-driven or DMA I/O; it does not support
0080  *  polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401
0081  *  are routed to the MC68040, and the processor generates an interrupt
0082  *  acknowledge cycle directly to the CD2401 to obtain an interrupt vector.
0083  *  The PCCchip2 supports a pseudo-polling mode in which interrupts from the
0084  *  CD2401 are not routed to the MC68040, but can be detected by the processor
0085  *  by reading the appropriate CD2401 registers. In this mode, interrupt
0086  *  acknowledge cycles must be generated to the CD2401 by reading the
0087  *  appropriate PCCchip2 registers.
0088  *
0089  *  Interrupts from the four channels cannot be routed independently; either
0090  *  all channels are used in the pseudo-polling mode, or all channels are used
0091  *  in interrupt-driven/DMA mode. There is no advantage in using the speudo-
0092  *  polling mode. Consenquently, this driver performs DMA input and output.
0093  *  Output is performed directly from the termios raw output buffer, while
0094  *  input is accumulated into a separate buffer.
0095  *
0096  *  THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from
0097  *  multiple tasks is likely to cause significant problems! Concurrency
0098  *  control is implemented in the termios package.
0099  *
0100  *  THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS.
0101  *  If the CD2401 is to be used for high speed synchronous serial I/O, the
0102  *  interrupt priority might need to be increased.
0103  *
0104  *  ALL INTERRUPT HANDLERS ARE SHARED.
0105  *  When adding extra device drivers, either rewrite the interrupt handlers
0106  *  to demultiplex the interrupts, or install separate vectors. Common vectors
0107  *  are currently used to catch spurious interrupts. We could already have
0108  *  installed separate vectors for each channel and used the spurious
0109  *  interrupt handler defined in some other BSPs, but handling spurious
0110  *  interrupts from the CD2401 in this device driver allows us to record more
0111  *  information on the source of the interrupts. Furthermore, we have observed
0112  *  the occasional spurious interrupt from channel 0. We definitely do not
0113  *  to call a debugger for those.
0114  *
0115  *  All page references are to the MVME166/MVME167/MVME187 Single Board
0116  *  Computer Programmer's Reference Guide (MVME187PG/D2) with the April
0117  *  1993 supplements/addenda (MVME187PG/D2A1).
0118  */
0119 
0120 /*
0121  *  Copyright (c) 1998, National Research Council of Canada
0122  *
0123  *  The license and distribution terms for this file may be
0124  *  found in the file LICENSE in this distribution or at
0125  *  http://www.rtems.org/license/LICENSE.
0126  */
0127 
0128 #define M167_INIT
0129 
0130 #include <stdarg.h>
0131 #include <stdio.h>
0132 #include <termios.h>
0133 
0134 #include <rtems/console.h>
0135 #include <rtems/libio.h>
0136 #include <rtems/termiostypes.h>
0137 #include <bsp.h>                /* Must be before libio.h */
0138 
0139 /* Utility functions */
0140 void cd2401_udelay( unsigned long delay );
0141 void cd2401_chan_cmd( uint8_t         channel, uint8_t         cmd, uint8_t         wait );
0142 uint16_t         cd2401_bitrate_divisor( uint32_t         clkrate, uint32_t        * bitrate );
0143 void cd2401_initialize( void );
0144 void cd2401_interrupts_initialize( bool enable );
0145 
0146 /* ISRs */
0147 rtems_isr cd2401_modem_isr( rtems_vector_number vector );
0148 rtems_isr cd2401_re_isr( rtems_vector_number vector );
0149 rtems_isr cd2401_rx_isr( rtems_vector_number vector );
0150 rtems_isr cd2401_tx_isr( rtems_vector_number vector );
0151 
0152 /* Termios callbacks */
0153 int cd2401_firstOpen( int major, int minor, void *arg );
0154 int cd2401_lastClose( int major, int minor, void *arg );
0155 int cd2401_setAttributes( int minor, const struct termios *t );
0156 int cd2401_startRemoteTx( int minor );
0157 int cd2401_stopRemoteTx( int minor );
0158 ssize_t cd2401_write( int minor, const char *buf, size_t len );
0159 int cd2401_drainOutput( int minor );
0160 int _167Bug_pollRead( int minor );
0161 ssize_t _167Bug_pollWrite( int minor, const char *buf, size_t len );
0162 
0163 /* Printk function */
0164 static void _BSP_output_char( char c );
0165 BSP_output_char_function_type     BSP_output_char = _BSP_output_char;
0166 BSP_polling_getchar_function_type BSP_poll_char = NULL;
0167 
0168 /* '\r' character in memory. This used to live on
0169  * the stack but storing the '\r' character is
0170  * optimized away by gcc-4.3.2 (since it seems to
0171  * be unused [only referenced from inline assembly
0172  * code in _167Bug_pollWrite()]).
0173  * Hence we make it a global constant.
0174  */
0175 static const char cr_char = '\r';
0176 
0177 /* Channel info */
0178 /* static */ volatile struct {
0179   void *tty;                    /* Really a struct rtems_termios_tty * */
0180   int len;                      /* Record nb of chars being TX'ed */
0181   const char *buf;              /* Record where DMA is coming from */
0182   uint32_t         spur_cnt;    /* Nb of spurious ints so far */
0183   uint32_t         spur_dev;    /* Indo on last spurious int */
0184   uint32_t         buserr_addr; /* Faulting address */
0185   uint32_t         buserr_type; /* Reason of bus error during DMA */
0186   uint8_t          own_buf_A;   /* If true, buffer A belongs to the driver */
0187   uint8_t          own_buf_B;   /* If true, buffer B belongs to the driver */
0188   uint8_t          txEmpty;     /* If true, the output FIFO should be empty */
0189 } CD2401_Channel_Info[4];
0190 
0191 /*
0192  *  The number of channels already opened. If zero, enable the interrupts. The
0193  *  initial value must be 0. If initialized explicitly, the variable ends up
0194  *  in the .data section. Its value is not re-initialized on system restart.
0195  *  Furthermore, because the variable is changed, the .data section would not
0196  *  be ROMable. We thus leave the variable uninitialized, which causes it to
0197  *  be allocated in the .bss section, and rely on RTEMS to zero the .bss
0198  *  section on every startup.
0199  */
0200 uint8_t         Init_count;
0201 
0202 /* Record previous handlers */
0203 rtems_isr_entry Prev_re_isr;        /* Previous rx exception isr */
0204 rtems_isr_entry Prev_rx_isr;        /* Previous rx isr */
0205 rtems_isr_entry Prev_tx_isr;        /* Previous tx isr */
0206 rtems_isr_entry Prev_modem_isr;     /* Previous modem/timer isr */
0207 
0208 /* Define the following symbol to trace the calls to this driver */
0209 /* #define CD2401_RECORD_DEBUG_INFO */
0210 #include "console-recording.h"
0211 
0212 /*
0213  *  Utility functions.
0214  */
0215 
0216 /*
0217  *  Assumes that clock ticks 1 million times per second.
0218  *
0219  *  MAXIMUM DELAY IS ABOUT 20 ms
0220  *
0221  *  Input parameters:
0222  *    delay: Number of microseconds to delay.
0223  *
0224  *  Output parameters: NONE
0225  *
0226  *  Return values: NONE
0227  */
0228  void cd2401_udelay
0229 (
0230   unsigned long delay
0231 )
0232 {
0233   unsigned long i = 20000;  /* In case clock is off */
0234   rtems_interval start_ticks, end_ticks, current_ticks;
0235 
0236   start_ticks = rtems_clock_get_ticks_since_boot();
0237   end_ticks = start_ticks + delay;
0238 
0239   do {
0240     current_ticks = rtems_clock_get_ticks_since_boot();
0241   } while ( --i && (current_ticks <= end_ticks) );
0242 
0243   CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i ));
0244 }
0245 
0246 /*
0247  *  cd2401_chan_cmd
0248  *
0249  *  Sends a CCR command to the specified channel. Waits for any unfinished
0250  *  previous command to complete, then sends the specified command. Optionally
0251  *  wait for the current command to finish before returning.
0252  *
0253  *  Input parameters:
0254  *    channel - CD2401 channel number
0255  *    cmd  - command byte
0256  *    wait - if non-zero, wait for specified command to complete before
0257  *          returning.
0258  *
0259  *  Output parameters: NONE
0260  *
0261  *  Return values: NONE
0262  */
0263 void cd2401_chan_cmd(
0264   uint8_t         channel,
0265   uint8_t         cmd,
0266   uint8_t         wait
0267 )
0268 {
0269   if ( channel < 4 ) {
0270     cd2401->car = channel;      /* Select channel */
0271 
0272     while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */
0273     cd2401->ccr = cmd;          /* Send command */
0274     if ( wait )
0275       while( cd2401->ccr != 0 );/* Wait for completion */
0276   }
0277   else {
0278     /* This may not be the best error message */
0279     rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
0280   }
0281 }
0282 
0283 /*
0284  *  cd2401_bitrate_divisor
0285  *
0286  *  Compute the divisor and clock source to use to obtain the desired bitrate.
0287  *
0288  *  Input parameters:
0289  *    clkrate - system clock rate (CLK input frequency)
0290  *    bitrate - the desired bitrate
0291  *
0292  *  Output parameters:
0293  *    bitrate - The actual bitrate achievable, to the nearest bps.
0294  *
0295  *  Return values:
0296  *    Returns divisor in lower byte and clock source in upper byte for the
0297  *    specified bitrate.
0298  */
0299 uint16_t         cd2401_bitrate_divisor(
0300   uint32_t         clkrate,
0301   uint32_t        * bitrate
0302 )
0303 {
0304   uint32_t         divisor;
0305   uint16_t         clksource;
0306 
0307   divisor = *bitrate << 3;          /* temporary; multiply by 8 for CLK/8 */
0308   divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */
0309 
0310   /* Use highest speed clock source for best precision - try clk0 to clk4 */
0311   for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 )
0312       divisor >>= 2;
0313   divisor--;                        /* adjustment, see specs */
0314   if( divisor < 1 )
0315     divisor = 1;
0316   else if( divisor > 0xFF )
0317     divisor = 0xFF;
0318   *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1);
0319   return( clksource | divisor );
0320 }
0321 
0322 /*
0323  *  cd2401_initialize
0324  *
0325  *  Initializes the CD2401 device. Individual channels on the chip are left in
0326  *  their default reset state, and should be subsequently configured.
0327  *
0328  *  Input parameters: NONE
0329  *
0330  *  Output parameters:  NONE
0331  *
0332  *  Return values: NONE
0333  */
0334 void cd2401_initialize( void )
0335 {
0336   int i;
0337 
0338   for ( i = 3; i >= 0; i-- ) {
0339     CD2401_Channel_Info[i].tty = NULL;
0340     CD2401_Channel_Info[i].len = 0;
0341     CD2401_Channel_Info[i].buf = NULL;
0342     CD2401_Channel_Info[i].spur_cnt = 0;
0343     CD2401_Channel_Info[i].spur_dev = 0;
0344     CD2401_Channel_Info[i].buserr_type = 0;
0345     CD2401_Channel_Info[i].buserr_addr = 0;
0346     CD2401_Channel_Info[i].own_buf_A = TRUE;
0347     CD2401_Channel_Info[i].own_buf_B = TRUE;
0348     CD2401_Channel_Info[i].txEmpty = TRUE;
0349   }
0350 
0351  /*
0352   *  Normally, do a device reset here. If we do it, we will most likely clober
0353   *  the port settings for 167Bug on channel 0. So we just shut up all the
0354   *  ports by disabling their interrupts.
0355   */
0356 #if 0
0357   cd2401->gfrcr = 0;            /* So we can detect that device init is done */
0358   cd2401_chan_cmd( 0x10, 0);    /* Reset all */
0359   while(cd2401->gfrcr == 0);    /* Wait for reset all */
0360 #endif
0361 
0362   /*
0363    *  The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that
0364    *  the LICR "contains the number of the interrupting channel being served.
0365    *  The channel number is always that of the current acknowledged interrupt."
0366    *  THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically
0367    *  by the hardware, as suggested by the manual.
0368    *
0369    *  The updated manual (part no 542400-007) has the story straight. The
0370    *  CD2401 automatically initializes the LICR to contain the channel number
0371    *  in bits 2 and 3. However, these bits are not preserved when the user
0372    *  defined bits are written.
0373    *
0374    *  The same vector number is used for all four channels. Different vector
0375    *  numbers could be programmed for each channel, thus avoiding the need to
0376    *  demultiplex the interrupts in the ISR.
0377    */
0378   for ( i = 0; i < 4; i++ ) {
0379     cd2401->car = i;            /* Select channel */
0380     cd2401->livr = 0x5C;        /* Motorola suggested value p. 3-15 */
0381     cd2401->licr = i << 2;      /* Don't rely on reset value */
0382     cd2401->ier = 0;            /* Disable all interrupts */
0383   }
0384 
0385   /*
0386    *  The content of the CD2401 xpilr registers must match the A7-A0 addresses
0387    *  generated by the PCCchip2 during interrupt acknowledge cycles in order
0388    *  for the CD2401 to recognize the IACK cycle and clear its interrupt
0389    *  request.
0390    */
0391   cd2401->mpilr = 0x01;         /* Match pccchip2->modem_piack p. 3-27 */
0392   cd2401->tpilr = 0x02;         /* Match pccchip2->tx_piack p. 3-28 */
0393   cd2401->rpilr = 0x03;         /* Match pccchip2->rx_piack p. 3-29 */
0394 
0395   /* Global CD2401 registers */
0396   cd2401->dmr = 0;              /* 16-bit DMA transfers when possible */
0397   cd2401->bercnt = 0;           /* Do not retry DMA upon bus errors */
0398 
0399   /*
0400    *  Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout
0401    *  and tx delay). The prescaler is clocked by the system clock) / 2048. The
0402    *  register must be in the range 0x0A..0xFF, ie. a rescaler period range of
0403    *  about 1ms..26ms for a nominal system clock rate  of 20MHz.
0404    */
0405   cd2401->tpr  = 0x0A;          /* Same value as 167Bug */
0406 }
0407 
0408 /*
0409  *  cd2401_interrupts_initialize
0410  *
0411  *  This routine enables or disables the CD2401 interrupts to the MC68040.
0412  *  Interrupts cannot be enabled/disabled on a per-channel basis.
0413  *
0414  *  Input parameters:
0415  *    enable - if true, enable the interrupts, else disable them.
0416  *
0417  *  Output parameters:  NONE
0418  *
0419  *  Return values: NONE
0420  *
0421  *  THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS.
0422  *  THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS.
0423  */
0424 void cd2401_interrupts_initialize(
0425   bool enable
0426 )
0427 {
0428   if ( enable ) {
0429    /*
0430     *  Enable interrupts from the CD2401 in the PCCchip2.
0431     *  During DMA transfers, the MC68040 supplies dirty data during read cycles
0432     *  from the CD2401 and leaves the data dirty in its data cache if there is
0433     *  a cache hit. The MC68040 updates the data cache during write cycles from
0434     *  the CD2401 if there is a cache hit.
0435     */
0436     pccchip2->SCC_error = 0x01;
0437     pccchip2->SCC_modem_int_ctl = 0x10 | CD2401_INT_LEVEL;
0438     pccchip2->SCC_tx_int_ctl = 0x10 | CD2401_INT_LEVEL;
0439     pccchip2->SCC_rx_int_ctl = 0x50 | CD2401_INT_LEVEL;
0440 
0441     pccchip2->gen_control |= 0x02;      /* Enable pccchip2 interrupts */
0442   }
0443   else {
0444     /* Disable interrupts */
0445     pccchip2->SCC_modem_int_ctl &= 0xEF;
0446     pccchip2->SCC_tx_int_ctl &= 0xEF;
0447     pccchip2->SCC_rx_int_ctl &= 0xEF;
0448   }
0449 }
0450 
0451 /* ISRs */
0452 
0453 /*
0454  *  cd2401_modem_isr
0455  *
0456  *  Modem/timer interrupt (group 1) from CD2401. These are not used, and not
0457  *  expected. Record as spurious and clear.
0458  *
0459  *  Input parameters:
0460  *    vector - vector number
0461  *
0462  *  Output parameters: NONE
0463  *
0464  *  Return values: NONE
0465  */
0466 rtems_isr cd2401_modem_isr(
0467   rtems_vector_number vector
0468 )
0469 {
0470   uint8_t         ch;
0471 
0472   /* Get interrupting channel ID */
0473   ch = cd2401->licr >> 2;
0474 
0475   /* Record interrupt info for debugging */
0476   CD2401_Channel_Info[ch].spur_dev =
0477       (vector << 24) | (cd2401->stk << 16) | (cd2401->mir << 8) | cd2401->misr;
0478   CD2401_Channel_Info[ch].spur_cnt++;
0479 
0480   cd2401->meoir = 0;            /* EOI */
0481   CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO(( ch,
0482                                           CD2401_Channel_Info[ch].spur_dev,
0483                                           CD2401_Channel_Info[ch].spur_cnt ));
0484 }
0485 
0486 /*
0487  *  cd2401_re_isr
0488  *
0489  *  RX exception interrupt (group 3, receiver exception) from CD2401. These are
0490  *  not used, and not expected. Record as spurious and clear.
0491  *
0492  *  FIX THIS ISR TO DETECT BREAK CONDITIONS AND RAISE SIGINT
0493  *
0494  *  Input parameters:
0495  *    vector - vector number
0496  *
0497  *  Output parameters: NONE
0498  *
0499  *  Return values: NONE
0500  */
0501 rtems_isr cd2401_re_isr(
0502   rtems_vector_number vector
0503 )
0504 {
0505   uint8_t         ch;
0506 
0507   /* Get interrupting channel ID */
0508   ch = cd2401->licr >> 2;
0509 
0510   /* Record interrupt info for debugging */
0511   CD2401_Channel_Info[ch].spur_dev =
0512       (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl;
0513   CD2401_Channel_Info[ch].spur_cnt++;
0514 
0515   if ( cd2401->u5.b.risrl & 0x80 )  /* Timeout interrupt? */
0516     cd2401->ier &= 0xDF;            /* Disable rx timeout interrupt */
0517   cd2401->reoir = 0x08;             /* EOI; exception char not read */
0518   CD2401_RECORD_RE_ISR_SPURIOUS_INFO(( ch,
0519                                        CD2401_Channel_Info[ch].spur_dev,
0520                                        CD2401_Channel_Info[ch].spur_cnt ));
0521 }
0522 
0523 /*
0524  *  cd2401_rx_isr
0525  *
0526  *  RX interrupt (group 3, receiver data) from CD2401.
0527  *
0528  *  Input parameters:
0529  *     vector - vector number
0530  *
0531  *  Output parameters: NONE
0532  *
0533  *  Return values: NONE
0534  */
0535 rtems_isr cd2401_rx_isr(
0536   rtems_vector_number vector
0537 )
0538 {
0539   char c;
0540   uint8_t         ch, status, nchars, total;
0541   #ifdef CD2401_RECORD_DEBUG_INFO
0542     uint8_t i = 0;
0543     char    buffer[256];
0544   #endif
0545 
0546   (void) total; /* avoid set but not used warnings when not recording info */
0547 
0548   status = cd2401->u5.b.risrl;
0549   ch = cd2401->licr >> 2;
0550 
0551   /* Has this channel been initialized or is it a condition we ignore? */
0552   if ( CD2401_Channel_Info[ch].tty && !status ) {
0553     /* Normal Rx Int, read chars, enqueue them, and issue EOI */
0554     total = nchars = cd2401->rfoc;  /* Nb of chars to retrieve from rx FIFO */
0555     while ( nchars-- > 0 ) {
0556       c = (char)cd2401->dr;         /* Next char in rx FIFO */
0557       rtems_termios_enqueue_raw_characters( CD2401_Channel_Info[ch].tty ,&c, 1 );
0558       #ifdef CD2401_RECORD_DEBUG_INFO
0559     buffer[i++] = c;
0560       #endif
0561     }
0562     cd2401->reoir = 0;              /* EOI */
0563     CD2401_RECORD_RX_ISR_INFO(( ch, total, buffer ));
0564   } else {
0565     /* No, record as spurious interrupt */
0566     CD2401_Channel_Info[ch].spur_dev =
0567         (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl;
0568     CD2401_Channel_Info[ch].spur_cnt++;
0569     cd2401->reoir = 0x04;           /* EOI - character not read */
0570     CD2401_RECORD_RX_ISR_SPURIOUS_INFO(( ch, status,
0571                                          CD2401_Channel_Info[ch].spur_dev,
0572                                          CD2401_Channel_Info[ch].spur_cnt ));
0573   }
0574 }
0575 
0576 /*
0577  *  cd2401_tx_isr
0578  *
0579  *  TX interrupt (group 2) from CD2401.
0580  *
0581  *  Input parameters:
0582  *    vector - vector number
0583  *
0584  *  Output parameters: NONE
0585  *
0586  *  Return values: NONE
0587  */
0588 rtems_isr cd2401_tx_isr(
0589   rtems_vector_number vector
0590 )
0591 {
0592   uint8_t         ch, status, buserr, initial_ier, final_ier;
0593 
0594   status = cd2401->tisr;
0595   ch = cd2401->licr >> 2;
0596   initial_ier = cd2401->ier;
0597 
0598   #ifndef CD2401_RECORD_DEBUG_INFO
0599     /*
0600      * When the debug is disabled, these variables are really not read.
0601      * But when debug is enabled, they are.
0602      */
0603     (void) initial_ier; /* avoid set but not used warning */
0604     (void) final_ier; /* avoid set but not used warning */
0605   #endif
0606 
0607   /* Has this channel been initialized? */
0608   if ( !CD2401_Channel_Info[ch].tty ) {
0609     /* No, record as spurious interrupt */
0610     CD2401_Channel_Info[ch].spur_dev =
0611         (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr;
0612     CD2401_Channel_Info[ch].spur_cnt++;
0613     final_ier = cd2401->ier &= 0xFC;/* Shut up, whoever you are */
0614 
0615     cd2401->teoir = 0x88;           /* EOI - Terminate buffer and no transfer */
0616     CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, final_ier,
0617                                          CD2401_Channel_Info[ch].spur_dev,
0618                                          CD2401_Channel_Info[ch].spur_cnt ));
0619     return;
0620   }
0621 
0622   if ( status & 0x80 ) {
0623     /*
0624      *  Bus error occurred during DMA transfer. For now, just record.
0625      *  Get reason for DMA bus error and clear the report for the next
0626      *  occurrence
0627      */
0628     buserr = pccchip2->SCC_error;
0629     pccchip2->SCC_error = 0x01;
0630     CD2401_Channel_Info[ch].buserr_type =
0631          (vector << 24) | (buserr << 16) | (cd2401->tir << 8) | cd2401->tisr;
0632     CD2401_Channel_Info[ch].buserr_addr =
0633         (((uint32_t)cd2401->tcbadru) << 16) | cd2401->tcbadrl;
0634 
0635     cd2401->teoir = 0x80;           /* EOI - terminate bad buffer */
0636     CD2401_RECORD_TX_ISR_BUSERR_INFO(( ch, status, initial_ier, buserr,
0637                                        CD2401_Channel_Info[ch].buserr_type,
0638                                        CD2401_Channel_Info[ch].buserr_addr ));
0639     return;
0640   }
0641 
0642   if ( status & 0x20 ) {
0643     /* DMA done -- Turn off TxD int, turn on TxMpty */
0644     final_ier = cd2401->ier = (cd2401->ier & 0xFE) | 0x02;
0645     if( status & 0x08 ) {
0646       /* Transmit buffer B was released */
0647       CD2401_Channel_Info[ch].own_buf_B = TRUE;
0648     }
0649     else {
0650       /* Transmit buffer A was released */
0651       CD2401_Channel_Info[ch].own_buf_A = TRUE;
0652     }
0653     CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier,
0654                                 CD2401_Channel_Info[ch].txEmpty ));
0655 
0656     /* This call can result in a call to cd2401_write() */
0657     rtems_termios_dequeue_characters (
0658         CD2401_Channel_Info[ch].tty,
0659         CD2401_Channel_Info[ch].len );
0660     cd2401->teoir = 0x08;           /* EOI - no data transfered */
0661   }
0662   else if ( status & 0x02 ) {
0663     /* TxEmpty */
0664     CD2401_Channel_Info[ch].txEmpty = TRUE;
0665     final_ier = cd2401->ier &= 0xFD;/* Shut up the interrupts */
0666     cd2401->teoir = 0x08;           /* EOI - no data transfered */
0667     CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier,
0668                                 CD2401_Channel_Info[ch].txEmpty ));
0669   }
0670   else {
0671     /* Why did we get a Tx interrupt? */
0672     CD2401_Channel_Info[ch].spur_dev =
0673         (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr;
0674     CD2401_Channel_Info[ch].spur_cnt++;
0675     cd2401->teoir = 0x08;           /* EOI - no data transfered */
0676     CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, 0xFF,
0677                                          CD2401_Channel_Info[ch].spur_dev,
0678                                          CD2401_Channel_Info[ch].spur_cnt ));
0679   }
0680 }
0681 
0682 /*
0683  *  termios callbacks
0684  */
0685 
0686 /*
0687  *  cd2401_firstOpen
0688  *
0689  *  This is the first time that this minor device (channel) is opened.
0690  *  Complete the asynchronous initialization.
0691  *
0692  *  Input parameters:
0693  *    major - device major number
0694  *    minor - channel number
0695  *    arg - pointer to a struct rtems_libio_open_close_args_t
0696  *
0697  *  Output parameters: NONE
0698  *
0699  *  Return value: IGNORED
0700  */
0701 int cd2401_firstOpen(
0702   int major,
0703   int minor,
0704   void *arg
0705 )
0706 {
0707   rtems_libio_open_close_args_t *args = arg;
0708   rtems_libio_ioctl_args_t newarg;
0709   struct termios termios;
0710   rtems_status_code sc;
0711   rtems_interrupt_level level;
0712 
0713   rtems_interrupt_disable (level);
0714 
0715   /*
0716    * Set up the line with the specified parameters. The difficulty is that
0717    * the line parameters are stored in the struct termios field of a
0718    * struct rtems_termios_tty that is not defined in a public header file.
0719    * Therefore, we do not have direct access to the termios passed in with
0720    * arg. So we make a rtems_termios_ioctl() call to get a pointer to the
0721    * termios structure.
0722    *
0723    * THIS KLUDGE MAY BREAK IN THE FUTURE!
0724    *
0725    * We could have made a tcgetattr() call if we had our fd.
0726    */
0727   newarg.iop = args->iop;
0728   newarg.command = TIOCGETA;
0729   newarg.buffer = &termios;
0730   sc = rtems_termios_ioctl (&newarg);
0731   if (sc != RTEMS_SUCCESSFUL)
0732     rtems_fatal_error_occurred (sc);
0733 
0734   /*
0735    *  Turn off hardware flow control. It is a pain with 3-wire cables.
0736    *  The rtems_termios_ioctl() call below results in a call to
0737    *  cd2401_setAttributes to initialize the line. The caller will "wait"
0738    *  on the ttyMutex that it already owns; this is safe in RTEMS.
0739    */
0740   termios.c_cflag |= CLOCAL;    /* Ignore modem status lines */
0741   newarg.command = TIOCGETA;
0742   sc = rtems_termios_ioctl (&newarg);
0743   if (sc != RTEMS_SUCCESSFUL)
0744     rtems_fatal_error_occurred (sc);
0745 
0746   /* Mark that the channel as initialized */
0747   CD2401_Channel_Info[minor].tty = args->iop->data1;
0748 
0749   /* If the first of the four channels to open, set up the interrupts */
0750   if ( !Init_count++ ) {
0751     /* Install the interrupt handlers */
0752     Prev_re_isr    = (rtems_isr_entry) set_vector( cd2401_re_isr,    0x5C, 1 );
0753     Prev_modem_isr = (rtems_isr_entry) set_vector( cd2401_modem_isr, 0x5D, 1 );
0754     Prev_tx_isr    = (rtems_isr_entry) set_vector( cd2401_tx_isr,    0x5E, 1 );
0755     Prev_rx_isr    = (rtems_isr_entry) set_vector( cd2401_rx_isr,    0x5F, 1 );
0756 
0757     cd2401_interrupts_initialize( TRUE );
0758   }
0759 
0760   CD2401_RECORD_FIRST_OPEN_INFO(( minor, Init_count ));
0761 
0762   rtems_interrupt_enable (level);
0763 
0764   /* Return something */
0765   return RTEMS_SUCCESSFUL;
0766 }
0767 
0768 /*
0769  * cd2401_lastClose
0770  *
0771  *  There are no more opened file descriptors to this device. Close it down.
0772  *
0773  *  Input parameters:
0774  *    major - device major number
0775  *    minor - channel number
0776  *    arg - pointer to a struct rtems_libio_open_close_args_t
0777  */
0778 int cd2401_lastClose(
0779   int major,
0780   int minor,
0781   void *arg
0782 )
0783 {
0784   rtems_interrupt_level level;
0785 
0786   rtems_interrupt_disable (level);
0787 
0788   /* Mark that the channel is no longer is use */
0789   CD2401_Channel_Info[minor].tty = NULL;
0790 
0791   /* If the last of the four channels to close, disable the interrupts */
0792   if ( !--Init_count ) {
0793     cd2401_interrupts_initialize( FALSE );
0794 
0795     /* De-install the interrupt handlers */
0796     set_vector( Prev_re_isr,    0x5C, 1 );
0797     set_vector( Prev_modem_isr, 0x5D, 1 );
0798     set_vector( Prev_tx_isr,    0x5E, 1 );
0799     set_vector( Prev_rx_isr,    0x5F, 1 );
0800   }
0801 
0802   CD2401_RECORD_LAST_CLOSE_INFO(( minor, Init_count ));
0803 
0804   rtems_interrupt_enable (level);
0805 
0806   /* return something */
0807   return RTEMS_SUCCESSFUL;
0808 }
0809 
0810 /*
0811  *  cd2401_setAttributes
0812  *
0813  *  Set up the selected channel of the CD2401 chip for doing asynchronous
0814  *  I/O with DMA.
0815  *
0816  *  The chip must already have been initialized by cd2401_initialize().
0817  *
0818  *  This code was written for clarity. The code space it occupies could be
0819  *  reduced. The code could also be compiled with aggressive optimization
0820  *  turned on.
0821  *
0822  *  Input parameters:
0823  *    minor - the selected channel
0824  *    t - the termios parameters
0825  *
0826  *  Output parameters: NONE
0827  *
0828  *  Return value: IGNORED
0829  */
0830 int cd2401_setAttributes(
0831   int minor,
0832   const struct termios *t
0833 )
0834 {
0835   uint8_t         csize, cstopb, parodd, parenb, ignpar, inpck;
0836   uint8_t         hw_flow_ctl, sw_flow_ctl, extra_flow_ctl;
0837   uint8_t         icrnl, igncr, inlcr, brkint, ignbrk, parmrk, istrip;
0838   uint8_t         need_reinitialization = FALSE;
0839   uint8_t         read_enabled;
0840   uint16_t         tx_period, rx_period;
0841   uint32_t         out_baud, in_baud;
0842   rtems_interrupt_level level;
0843 
0844   /* Determine what the line parameters should be */
0845 
0846   /* baud rates */
0847   out_baud = rtems_termios_baud_to_number(t->c_ospeed);
0848   in_baud  = rtems_termios_baud_to_number(t->c_ispeed);
0849 
0850   /* Number of bits per char */
0851   csize = 0x07; /* to avoid a warning */
0852   switch ( t->c_cflag & CSIZE ) {
0853     case CS5:     csize = 0x04;       break;
0854     case CS6:     csize = 0x05;       break;
0855     case CS7:     csize = 0x06;       break;
0856     case CS8:     csize = 0x07;       break;
0857   }
0858 
0859   /* Parity */
0860   if ( t->c_cflag & PARODD )
0861     parodd = 0x80;              /* Odd parity */
0862   else
0863     parodd = 0;
0864 
0865   if ( t->c_cflag & PARENB )
0866     parenb = 0x40;              /* Parity enabled on Tx and Rx */
0867   else
0868     parenb = 0x00;              /* No parity on Tx and Rx */
0869 
0870   /* CD2401 IGNPAR and INPCK bits are inverted wrt POSIX standard? */
0871   if ( t->c_iflag & INPCK )
0872     ignpar = 0;                 /* Check parity on input */
0873   else
0874     ignpar = 0x10;              /* Do not check parity on input */
0875   if ( t->c_iflag & IGNPAR ) {
0876     inpck = 0x03;               /* Discard error character */
0877     parmrk = 0;
0878   } else {
0879     if ( t->c_iflag & PARMRK ) {
0880       inpck = 0x01;             /* Translate to 0xFF 0x00 <char> */
0881       parmrk = 0x04;
0882     } else {
0883       inpck = 0x01;             /* Translate to 0x00 */
0884       parmrk = 0;
0885     }
0886   }
0887 
0888   /* Stop bits */
0889   if ( t->c_cflag & CSTOPB )
0890     cstopb = 0x04;              /* Two stop bits */
0891   else
0892     cstopb = 0x02;              /* One stop bit */
0893 
0894   /* Modem flow control */
0895   if ( t->c_cflag & CLOCAL )
0896     hw_flow_ctl = 0x04;         /* Always assert RTS before Tx */
0897   else
0898     hw_flow_ctl = 0x07;         /* Always assert RTS before Tx,
0899                                    wait for CTS and DSR */
0900 
0901   /* XON/XOFF Tx flow control */
0902   if ( t->c_iflag & IXON ) {
0903     sw_flow_ctl = 0x40;         /* Tx in-band flow ctl enabled, wait for XON */
0904     extra_flow_ctl = 0x30;      /* Eat XON/XOFF, XON/XOFF in SCHR1, SCHR2 */
0905   }
0906   else {
0907     sw_flow_ctl = 0;            /* Tx in-band flow ctl disabled */
0908     extra_flow_ctl = 0;         /* Pass on XON/XOFF */
0909   }
0910 
0911   /* CL/LF translation */
0912   if ( t->c_iflag & ICRNL )
0913     icrnl = 0x40;               /* Map CR to NL on input */
0914   else
0915     icrnl = 0;                  /* Pass on CR */
0916   if ( t->c_iflag & INLCR )
0917     inlcr = 0x20;               /* Map NL to CR on input */
0918   else
0919     inlcr = 0;                  /* Pass on NL */
0920   if ( t->c_iflag & IGNCR )
0921     igncr = 0x80;               /* CR discarded on input */
0922   else
0923     igncr = 0;
0924 
0925   /* Break handling */
0926   if ( t->c_iflag & IGNBRK ) {
0927     ignbrk = 0x10;              /* Ignore break on input */
0928     brkint = 0x08;
0929   } else {
0930     if ( t->c_iflag & BRKINT ) {
0931       ignbrk = 0;               /* Generate SIGINT (interrupt ) */
0932       brkint = 0;
0933     } else {
0934       ignbrk = 0;               /* Convert to 0x00 */
0935       brkint = 0x08;
0936     }
0937   }
0938 
0939   /* Stripping */
0940   if ( t->c_iflag & ISTRIP )
0941     istrip = 0x80;              /* Strip to 7 bits */
0942   else
0943     istrip = 0;                 /* Leave as 8 bits */
0944 
0945   rx_period = cd2401_bitrate_divisor( 20000000Ul, &in_baud );
0946   tx_period = cd2401_bitrate_divisor( 20000000Ul, &out_baud );
0947 
0948   /*
0949    *  If this is the first time that the line characteristics are set up, then
0950    *  the device must be re-initialized.
0951    *  Also check if we need to change anything. It is preferable to not touch
0952    *  the device if nothing changes. As soon as we touch it, it tends to
0953    *  glitch. If anything changes, we reprogram all registers. This is
0954    *  harmless.
0955    */
0956   if ( ( CD2401_Channel_Info[minor].tty == 0 ) ||
0957        ( cd2401->cor1 != (parodd | parenb | ignpar | csize) ) ||
0958        ( cd2401->cor2 != (sw_flow_ctl | hw_flow_ctl) ) ||
0959        ( cd2401->cor3 != (extra_flow_ctl | cstopb) )  ||
0960        ( cd2401->cor6 != (igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck) ) ||
0961        ( cd2401->cor7 != istrip ) ||
0962        ( cd2401->u1.async.schr1 != t->c_cc[VSTART] ) ||
0963        ( cd2401->u1.async.schr2 != t->c_cc[VSTOP] ) ||
0964        ( cd2401->rbpr != (unsigned char)rx_period ) ||
0965        ( cd2401->rcor != (unsigned char)(rx_period >> 8) ) ||
0966        ( cd2401->tbpr != (unsigned char)tx_period ) ||
0967        ( cd2401->tcor != ( (tx_period >> 3) & 0xE0 ) ) )
0968     need_reinitialization = TRUE;
0969 
0970   /* Write to the ports */
0971   rtems_interrupt_disable (level);
0972 
0973   cd2401->car = minor;          /* Select channel */
0974   read_enabled = cd2401->csr & 0x80 ? TRUE : FALSE;
0975 
0976   if ( (t->c_cflag & CREAD ? TRUE : FALSE ) != read_enabled ) {
0977     /* Read enable status is changing */
0978     need_reinitialization = TRUE;
0979   }
0980 
0981   if ( need_reinitialization ) {
0982     /*
0983      *  Could not find a way to test whether the CD2401 was done transmitting.
0984      *  The TxEmpty interrupt does not seem to indicate that the FIFO is empty
0985      *  in DMA mode. So, just wait a while for output to drain. May not be
0986      *  enough, but it will have to do (should be long enough for 1 char at
0987      *  9600 bsp)...
0988      */
0989     cd2401_udelay( 2000L );
0990 
0991     /* Clear channel */
0992     cd2401_chan_cmd (minor, 0x40, 1);
0993 
0994     cd2401->car = minor;    /* Select channel */
0995     cd2401->cmr = 0x42;     /* Interrupt Rx, DMA Tx, async mode */
0996     cd2401->cor1 = parodd | parenb | ignpar | csize;
0997     cd2401->cor2 = sw_flow_ctl | hw_flow_ctl;
0998     cd2401->cor3 = extra_flow_ctl | cstopb;
0999     cd2401->cor4 = 0x0A;    /* No DSR/DCD/CTS detect; FIFO threshold of 10 */
1000     cd2401->cor5 = 0x0A;    /* No DSR/DCD/CTS detect; DTR threshold of 10 */
1001     cd2401->cor6 = igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck;
1002     cd2401->cor7 = istrip;  /* No LNext; ignore XON/XOFF if frame error; no tx translations */
1003     /* Special char 1: XON character */
1004     cd2401->u1.async.schr1 = t->c_cc[VSTART];
1005     /* special char 2: XOFF character */
1006     cd2401->u1.async.schr2 = t->c_cc[VSTOP];
1007 
1008     /*
1009      *  Special chars 3 and 4, char range, LNext, RFAR[1..4] and CRC
1010      *  are unused, left as is.
1011      */
1012 
1013     /* Set baudrates for receiver and transmitter */
1014     cd2401->rbpr = (unsigned char)rx_period;
1015     cd2401->rcor = (unsigned char)(rx_period >> 8); /* no DPLL */
1016     cd2401->tbpr = (unsigned char)tx_period;
1017     cd2401->tcor = (tx_period >> 3) & 0xE0; /* no x1 ext clk, no loopback */
1018 
1019     /* Timeout for 4 chars at 9600, 8 bits per char, 1 stop bit */
1020     cd2401->u2.w.rtpr  = 0x04;  /* NEED TO LOOK AT THIS LINE! */
1021 
1022     if ( t->c_cflag & CREAD ) {
1023       /* Re-initialize channel, enable rx and tx */
1024       cd2401_chan_cmd (minor, 0x2A, 1);
1025       /* Enable rx data ints */
1026       cd2401->ier = 0x08;
1027     } else {
1028       /* Re-initialize channel, enable tx, disable rx */
1029       cd2401_chan_cmd (minor, 0x29, 1);
1030     }
1031   }
1032 
1033   CD2401_RECORD_SET_ATTRIBUTES_INFO(( minor, need_reinitialization, csize,
1034                                       cstopb, parodd, parenb, ignpar, inpck,
1035                                       hw_flow_ctl, sw_flow_ctl, extra_flow_ctl,
1036                                       icrnl, igncr, inlcr, brkint, ignbrk,
1037                                       parmrk, istrip, tx_period, rx_period,
1038                                       out_baud, in_baud ));
1039 
1040   rtems_interrupt_enable (level);
1041 
1042   /*
1043    *  Looks like the CD2401 needs time to settle after initialization. Give it
1044    *  10 ms. I don't really believe it, but if output resumes to quickly after
1045    *  this call, the first few characters are not right.
1046    */
1047   if ( need_reinitialization )
1048     cd2401_udelay( 10000L );
1049 
1050   /* Return something */
1051   return RTEMS_SUCCESSFUL;
1052 }
1053 
1054 /*
1055  *  cd2401_startRemoreTx
1056  *
1057  *  Defined as a callback, but it would appear that it is never called. The
1058  *  POSIX standard states that when the tcflow() function is called with the
1059  *  TCION action, the system wall transmit a START character. Presumably,
1060  *  tcflow() is called internally when IXOFF is set in the termios c_iflag
1061  *  field when the input buffer can accomodate enough characters. It should
1062  *  probably be called from fillBufferQueue(). Clearly, the function is also
1063  *  explicitly callable by user code. The action is clearly to send the START
1064  *  character, regardless of whether START/STOP flow control is in effect.
1065  *
1066  *  Input parameters:
1067  *    minor - selected channel
1068  *
1069  *  Output parameters: NONE
1070  *
1071  *  Return value: IGNORED
1072  *
1073  *  PROPER START CHARACTER MUST BE PROGRAMMED IN SCHR1.
1074  */
1075 int cd2401_startRemoteTx(
1076   int minor
1077 )
1078 {
1079   rtems_interrupt_level level;
1080 
1081   rtems_interrupt_disable (level);
1082 
1083   cd2401->car = minor;              /* Select channel */
1084   cd2401->stcr = 0x01;              /* Send SCHR1 ahead of chars in FIFO */
1085 
1086   CD2401_RECORD_START_REMOTE_TX_INFO(( minor ));
1087 
1088   rtems_interrupt_enable (level);
1089 
1090   /* Return something */
1091   return RTEMS_SUCCESSFUL;
1092 }
1093 
1094 /*
1095  *  cd2401_stopRemoteTx
1096  *
1097  *  Defined as a callback, but it would appear that it is never called. The
1098  *  POSIX standard states that when the tcflow() function is called with the
1099  *  TCIOFF function, the system wall transmit a STOP character. Presumably,
1100  *  tcflow() is called internally when IXOFF is set in the termios c_iflag
1101  *  field as the input buffer is about to overflow. It should probably be
1102  *  called from rtems_termios_enqueue_raw_characters(). Clearly, the function
1103  *  is also explicitly callable by user code. The action is clearly to send
1104  *  the STOP character, regardless of whether START/STOP flow control is in
1105  *  effect.
1106  *
1107  *  Input parameters:
1108  *    minor - selected channel
1109  *
1110  *  Output parameters: NONE
1111  *
1112  *  Return value: IGNORED
1113  *
1114  *  PROPER STOP CHARACTER MUST BE PROGRAMMED IN SCHR2.
1115  */
1116 int cd2401_stopRemoteTx(
1117   int minor
1118 )
1119 {
1120   rtems_interrupt_level level;
1121 
1122   rtems_interrupt_disable (level);
1123 
1124   cd2401->car = minor;              /* Select channel */
1125   cd2401->stcr = 0x02;              /* Send SCHR2 ahead of chars in FIFO */
1126 
1127   CD2401_RECORD_STOP_REMOTE_TX_INFO(( minor ));
1128 
1129   rtems_interrupt_enable (level);
1130 
1131   /* Return something */
1132   return RTEMS_SUCCESSFUL;
1133 }
1134 
1135 /*
1136  *  cd2401_write
1137  *
1138  *  Initiate DMA output. Termios guarantees that the buffer does not wrap
1139  *  around, so we can do DMA strait from the supplied buffer.
1140  *
1141  *  Input parameters:
1142  *    minor - selected channel
1143  *    buf - output buffer
1144  *    len - number of chars to output
1145  *
1146  *  Output parameters:  NONE
1147  *
1148  *  Return value: IGNORED
1149  *
1150  *  MUST BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED!
1151  *  The processor is placed at interrupt level CD2401_INT_LEVEL explicitly in
1152  *  console_write(). The processor is necessarily at interrupt level 1 in
1153  *  cd2401_tx_isr().
1154  */
1155 ssize_t cd2401_write(
1156   int minor,
1157   const char *buf,
1158   size_t len
1159 )
1160 {
1161   if (len > 0) {
1162     cd2401->car = minor;              /* Select channel */
1163 
1164     if ( (cd2401->dmabsts & 0x08) == 0 ) {
1165       /* Next buffer is A. Wait for it to be ours. */
1166       while ( cd2401->atbsts & 0x01 );
1167 
1168       CD2401_Channel_Info[minor].own_buf_A = FALSE;
1169       CD2401_Channel_Info[minor].len = len;
1170       CD2401_Channel_Info[minor].buf = buf;
1171       cd2401->atbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 );
1172       cd2401->atbadrl = (uint16_t)( (uint32_t) buf );
1173       cd2401->atbcnt = len;
1174       CD2401_RECORD_WRITE_INFO(( len, buf, 'A' ));
1175       cd2401->atbsts = 0x03;          /* CD2401 owns buffer, int when empty */
1176     }
1177     else {
1178       /* Next buffer is B. Wait for it to be ours. */
1179       while ( cd2401->btbsts & 0x01 );
1180 
1181       CD2401_Channel_Info[minor].own_buf_B = FALSE;
1182       CD2401_Channel_Info[minor].len = len;
1183       CD2401_Channel_Info[minor].buf = buf;
1184       cd2401->btbadru = (uint16_t)( ( (uint32_t) buf ) >> 16 );
1185       cd2401->btbadrl = (uint16_t)( (uint32_t) buf );
1186       cd2401->btbcnt = len;
1187       CD2401_RECORD_WRITE_INFO(( len, buf, 'B' ));
1188       cd2401->btbsts = 0x03;          /* CD2401 owns buffer, int when empty */
1189     }
1190     /* Nuts -- Need TxD ints */
1191     CD2401_Channel_Info[minor].txEmpty = FALSE;
1192     cd2401->ier |= 0x01;
1193   }
1194 
1195   /* Return something */
1196   return len;
1197 }
1198 
1199 #if 0
1200 /*
1201  *  cd2401_drainOutput
1202  *
1203  *  Wait for the txEmpty indication on the specified channel.
1204  *
1205  *  Input parameters:
1206  *    minor - selected channel
1207  *
1208  *  Output parameters:  NONE
1209  *
1210  *  Return value: IGNORED
1211  *
1212  *  MUST NOT BE EXECUTED WITH THE CD2401 INTERRUPTS DISABLED!
1213  *  The txEmpty flag is set by the tx ISR.
1214  *
1215  *  DOES NOT WORK! DO NOT ENABLE THIS CODE. THE CD2401 DOES NOT COOPERATE!
1216  *  The code is here to document that the output FIFO is NOT empty when
1217  *  the CD2401 reports that the Tx buffer is empty.
1218  */
1219 int cd2401_drainOutput(
1220   int minor
1221 )
1222 {
1223   CD2401_RECORD_DRAIN_OUTPUT_INFO(( CD2401_Channel_Info[minor].txEmpty,
1224                                     CD2401_Channel_Info[minor].own_buf_A,
1225                                     CD2401_Channel_Info[minor].own_buf_B ));
1226 
1227   while( ! (CD2401_Channel_Info[minor].txEmpty &&
1228             CD2401_Channel_Info[minor].own_buf_A &&
1229             CD2401_Channel_Info[minor].own_buf_B) );
1230 
1231   /* Return something */
1232   return RTEMS_SUCCESSFUL;
1233 }
1234 #endif
1235 
1236 /*
1237  * _167Bug_pollRead
1238  *
1239  *  Read a character from the 167Bug console, and return it. Return -1
1240  *  if there is no character in the input FIFO.
1241  *
1242  *  Input parameters:
1243  *    minor - selected channel
1244  *
1245  *  Output parameters:  NONE
1246  *
1247  *  Return value: char returned as positive signed int
1248  *                -1 if no character is present in the input FIFO.
1249  *
1250  *  CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O!
1251  */
1252 int _167Bug_pollRead(
1253   int minor
1254 )
1255 {
1256   int char_not_available;
1257   unsigned char c;
1258   rtems_interrupt_level previous_level;
1259 
1260   /*
1261    *  Redirection of .INSTAT does not work: 167-Bug crashes.
1262    *  Switch the input stream to the specified port.
1263    *  Make sure this is atomic code.
1264    */
1265   rtems_interrupt_disable( previous_level );
1266 
1267   __asm__ volatile( "movew  %1, -(%%sp)\n\t"/* Channel */
1268                 "trap   #15\n\t"        /* Trap to 167Bug */
1269                 ".short 0x61\n\t"       /* Code for .REDIR_I */
1270                 "trap   #15\n\t"        /* Trap to 167Bug */
1271         ".short 0x01\n\t"       /* Code for .INSTAT */
1272                 "move   %%cc, %0\n\t"   /* Get condition codes */
1273                 "andil  #4, %0"         /* Keep the Zero bit */
1274     : "=d" (char_not_available) : "d" (minor): "%%cc" );
1275 
1276   if (char_not_available) {
1277     rtems_interrupt_enable( previous_level );
1278     return -1;
1279   }
1280 
1281   /* Read the char and return it */
1282   __asm__ volatile( "subq.l #2,%%a7\n\t"    /* Space for result */
1283                 "trap   #15\n\t"        /* Trap to 167 Bug */
1284                 ".short 0x00\n\t"       /* Code for .INCHR */
1285                 "moveb  (%%a7)+, %0"    /* Pop char into c */
1286     : "=d" (c) : );
1287 
1288   rtems_interrupt_enable( previous_level );
1289 
1290   return (int)c;
1291 }
1292 
1293 /*
1294  * _167Bug_pollWrite
1295  *
1296  *  Output buffer through 167Bug. Returns only once every character has been
1297  *  sent (polled output).
1298  *
1299  *  Input parameters:
1300  *    minor - selected channel
1301  *    buf - output buffer
1302  *    len - number of chars to output
1303  *
1304  *  Output parameters:  NONE
1305  *
1306  *  Return value: IGNORED
1307  *
1308  *  CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O!
1309  */
1310 ssize_t _167Bug_pollWrite(
1311   int minor,
1312   const char *buf,
1313   size_t len
1314 )
1315 {
1316   const char *endbuf = buf + len;
1317 
1318   __asm__ volatile( "pea    (%0)\n\t"            /* endbuf */
1319                 "pea    (%1)\n\t"            /* buf */
1320                 "movew  #0x21, -(%%sp)\n\t"  /* Code for .OUTSTR */
1321                 "movew  %2, -(%%sp)\n\t"     /* Channel */
1322                 "trap   #15\n\t"             /* Trap to 167Bug */
1323                 ".short 0x60"                /* Code for .REDIR */
1324     :: "a" (endbuf), "a" (buf), "d" (minor) );
1325 
1326   /* Return something */
1327   return len;
1328 }
1329 
1330 /*
1331  *  do_poll_read
1332  *
1333  *  Input characters through 167Bug. Returns has soon as a character has been
1334  *  received. Otherwise, if we wait for the number of requested characters, we
1335  *  could be here forever!
1336  *
1337  *  CR is converted to LF on input. The terminal should not send a CR/LF pair
1338  *  when the return or enter key is pressed.
1339  *
1340  *  Input parameters:
1341  *    major - ignored. Should be the major number for this driver.
1342  *    minor - selected channel.
1343  *    arg->buffer - where to put the received characters.
1344  *    arg->count  - number of characters to receive before returning--Ignored.
1345  *
1346  *  Output parameters:
1347  *    arg->bytes_moved - the number of characters read. Always 1.
1348  *
1349  *  Return value: RTEMS_SUCCESSFUL
1350  *
1351  *  CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O!
1352  */
1353 static rtems_status_code do_poll_read(
1354   rtems_device_major_number major,
1355   rtems_device_minor_number minor,
1356   void                    * arg
1357 )
1358 {
1359   rtems_libio_rw_args_t *rw_args = arg;
1360   int c;
1361 
1362   while( (c = _167Bug_pollRead (minor)) == -1 );
1363   rw_args->buffer[0] = (uint8_t)c;
1364   if( rw_args->buffer[0] == '\r' )
1365       rw_args->buffer[0] = '\n';
1366   rw_args->bytes_moved = 1;
1367   return RTEMS_SUCCESSFUL;
1368 }
1369 
1370 /*
1371  *  do_poll_write
1372  *
1373  *  Output characters through 167Bug. Returns only once every character has
1374  *  been sent.
1375  *
1376  *  CR is transmitted AFTER a LF on output.
1377  *
1378  *  Input parameters:
1379  *    major - ignored. Should be the major number for this driver.
1380  *    minor - selected channel
1381  *    arg->buffer - where to get the characters to transmit.
1382  *    arg->count  - the number of characters to transmit before returning.
1383  *
1384  *  Output parameters:
1385  *    arg->bytes_moved - the number of characters read
1386  *
1387  *  Return value: RTEMS_SUCCESSFUL
1388  *
1389  *  CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O!
1390  */
1391 static rtems_status_code do_poll_write(
1392   rtems_device_major_number major,
1393   rtems_device_minor_number minor,
1394   void                    * arg
1395 )
1396 {
1397   rtems_libio_rw_args_t *rw_args = arg;
1398   uint32_t   i;
1399 
1400   for( i = 0; i < rw_args->count; i++ ) {
1401     _167Bug_pollWrite(minor, &(rw_args->buffer[i]), 1);
1402     if ( rw_args->buffer[i] == '\n' )
1403       _167Bug_pollWrite(minor, &cr_char, 1);
1404   }
1405   rw_args->bytes_moved = i;
1406   return RTEMS_SUCCESSFUL;
1407 }
1408 
1409 /*
1410  *  _BSP_output_char
1411  *
1412  *  printk() function prototyped in bspIo.h. Does not use termios.
1413  */
1414 void _BSP_output_char(char c)
1415 {
1416   rtems_device_minor_number printk_minor;
1417 
1418   /*
1419    *  Can't rely on console_initialize having been called before this function
1420    *  is used.
1421    */
1422   if ( NVRAM_CONFIGURE )
1423     /* J1-4 is on, use NVRAM info for configuration */
1424     printk_minor = (nvram->console_printk_port & 0x30) >> 4;
1425   else
1426     printk_minor = PRINTK_MINOR;
1427 
1428   _167Bug_pollWrite(printk_minor, &c, 1);
1429 }
1430 
1431 /*
1432  ***************
1433  * BOILERPLATE *
1434  ***************
1435  *
1436  *  All these functions are prototyped in rtems/c/src/lib/include/console.h.
1437  */
1438 
1439 /*
1440  * Initialize and register the device
1441  */
1442 rtems_device_driver console_initialize(
1443   rtems_device_major_number  major,
1444   rtems_device_minor_number  minor,
1445   void                      *arg
1446 )
1447 {
1448   rtems_status_code status;
1449   rtems_device_minor_number console_minor;
1450 
1451   /*
1452    * Set up TERMIOS if needed
1453    */
1454   if ( NVRAM_CONFIGURE ) {
1455     /* J1-4 is on, use NVRAM info for configuration */
1456     console_minor = nvram->console_printk_port & 0x03;
1457 
1458     if ( nvram->console_mode & 0x01 )
1459       /* termios */
1460       rtems_termios_initialize ();
1461   }
1462   else {
1463     console_minor = CONSOLE_MINOR;
1464 #if CD2401_USE_TERMIOS == 1
1465     rtems_termios_initialize ();
1466 #endif
1467   }
1468 
1469   /*
1470    * Do device-specific initialization
1471    * Does not affect 167-Bug.
1472    */
1473   cd2401_initialize ();
1474 
1475   /*
1476    * Register the devices
1477    */
1478   status = rtems_io_register_name ("/dev/tty0", major, 0);
1479   if (status != RTEMS_SUCCESSFUL)
1480     rtems_fatal_error_occurred (status);
1481 
1482   status = rtems_io_register_name ("/dev/tty1", major, 1);
1483   if (status != RTEMS_SUCCESSFUL)
1484     rtems_fatal_error_occurred (status);
1485 
1486   status = rtems_io_register_name ("/dev/console", major, console_minor);
1487   if (status != RTEMS_SUCCESSFUL)
1488     rtems_fatal_error_occurred (status);
1489 
1490   status = rtems_io_register_name ("/dev/tty2", major, 2);
1491   if (status != RTEMS_SUCCESSFUL)
1492     rtems_fatal_error_occurred (status);
1493 
1494   status = rtems_io_register_name ("/dev/tty3", major, 3);
1495   if (status != RTEMS_SUCCESSFUL)
1496     rtems_fatal_error_occurred (status);
1497 
1498   return RTEMS_SUCCESSFUL;
1499 }
1500 
1501 /*
1502  * Open the device
1503  */
1504 rtems_device_driver console_open(
1505   rtems_device_major_number major,
1506   rtems_device_minor_number minor,
1507   void                    * arg
1508 )
1509 {
1510   static const rtems_termios_callbacks pollCallbacks = {
1511     NULL,                       /* firstOpen */
1512     NULL,                       /* lastClose */
1513     _167Bug_pollRead,           /* pollRead */
1514     _167Bug_pollWrite,          /* write */
1515     NULL,                       /* setAttributes */
1516     NULL,                       /* stopRemoteTx */
1517     NULL,                       /* startRemoteTx */
1518     TERMIOS_POLLED              /* outputUsesInterrupts */
1519   };
1520 
1521   static const rtems_termios_callbacks intrCallbacks = {
1522     cd2401_firstOpen,           /* firstOpen */
1523     cd2401_lastClose,           /* lastClose */
1524     NULL,                       /* pollRead */
1525     cd2401_write,               /* write */
1526     cd2401_setAttributes,       /* setAttributes */
1527     cd2401_stopRemoteTx,        /* stopRemoteTx */
1528     cd2401_startRemoteTx,       /* startRemoteTx */
1529     TERMIOS_IRQ_DRIVEN          /* outputUsesInterrupts */
1530   };
1531 
1532   if ( NVRAM_CONFIGURE )
1533     /* J1-4 is on, use NVRAM info for configuration */
1534     if ( nvram->console_mode & 0x01 )
1535       /* termios */
1536       if ( nvram->console_mode & 0x02 )
1537         /* interrupt-driven I/O */
1538         return rtems_termios_open (major, minor, arg, &intrCallbacks);
1539         else
1540         /* polled I/O */
1541         return rtems_termios_open (major, minor, arg, &pollCallbacks);
1542       else
1543         /* no termios -- default to polled I/O */
1544         return RTEMS_SUCCESSFUL;
1545 #if CD2401_USE_TERMIOS == 1
1546 #if CD2401_IO_MODE != 1
1547   else
1548     /* termios & polled I/O*/
1549     return rtems_termios_open (major, minor, arg, &pollCallbacks);
1550 #else
1551   else
1552     /* termios & interrupt-driven I/O*/
1553     return rtems_termios_open (major, minor, arg, &intrCallbacks);
1554 #endif
1555 #else
1556   else
1557     /* no termios -- default to polled I/O */
1558     return RTEMS_SUCCESSFUL;
1559 #endif
1560 }
1561 
1562 /*
1563  * Close the device
1564  */
1565 rtems_device_driver console_close(
1566   rtems_device_major_number major,
1567   rtems_device_minor_number minor,
1568   void                    * arg
1569 )
1570 {
1571   if ( NVRAM_CONFIGURE ) {
1572     /* J1-4 is on, use NVRAM info for configuration */
1573     if ( nvram->console_mode & 0x01 )
1574       /* termios */
1575       return rtems_termios_close (arg);
1576     else
1577       /* no termios */
1578       return RTEMS_SUCCESSFUL;
1579   }
1580 #if CD2401_USE_TERMIOS == 1
1581   else
1582     /* termios */
1583     return rtems_termios_close (arg);
1584 #else
1585   else
1586     /* no termios */
1587     return RTEMS_SUCCESSFUL;
1588 #endif
1589 }
1590 
1591 /*
1592  * Read from the device
1593  */
1594 rtems_device_driver console_read(
1595   rtems_device_major_number major,
1596   rtems_device_minor_number minor,
1597   void                    * arg
1598 )
1599 {
1600   if ( NVRAM_CONFIGURE ) {
1601     /* J1-4 is on, use NVRAM info for configuration */
1602     if ( nvram->console_mode & 0x01 )
1603       /* termios */
1604       return rtems_termios_read (arg);
1605     else
1606       /* no termios -- default to polled */
1607       return do_poll_read (major, minor, arg);
1608   }
1609 #if CD2401_USE_TERMIOS == 1
1610   else
1611     /* termios */
1612     return rtems_termios_read (arg);
1613 #else
1614   else
1615     /* no termios -- default to polled */
1616     return do_poll_read (major, minor, arg);
1617 #endif
1618 }
1619 
1620 /*
1621  * Write to the device
1622  */
1623 rtems_device_driver console_write(
1624   rtems_device_major_number major,
1625   rtems_device_minor_number minor,
1626   void                    * arg
1627 )
1628 {
1629   if ( NVRAM_CONFIGURE ) {
1630     /* J1-4 is on, use NVRAM info for configuration */
1631     if ( nvram->console_mode & 0x01 )
1632       /* termios */
1633       return rtems_termios_write (arg);
1634     else
1635       /* no termios -- default to polled */
1636       return do_poll_write (major, minor, arg);
1637   }
1638 #if CD2401_USE_TERMIOS == 1
1639   else
1640     /* termios */
1641     return rtems_termios_write (arg);
1642 #else
1643   else
1644     /* no termios -- default to polled */
1645     return do_poll_write (major, minor, arg);
1646 #endif
1647 }
1648 
1649 /*
1650  * Handle ioctl request.
1651  */
1652 rtems_device_driver console_control(
1653   rtems_device_major_number major,
1654   rtems_device_minor_number minor,
1655   void                    * arg
1656 )
1657 {
1658   if ( NVRAM_CONFIGURE ) {
1659     /* J1-4 is on, use NVRAM info for configuration */
1660     if ( nvram->console_mode & 0x01 )
1661       /* termios */
1662       return rtems_termios_ioctl (arg);
1663     else
1664       /* no termios -- default to polled */
1665       return RTEMS_SUCCESSFUL;
1666   }
1667 #if CD2401_USE_TERMIOS == 1
1668   else
1669     /* termios */
1670     return rtems_termios_ioctl (arg);
1671 #else
1672   else
1673     /* no termios -- default to polled */
1674     return RTEMS_SUCCESSFUL;
1675 #endif
1676 }