source: rtems/c/src/lib/libbsp/m68k/mvme167/console/console.c @ 1c6926c1

5
Last change on this file since 1c6926c1 was 1c6926c1, checked in by Kevin Kirspel <kevin-kirspel@…>, on 03/21/17 at 19:39:48

termios: Synchronize with latest FreeBSD headers

Adding modified FreeBSD headers to synchronize RTEMS termios with
FreeBSD. Modify termios to support dedicated input and output baud for
termios structure. Updated BSPs to use dedicated input and output baud
in termios structure. Updated tools to use dedicated input and output
baud in termios structure. Updated termios testsuites to use dedicated
input and output baud in termios structure.

Close #2897.

  • Property mode set to 100644
File size: 53.7 KB
Line 
1/*
2 *  This file contains the MVME167 termios console package. Only asynchronous
3 *  I/O is supported.
4 *
5 *  /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M.
6 *  /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M.
7 *  /dev/tty2 is channel 2, Serial Port 3 on the MVME712M.
8 *  /dev/tty3 is channel 3, Serial Port 4 on the MVME712M.
9 *
10 *  Normal I/O uses DMA for output, interrupts for input. /dev/console is
11 *  fixed to be /dev/tty01, Serial Port 2. Very limited support is provided
12 *  for polled I/O. Polled I/O is intended only for running the RTEMS test
13 *  suites. In all cases, Serial Port 1/Console is allocated to 167Bug and
14 *  is the dedicated debugger port. We configure GDB to use 167Bug for
15 *  debugging. When debugging with GDB or 167Bug, do not open /dev/tty00.
16 *
17 *  Modern I/O chips often contain a number of I/O devices that can operate
18 *  almost independently of each other. Typically, in RTEMS, all devices in
19 *  an I/O chip are handled by a single device driver, but that need not be
20 *  always the case. Each device driver must supply six entry points in the
21 *  Device Driver Table: a device initialization function, as well as an open,
22 *  close, read, write and a control function. RTEMS assigns a device major
23 *  number to each device driver. This major device number is the index of the
24 *  device driver entries in the Device Driver Table, and it used to identify
25 *  a particular device driver. To distinguish multiple I/O sub-devices within
26 *  an I/O chip, RTEMS supports device minor numbers. When a I/O device is
27 *  initialized, the major number is supplied to the initialization function.
28 *  That function must register each sub-device with a separate name and minor
29 *  number (as well as the supplied major number). When an application opens a
30 *  device by name, the corresponding major and minor numbers are returned to
31 *  the caller to be used in subsequent I/O operations (although these details
32 *  are typically hidden within the library functions).
33 *
34 *  Such a scheme recognizes that the initialization of the individual
35 *  sub-devices is generally not completely independent. For example, the
36 *  four serial ports of the CD2401 can be configured almost independently
37 *  from each other. One port could be configured to operate in asynchronous
38 *  mode with interrupt-driven I/O, while another port could be configured to
39 *  operate in HDLC mode with DMA I/O. However, a device reset command will
40 *  reset all four channels, and the width of DMA transfers and the number of
41 *  retries following bus errors selected applies to all four channels.
42 *  Consequently, when initializing one channel, one must be careful not to
43 *  destroy the configuration of other channels that are already configured.
44 *
45 *  One problem with the RTEMS I/O initialization model is that no information
46 *  other than a device major number is passed to the initialization function.
47 *  Consequently, the sub-devices must be initialized with some pre-determined
48 *  configuration. To change the configuration of a sub-device, it is
49 *  necessary to either rewrite the initialization function, or to make a
50 *  series of rtems_io_control() calls after initialization. The first
51 *  approach is not very elegant. The second approach is acceptable if an
52 *  application is simply changing baud rates, parity or other such
53 *  asynchronous parameters (as supplied by the termios package). But what if
54 *  an application requires one channel to run in HDLC or Bisync mode and
55 *  another in async mode? With a single driver per I/O chip approach, the
56 *  device driver must support multiple protocols. This is feasible, but it
57 *  often means that an application that only does asynchronous I/O now links
58 *  in code for other unused protocols, thus wasting precious ROM space.
59 *  Worse, it requires that the sub-devices be initialized in some
60 *  configuration, and that configuration then changed through a series of
61 *  device driver control calls. There is no standard API in RTEMS to switch
62 *  a serial line to some synchronous protocol.
63 *
64 *  A better approach is to treat each channel as a separate device, each with
65 *  its own device device driver. The application then supplies its own device
66 *  driver table with only the required protocols (drivers) on each line. The
67 *  problem with this approach is that the device drivers are not really
68 *  independent, given that the I/O sub-devices within a common chip are not
69 *  independent themselves. Consequently, the related device drivers must
70 *  share some information. In RTEMS, there is no standard location in which
71 *  to share information.
72 *
73 *  This driver handles all four channels, i.e. it distinguishes the
74 *  sub-devices using minor device numbers. Only asynchronous I/O is
75 *  supported. The console is currently fixed to be channel 1 on the CD2401,
76 *  which corresponds to the TTY01 port (Serial Port 2) on the MVME712M
77 *  Transition Module.
78 *
79 *  The CD2401 does either interrupt-driven or DMA I/O; it does not support
80 *  polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401
81 *  are routed to the MC68040, and the processor generates an interrupt
82 *  acknowledge cycle directly to the CD2401 to obtain an interrupt vector.
83 *  The PCCchip2 supports a pseudo-polling mode in which interrupts from the
84 *  CD2401 are not routed to the MC68040, but can be detected by the processor
85 *  by reading the appropriate CD2401 registers. In this mode, interrupt
86 *  acknowledge cycles must be generated to the CD2401 by reading the
87 *  appropriate PCCchip2 registers.
88 *
89 *  Interrupts from the four channels cannot be routed independently; either
90 *  all channels are used in the pseudo-polling mode, or all channels are used
91 *  in interrupt-driven/DMA mode. There is no advantage in using the speudo-
92 *  polling mode. Consenquently, this driver performs DMA input and output.
93 *  Output is performed directly from the termios raw output buffer, while
94 *  input is accumulated into a separate buffer.
95 *
96 *  THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from
97 *  multiple tasks is likely to cause significant problems! Concurrency
98 *  control is implemented in the termios package.
99 *
100 *  THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS.
101 *  If the CD2401 is to be used for high speed synchronous serial I/O, the
102 *  interrupt priority might need to be increased.
103 *
104 *  ALL INTERRUPT HANDLERS ARE SHARED.
105 *  When adding extra device drivers, either rewrite the interrupt handlers
106 *  to demultiplex the interrupts, or install separate vectors. Common vectors
107 *  are currently used to catch spurious interrupts. We could already have
108 *  installed separate vectors for each channel and used the spurious
109 *  interrupt handler defined in some other BSPs, but handling spurious
110 *  interrupts from the CD2401 in this device driver allows us to record more
111 *  information on the source of the interrupts. Furthermore, we have observed
112 *  the occasional spurious interrupt from channel 0. We definitely do not
113 *  to call a debugger for those.
114 *
115 *  All page references are to the MVME166/MVME167/MVME187 Single Board
116 *  Computer Programmer's Reference Guide (MVME187PG/D2) with the April
117 *  1993 supplements/addenda (MVME187PG/D2A1).
118 */
119
120/*
121 *  Copyright (c) 1998, National Research Council of Canada
122 *
123 *  The license and distribution terms for this file may be
124 *  found in the file LICENSE in this distribution or at
125 *  http://www.rtems.org/license/LICENSE.
126 */
127
128#define M167_INIT
129
130#include <stdarg.h>
131#include <stdio.h>
132#include <termios.h>
133
134#include <rtems/console.h>
135#include <rtems/libio.h>
136#include <rtems/termiostypes.h>
137#include <bsp.h>                /* Must be before libio.h */
138
139/* Utility functions */
140void cd2401_udelay( unsigned long delay );
141void cd2401_chan_cmd( uint8_t         channel, uint8_t         cmd, uint8_t         wait );
142uint16_t         cd2401_bitrate_divisor( uint32_t         clkrate, uint32_t        * bitrate );
143void cd2401_initialize( void );
144void cd2401_interrupts_initialize( bool enable );
145
146/* ISRs */
147rtems_isr cd2401_modem_isr( rtems_vector_number vector );
148rtems_isr cd2401_re_isr( rtems_vector_number vector );
149rtems_isr cd2401_rx_isr( rtems_vector_number vector );
150rtems_isr cd2401_tx_isr( rtems_vector_number vector );
151
152/* Termios callbacks */
153int cd2401_firstOpen( int major, int minor, void *arg );
154int cd2401_lastClose( int major, int minor, void *arg );
155int cd2401_setAttributes( int minor, const struct termios *t );
156int cd2401_startRemoteTx( int minor );
157int cd2401_stopRemoteTx( int minor );
158ssize_t cd2401_write( int minor, const char *buf, size_t len );
159int cd2401_drainOutput( int minor );
160int _167Bug_pollRead( int minor );
161ssize_t _167Bug_pollWrite( int minor, const char *buf, size_t len );
162
163/* Printk function */
164static void _BSP_output_char( char c );
165BSP_output_char_function_type     BSP_output_char = _BSP_output_char;
166BSP_polling_getchar_function_type BSP_poll_char = NULL;
167
168/* '\r' character in memory. This used to live on
169 * the stack but storing the '\r' character is
170 * optimized away by gcc-4.3.2 (since it seems to
171 * be unused [only referenced from inline assembly
172 * code in _167Bug_pollWrite()]).
173 * Hence we make it a global constant.
174 */
175static const char cr_char = '\r';
176
177/* Channel info */
178/* static */ volatile struct {
179  void *tty;                    /* Really a struct rtems_termios_tty * */
180  int len;                      /* Record nb of chars being TX'ed */
181  const char *buf;              /* Record where DMA is coming from */
182  uint32_t         spur_cnt;    /* Nb of spurious ints so far */
183  uint32_t         spur_dev;    /* Indo on last spurious int */
184  uint32_t         buserr_addr; /* Faulting address */
185  uint32_t         buserr_type; /* Reason of bus error during DMA */
186  uint8_t          own_buf_A;   /* If true, buffer A belongs to the driver */
187  uint8_t          own_buf_B;   /* If true, buffer B belongs to the driver */
188  uint8_t          txEmpty;     /* If true, the output FIFO should be empty */
189} CD2401_Channel_Info[4];
190
191/*
192 *  The number of channels already opened. If zero, enable the interrupts. The
193 *  initial value must be 0. If initialized explicitly, the variable ends up
194 *  in the .data section. Its value is not re-initialized on system restart.
195 *  Furthermore, because the variable is changed, the .data section would not
196 *  be ROMable. We thus leave the variable uninitialized, which causes it to
197 *  be allocated in the .bss section, and rely on RTEMS to zero the .bss
198 *  section on every startup.
199 */
200uint8_t         Init_count;
201
202/* Record previous handlers */
203rtems_isr_entry Prev_re_isr;        /* Previous rx exception isr */
204rtems_isr_entry Prev_rx_isr;        /* Previous rx isr */
205rtems_isr_entry Prev_tx_isr;        /* Previous tx isr */
206rtems_isr_entry Prev_modem_isr;     /* Previous modem/timer isr */
207
208/* Define the following symbol to trace the calls to this driver */
209/* #define CD2401_RECORD_DEBUG_INFO */
210#include "console-recording.h"
211
212/*
213 *  Utility functions.
214 */
215
216/*
217 *  Assumes that clock ticks 1 million times per second.
218 *
219 *  MAXIMUM DELAY IS ABOUT 20 ms
220 *
221 *  Input parameters:
222 *    delay: Number of microseconds to delay.
223 *
224 *  Output parameters: NONE
225 *
226 *  Return values: NONE
227 */
228 void cd2401_udelay
229(
230  unsigned long delay
231)
232{
233  unsigned long i = 20000;  /* In case clock is off */
234  rtems_interval start_ticks, end_ticks, current_ticks;
235
236  start_ticks = rtems_clock_get_ticks_since_boot();
237  end_ticks = start_ticks + delay;
238
239  do {
240    current_ticks = rtems_clock_get_ticks_since_boot();
241  } while ( --i && (current_ticks <= end_ticks) );
242
243  CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i ));
244}
245
246/*
247 *  cd2401_chan_cmd
248 *
249 *  Sends a CCR command to the specified channel. Waits for any unfinished
250 *  previous command to complete, then sends the specified command. Optionally
251 *  wait for the current command to finish before returning.
252 *
253 *  Input parameters:
254 *    channel - CD2401 channel number
255 *    cmd  - command byte
256 *    wait - if non-zero, wait for specified command to complete before
257 *          returning.
258 *
259 *  Output parameters: NONE
260 *
261 *  Return values: NONE
262 */
263void cd2401_chan_cmd(
264  uint8_t         channel,
265  uint8_t         cmd,
266  uint8_t         wait
267)
268{
269  if ( channel < 4 ) {
270    cd2401->car = channel;      /* Select channel */
271
272    while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */
273    cd2401->ccr = cmd;          /* Send command */
274    if ( wait )
275      while( cd2401->ccr != 0 );/* Wait for completion */
276  }
277  else {
278    /* This may not be the best error message */
279    rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
280  }
281}
282
283/*
284 *  cd2401_bitrate_divisor
285 *
286 *  Compute the divisor and clock source to use to obtain the desired bitrate.
287 *
288 *  Input parameters:
289 *    clkrate - system clock rate (CLK input frequency)
290 *    bitrate - the desired bitrate
291 *
292 *  Output parameters:
293 *    bitrate - The actual bitrate achievable, to the nearest bps.
294 *
295 *  Return values:
296 *    Returns divisor in lower byte and clock source in upper byte for the
297 *    specified bitrate.
298 */
299uint16_t         cd2401_bitrate_divisor(
300  uint32_t         clkrate,
301  uint32_t        * bitrate
302)
303{
304  uint32_t         divisor;
305  uint16_t         clksource;
306
307  divisor = *bitrate << 3;          /* temporary; multiply by 8 for CLK/8 */
308  divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */
309
310  /* Use highest speed clock source for best precision - try clk0 to clk4 */
311  for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 )
312      divisor >>= 2;
313  divisor--;                        /* adjustment, see specs */
314  if( divisor < 1 )
315    divisor = 1;
316  else if( divisor > 0xFF )
317    divisor = 0xFF;
318  *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1);
319  return( clksource | divisor );
320}
321
322/*
323 *  cd2401_initialize
324 *
325 *  Initializes the CD2401 device. Individual channels on the chip are left in
326 *  their default reset state, and should be subsequently configured.
327 *
328 *  Input parameters: NONE
329 *
330 *  Output parameters:  NONE
331 *
332 *  Return values: NONE
333 */
334void cd2401_initialize( void )
335{
336  int i;
337
338  for ( i = 3; i >= 0; i-- ) {
339    CD2401_Channel_Info[i].tty = NULL;
340    CD2401_Channel_Info[i].len = 0;
341    CD2401_Channel_Info[i].buf = NULL;
342    CD2401_Channel_Info[i].spur_cnt = 0;
343    CD2401_Channel_Info[i].spur_dev = 0;
344    CD2401_Channel_Info[i].buserr_type = 0;
345    CD2401_Channel_Info[i].buserr_addr = 0;
346    CD2401_Channel_Info[i].own_buf_A = TRUE;
347    CD2401_Channel_Info[i].own_buf_B = TRUE;
348    CD2401_Channel_Info[i].txEmpty = TRUE;
349  }
350
351 /*
352  *  Normally, do a device reset here. If we do it, we will most likely clober
353  *  the port settings for 167Bug on channel 0. So we just shut up all the
354  *  ports by disabling their interrupts.
355  */
356#if 0
357  cd2401->gfrcr = 0;            /* So we can detect that device init is done */
358  cd2401_chan_cmd( 0x10, 0);    /* Reset all */
359  while(cd2401->gfrcr == 0);    /* Wait for reset all */
360#endif
361
362  /*
363   *  The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that
364   *  the LICR "contains the number of the interrupting channel being served.
365   *  The channel number is always that of the current acknowledged interrupt."
366   *  THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically
367   *  by the hardware, as suggested by the manual.
368   *
369   *  The updated manual (part no 542400-007) has the story straight. The
370   *  CD2401 automatically initializes the LICR to contain the channel number
371   *  in bits 2 and 3. However, these bits are not preserved when the user
372   *  defined bits are written.
373   *
374   *  The same vector number is used for all four channels. Different vector
375   *  numbers could be programmed for each channel, thus avoiding the need to
376   *  demultiplex the interrupts in the ISR.
377   */
378  for ( i = 0; i < 4; i++ ) {
379    cd2401->car = i;            /* Select channel */
380    cd2401->livr = 0x5C;        /* Motorola suggested value p. 3-15 */
381    cd2401->licr = i << 2;      /* Don't rely on reset value */
382    cd2401->ier = 0;            /* Disable all interrupts */
383  }
384
385  /*
386   *  The content of the CD2401 xpilr registers must match the A7-A0 addresses
387   *  generated by the PCCchip2 during interrupt acknowledge cycles in order
388   *  for the CD2401 to recognize the IACK cycle and clear its interrupt
389   *  request.
390   */
391  cd2401->mpilr = 0x01;         /* Match pccchip2->modem_piack p. 3-27 */
392  cd2401->tpilr = 0x02;         /* Match pccchip2->tx_piack p. 3-28 */
393  cd2401->rpilr = 0x03;         /* Match pccchip2->rx_piack p. 3-29 */
394
395  /* Global CD2401 registers */
396  cd2401->dmr = 0;              /* 16-bit DMA transfers when possible */
397  cd2401->bercnt = 0;           /* Do not retry DMA upon bus errors */
398
399  /*
400   *  Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout
401   *  and tx delay). The prescaler is clocked by the system clock) / 2048. The
402   *  register must be in the range 0x0A..0xFF, ie. a rescaler period range of
403   *  about 1ms..26ms for a nominal system clock rate  of 20MHz.
404   */
405  cd2401->tpr  = 0x0A;          /* Same value as 167Bug */
406}
407
408/*
409 *  cd2401_interrupts_initialize
410 *
411 *  This routine enables or disables the CD2401 interrupts to the MC68040.
412 *  Interrupts cannot be enabled/disabled on a per-channel basis.
413 *
414 *  Input parameters:
415 *    enable - if true, enable the interrupts, else disable them.
416 *
417 *  Output parameters:  NONE
418 *
419 *  Return values: NONE
420 *
421 *  THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS.
422 *  THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS.
423 */
424void cd2401_interrupts_initialize(
425  bool enable
426)
427{
428  if ( enable ) {
429   /*
430    *  Enable interrupts from the CD2401 in the PCCchip2.
431    *  During DMA transfers, the MC68040 supplies dirty data during read cycles
432    *  from the CD2401 and leaves the data dirty in its data cache if there is
433    *  a cache hit. The MC68040 updates the data cache during write cycles from
434    *  the CD2401 if there is a cache hit.
435    */
436    pccchip2->SCC_error = 0x01;
437    pccchip2->SCC_modem_int_ctl = 0x10 | CD2401_INT_LEVEL;
438    pccchip2->SCC_tx_int_ctl = 0x10 | CD2401_INT_LEVEL;
439    pccchip2->SCC_rx_int_ctl = 0x50 | CD2401_INT_LEVEL;
440
441    pccchip2->gen_control |= 0x02;      /* Enable pccchip2 interrupts */
442  }
443  else {
444    /* Disable interrupts */
445    pccchip2->SCC_modem_int_ctl &= 0xEF;
446    pccchip2->SCC_tx_int_ctl &= 0xEF;
447    pccchip2->SCC_rx_int_ctl &= 0xEF;
448  }
449}
450
451/* ISRs */
452
453/*
454 *  cd2401_modem_isr
455 *
456 *  Modem/timer interrupt (group 1) from CD2401. These are not used, and not
457 *  expected. Record as spurious and clear.
458 *
459 *  Input parameters:
460 *    vector - vector number
461 *
462 *  Output parameters: NONE
463 *
464 *  Return values: NONE
465 */
466rtems_isr cd2401_modem_isr(
467  rtems_vector_number vector
468)
469{
470  uint8_t         ch;
471
472  /* Get interrupting channel ID */
473  ch = cd2401->licr >> 2;
474
475  /* Record interrupt info for debugging */
476  CD2401_Channel_Info[ch].spur_dev =
477      (vector << 24) | (cd2401->stk << 16) | (cd2401->mir << 8) | cd2401->misr;
478  CD2401_Channel_Info[ch].spur_cnt++;
479
480  cd2401->meoir = 0;            /* EOI */
481  CD2401_RECORD_MODEM_ISR_SPURIOUS_INFO(( ch,
482                                          CD2401_Channel_Info[ch].spur_dev,
483                                          CD2401_Channel_Info[ch].spur_cnt ));
484}
485
486/*
487 *  cd2401_re_isr
488 *
489 *  RX exception interrupt (group 3, receiver exception) from CD2401. These are
490 *  not used, and not expected. Record as spurious and clear.
491 *
492 *  FIX THIS ISR TO DETECT BREAK CONDITIONS AND RAISE SIGINT
493 *
494 *  Input parameters:
495 *    vector - vector number
496 *
497 *  Output parameters: NONE
498 *
499 *  Return values: NONE
500 */
501rtems_isr cd2401_re_isr(
502  rtems_vector_number vector
503)
504{
505  uint8_t         ch;
506
507  /* Get interrupting channel ID */
508  ch = cd2401->licr >> 2;
509
510  /* Record interrupt info for debugging */
511  CD2401_Channel_Info[ch].spur_dev =
512      (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl;
513  CD2401_Channel_Info[ch].spur_cnt++;
514
515  if ( cd2401->u5.b.risrl & 0x80 )  /* Timeout interrupt? */
516    cd2401->ier &= 0xDF;            /* Disable rx timeout interrupt */
517  cd2401->reoir = 0x08;             /* EOI; exception char not read */
518  CD2401_RECORD_RE_ISR_SPURIOUS_INFO(( ch,
519                                       CD2401_Channel_Info[ch].spur_dev,
520                                       CD2401_Channel_Info[ch].spur_cnt ));
521}
522
523/*
524 *  cd2401_rx_isr
525 *
526 *  RX interrupt (group 3, receiver data) from CD2401.
527 *
528 *  Input parameters:
529 *     vector - vector number
530 *
531 *  Output parameters: NONE
532 *
533 *  Return values: NONE
534 */
535rtems_isr cd2401_rx_isr(
536  rtems_vector_number vector
537)
538{
539  char c;
540  uint8_t         ch, status, nchars, total;
541  #ifdef CD2401_RECORD_DEBUG_INFO
542    uint8_t i = 0;
543    char    buffer[256];
544  #endif
545
546  (void) total; /* avoid set but not used warnings when not recording info */
547
548  status = cd2401->u5.b.risrl;
549  ch = cd2401->licr >> 2;
550
551  /* Has this channel been initialized or is it a condition we ignore? */
552  if ( CD2401_Channel_Info[ch].tty && !status ) {
553    /* Normal Rx Int, read chars, enqueue them, and issue EOI */
554    total = nchars = cd2401->rfoc;  /* Nb of chars to retrieve from rx FIFO */
555    while ( nchars-- > 0 ) {
556      c = (char)cd2401->dr;         /* Next char in rx FIFO */
557      rtems_termios_enqueue_raw_characters( CD2401_Channel_Info[ch].tty ,&c, 1 );
558      #ifdef CD2401_RECORD_DEBUG_INFO
559        buffer[i++] = c;
560      #endif
561    }
562    cd2401->reoir = 0;              /* EOI */
563    CD2401_RECORD_RX_ISR_INFO(( ch, total, buffer ));
564  } else {
565    /* No, record as spurious interrupt */
566    CD2401_Channel_Info[ch].spur_dev =
567        (vector << 24) | (cd2401->stk << 16) | (cd2401->rir << 8) | cd2401->u5.b.risrl;
568    CD2401_Channel_Info[ch].spur_cnt++;
569    cd2401->reoir = 0x04;           /* EOI - character not read */
570    CD2401_RECORD_RX_ISR_SPURIOUS_INFO(( ch, status,
571                                         CD2401_Channel_Info[ch].spur_dev,
572                                         CD2401_Channel_Info[ch].spur_cnt ));
573  }
574}
575
576/*
577 *  cd2401_tx_isr
578 *
579 *  TX interrupt (group 2) from CD2401.
580 *
581 *  Input parameters:
582 *    vector - vector number
583 *
584 *  Output parameters: NONE
585 *
586 *  Return values: NONE
587 */
588rtems_isr cd2401_tx_isr(
589  rtems_vector_number vector
590)
591{
592  uint8_t         ch, status, buserr, initial_ier, final_ier;
593
594  status = cd2401->tisr;
595  ch = cd2401->licr >> 2;
596  initial_ier = cd2401->ier;
597
598  #ifndef CD2401_RECORD_DEBUG_INFO
599    /*
600     * When the debug is disabled, these variables are really not read.
601     * But when debug is enabled, they are.
602     */
603    (void) initial_ier; /* avoid set but not used warning */
604    (void) final_ier; /* avoid set but not used warning */
605  #endif
606
607  /* Has this channel been initialized? */
608  if ( !CD2401_Channel_Info[ch].tty ) {
609    /* No, record as spurious interrupt */
610    CD2401_Channel_Info[ch].spur_dev =
611        (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr;
612    CD2401_Channel_Info[ch].spur_cnt++;
613    final_ier = cd2401->ier &= 0xFC;/* Shut up, whoever you are */
614
615    cd2401->teoir = 0x88;           /* EOI - Terminate buffer and no transfer */
616    CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, final_ier,
617                                         CD2401_Channel_Info[ch].spur_dev,
618                                         CD2401_Channel_Info[ch].spur_cnt ));
619    return;
620  }
621
622  if ( status & 0x80 ) {
623    /*
624     *  Bus error occurred during DMA transfer. For now, just record.
625     *  Get reason for DMA bus error and clear the report for the next
626     *  occurrence
627     */
628    buserr = pccchip2->SCC_error;
629    pccchip2->SCC_error = 0x01;
630    CD2401_Channel_Info[ch].buserr_type =
631         (vector << 24) | (buserr << 16) | (cd2401->tir << 8) | cd2401->tisr;
632    CD2401_Channel_Info[ch].buserr_addr =
633        (((uint32_t)cd2401->tcbadru) << 16) | cd2401->tcbadrl;
634
635    cd2401->teoir = 0x80;           /* EOI - terminate bad buffer */
636    CD2401_RECORD_TX_ISR_BUSERR_INFO(( ch, status, initial_ier, buserr,
637                                       CD2401_Channel_Info[ch].buserr_type,
638                                       CD2401_Channel_Info[ch].buserr_addr ));
639    return;
640  }
641
642  if ( status & 0x20 ) {
643    /* DMA done -- Turn off TxD int, turn on TxMpty */
644    final_ier = cd2401->ier = (cd2401->ier & 0xFE) | 0x02;
645    if( status & 0x08 ) {
646      /* Transmit buffer B was released */
647      CD2401_Channel_Info[ch].own_buf_B = TRUE;
648    }
649    else {
650      /* Transmit buffer A was released */
651      CD2401_Channel_Info[ch].own_buf_A = TRUE;
652    }
653    CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier,
654                                CD2401_Channel_Info[ch].txEmpty ));
655
656    /* This call can result in a call to cd2401_write() */
657    rtems_termios_dequeue_characters (
658        CD2401_Channel_Info[ch].tty,
659        CD2401_Channel_Info[ch].len );
660    cd2401->teoir = 0x08;           /* EOI - no data transfered */
661  }
662  else if ( status & 0x02 ) {
663    /* TxEmpty */
664    CD2401_Channel_Info[ch].txEmpty = TRUE;
665    final_ier = cd2401->ier &= 0xFD;/* Shut up the interrupts */
666    cd2401->teoir = 0x08;           /* EOI - no data transfered */
667    CD2401_RECORD_TX_ISR_INFO(( ch, status, initial_ier, final_ier,
668                                CD2401_Channel_Info[ch].txEmpty ));
669  }
670  else {
671    /* Why did we get a Tx interrupt? */
672    CD2401_Channel_Info[ch].spur_dev =
673        (vector << 24) | (cd2401->stk << 16) | (cd2401->tir << 8) | cd2401->tisr;
674    CD2401_Channel_Info[ch].spur_cnt++;
675    cd2401->teoir = 0x08;           /* EOI - no data transfered */
676    CD2401_RECORD_TX_ISR_SPURIOUS_INFO(( ch, status, initial_ier, 0xFF,
677                                         CD2401_Channel_Info[ch].spur_dev,
678                                         CD2401_Channel_Info[ch].spur_cnt ));
679  }
680}
681
682/*
683 *  termios callbacks
684 */
685
686/*
687 *  cd2401_firstOpen
688 *
689 *  This is the first time that this minor device (channel) is opened.
690 *  Complete the asynchronous initialization.
691 *
692 *  Input parameters:
693 *    major - device major number
694 *    minor - channel number
695 *    arg - pointer to a struct rtems_libio_open_close_args_t
696 *
697 *  Output parameters: NONE
698 *
699 *  Return value: IGNORED
700 */
701int cd2401_firstOpen(
702  int major,
703  int minor,
704  void *arg
705)
706{
707  rtems_libio_open_close_args_t *args = arg;
708  rtems_libio_ioctl_args_t newarg;
709  struct termios termios;
710  rtems_status_code sc;
711  rtems_interrupt_level level;
712
713  rtems_interrupt_disable (level);
714
715  /*
716   * Set up the line with the specified parameters. The difficulty is that
717   * the line parameters are stored in the struct termios field of a
718   * struct rtems_termios_tty that is not defined in a public header file.
719   * Therefore, we do not have direct access to the termios passed in with
720   * arg. So we make a rtems_termios_ioctl() call to get a pointer to the
721   * termios structure.
722   *
723   * THIS KLUDGE MAY BREAK IN THE FUTURE!
724   *
725   * We could have made a tcgetattr() call if we had our fd.
726   */
727  newarg.iop = args->iop;
728  newarg.command = TIOCGETA;
729  newarg.buffer = &termios;
730  sc = rtems_termios_ioctl (&newarg);
731  if (sc != RTEMS_SUCCESSFUL)
732    rtems_fatal_error_occurred (sc);
733
734  /*
735   *  Turn off hardware flow control. It is a pain with 3-wire cables.
736   *  The rtems_termios_ioctl() call below results in a call to
737   *  cd2401_setAttributes to initialize the line. The caller will "wait"
738   *  on the ttyMutex that it already owns; this is safe in RTEMS.
739   */
740  termios.c_cflag |= CLOCAL;    /* Ignore modem status lines */
741  newarg.command = TIOCGETA;
742  sc = rtems_termios_ioctl (&newarg);
743  if (sc != RTEMS_SUCCESSFUL)
744    rtems_fatal_error_occurred (sc);
745
746  /* Mark that the channel as initialized */
747  CD2401_Channel_Info[minor].tty = args->iop->data1;
748
749  /* If the first of the four channels to open, set up the interrupts */
750  if ( !Init_count++ ) {
751    /* Install the interrupt handlers */
752    Prev_re_isr    = (rtems_isr_entry) set_vector( cd2401_re_isr,    0x5C, 1 );
753    Prev_modem_isr = (rtems_isr_entry) set_vector( cd2401_modem_isr, 0x5D, 1 );
754    Prev_tx_isr    = (rtems_isr_entry) set_vector( cd2401_tx_isr,    0x5E, 1 );
755    Prev_rx_isr    = (rtems_isr_entry) set_vector( cd2401_rx_isr,    0x5F, 1 );
756
757    cd2401_interrupts_initialize( TRUE );
758  }
759
760  CD2401_RECORD_FIRST_OPEN_INFO(( minor, Init_count ));
761
762  rtems_interrupt_enable (level);
763
764  /* Return something */
765  return RTEMS_SUCCESSFUL;
766}
767
768/*
769 * cd2401_lastClose
770 *
771 *  There are no more opened file descriptors to this device. Close it down.
772 *
773 *  Input parameters:
774 *    major - device major number
775 *    minor - channel number
776 *    arg - pointer to a struct rtems_libio_open_close_args_t
777 */
778int cd2401_lastClose(
779  int major,
780  int minor,
781  void *arg
782)
783{
784  rtems_interrupt_level level;
785
786  rtems_interrupt_disable (level);
787
788  /* Mark that the channel is no longer is use */
789  CD2401_Channel_Info[minor].tty = NULL;
790
791  /* If the last of the four channels to close, disable the interrupts */
792  if ( !--Init_count ) {
793    cd2401_interrupts_initialize( FALSE );
794
795    /* De-install the interrupt handlers */
796    set_vector( Prev_re_isr,    0x5C, 1 );
797    set_vector( Prev_modem_isr, 0x5D, 1 );
798    set_vector( Prev_tx_isr,    0x5E, 1 );
799    set_vector( Prev_rx_isr,    0x5F, 1 );
800  }
801
802  CD2401_RECORD_LAST_CLOSE_INFO(( minor, Init_count ));
803
804  rtems_interrupt_enable (level);
805
806  /* return something */
807  return RTEMS_SUCCESSFUL;
808}
809
810/*
811 *  cd2401_setAttributes
812 *
813 *  Set up the selected channel of the CD2401 chip for doing asynchronous
814 *  I/O with DMA.
815 *
816 *  The chip must already have been initialized by cd2401_initialize().
817 *
818 *  This code was written for clarity. The code space it occupies could be
819 *  reduced. The code could also be compiled with aggressive optimization
820 *  turned on.
821 *
822 *  Input parameters:
823 *    minor - the selected channel
824 *    t - the termios parameters
825 *
826 *  Output parameters: NONE
827 *
828 *  Return value: IGNORED
829 */
830int cd2401_setAttributes(
831  int minor,
832  const struct termios *t
833)
834{
835  uint8_t         csize, cstopb, parodd, parenb, ignpar, inpck;
836  uint8_t         hw_flow_ctl, sw_flow_ctl, extra_flow_ctl;
837  uint8_t         icrnl, igncr, inlcr, brkint, ignbrk, parmrk, istrip;
838  uint8_t         need_reinitialization = FALSE;
839  uint8_t         read_enabled;
840  uint16_t         tx_period, rx_period;
841  uint32_t         out_baud, in_baud;
842  rtems_interrupt_level level;
843
844  /* Determine what the line parameters should be */
845
846  /* baud rates */
847  out_baud = rtems_termios_baud_to_number(t->c_ospeed);
848  in_baud  = rtems_termios_baud_to_number(t->c_ispeed);
849
850  /* Number of bits per char */
851  csize = 0x07; /* to avoid a warning */
852  switch ( t->c_cflag & CSIZE ) {
853    case CS5:     csize = 0x04;       break;
854    case CS6:     csize = 0x05;       break;
855    case CS7:     csize = 0x06;       break;
856    case CS8:     csize = 0x07;       break;
857  }
858
859  /* Parity */
860  if ( t->c_cflag & PARODD )
861    parodd = 0x80;              /* Odd parity */
862  else
863    parodd = 0;
864
865  if ( t->c_cflag & PARENB )
866    parenb = 0x40;              /* Parity enabled on Tx and Rx */
867  else
868    parenb = 0x00;              /* No parity on Tx and Rx */
869
870  /* CD2401 IGNPAR and INPCK bits are inverted wrt POSIX standard? */
871  if ( t->c_iflag & INPCK )
872    ignpar = 0;                 /* Check parity on input */
873  else
874    ignpar = 0x10;              /* Do not check parity on input */
875  if ( t->c_iflag & IGNPAR ) {
876    inpck = 0x03;               /* Discard error character */
877    parmrk = 0;
878  } else {
879    if ( t->c_iflag & PARMRK ) {
880      inpck = 0x01;             /* Translate to 0xFF 0x00 <char> */
881      parmrk = 0x04;
882    } else {
883      inpck = 0x01;             /* Translate to 0x00 */
884      parmrk = 0;
885    }
886  }
887
888  /* Stop bits */
889  if ( t->c_cflag & CSTOPB )
890    cstopb = 0x04;              /* Two stop bits */
891  else
892    cstopb = 0x02;              /* One stop bit */
893
894  /* Modem flow control */
895  if ( t->c_cflag & CLOCAL )
896    hw_flow_ctl = 0x04;         /* Always assert RTS before Tx */
897  else
898    hw_flow_ctl = 0x07;         /* Always assert RTS before Tx,
899                                   wait for CTS and DSR */
900
901  /* XON/XOFF Tx flow control */
902  if ( t->c_iflag & IXON ) {
903    sw_flow_ctl = 0x40;         /* Tx in-band flow ctl enabled, wait for XON */
904    extra_flow_ctl = 0x30;      /* Eat XON/XOFF, XON/XOFF in SCHR1, SCHR2 */
905  }
906  else {
907    sw_flow_ctl = 0;            /* Tx in-band flow ctl disabled */
908    extra_flow_ctl = 0;         /* Pass on XON/XOFF */
909  }
910
911  /* CL/LF translation */
912  if ( t->c_iflag & ICRNL )
913    icrnl = 0x40;               /* Map CR to NL on input */
914  else
915    icrnl = 0;                  /* Pass on CR */
916  if ( t->c_iflag & INLCR )
917    inlcr = 0x20;               /* Map NL to CR on input */
918  else
919    inlcr = 0;                  /* Pass on NL */
920  if ( t->c_iflag & IGNCR )
921    igncr = 0x80;               /* CR discarded on input */
922  else
923    igncr = 0;
924
925  /* Break handling */
926  if ( t->c_iflag & IGNBRK ) {
927    ignbrk = 0x10;              /* Ignore break on input */
928    brkint = 0x08;
929  } else {
930    if ( t->c_iflag & BRKINT ) {
931      ignbrk = 0;               /* Generate SIGINT (interrupt ) */
932      brkint = 0;
933    } else {
934      ignbrk = 0;               /* Convert to 0x00 */
935      brkint = 0x08;
936    }
937  }
938
939  /* Stripping */
940  if ( t->c_iflag & ISTRIP )
941    istrip = 0x80;              /* Strip to 7 bits */
942  else
943    istrip = 0;                 /* Leave as 8 bits */
944
945  rx_period = cd2401_bitrate_divisor( 20000000Ul, &in_baud );
946  tx_period = cd2401_bitrate_divisor( 20000000Ul, &out_baud );
947
948  /*
949   *  If this is the first time that the line characteristics are set up, then
950   *  the device must be re-initialized.
951   *  Also check if we need to change anything. It is preferable to not touch
952   *  the device if nothing changes. As soon as we touch it, it tends to
953   *  glitch. If anything changes, we reprogram all registers. This is
954   *  harmless.
955   */
956  if ( ( CD2401_Channel_Info[minor].tty == 0 ) ||
957       ( cd2401->cor1 != (parodd | parenb | ignpar | csize) ) ||
958       ( cd2401->cor2 != (sw_flow_ctl | hw_flow_ctl) ) ||
959       ( cd2401->cor3 != (extra_flow_ctl | cstopb) )  ||
960       ( cd2401->cor6 != (igncr | icrnl | inlcr | ignbrk | brkint | parmrk | inpck) ) ||
961       ( cd2401->cor7 != istrip ) ||
962       ( cd2401->u1.async.schr1 != t->c_cc[VSTART] ) ||
963       ( cd2401->u1.async.schr2 != t->c_cc[VSTOP] ) ||
964       ( cd2401->rbpr != (unsigned char)rx_period ) ||
965       ( cd2401->rcor != (unsigned char)(rx_period >> 8) ) ||
966       ( cd2401->tbpr != (unsigned char)tx_period ) ||
967       ( cd2401->tcor != ( (tx_period >> 3) & 0xE0 ) ) )
968    need_reinitialization = TRUE;
969
970  /* Write to the ports */
971  rtems_interrupt_disable (level);
972
973  cd2401->car = minor;          /* Select channel */
974  read_enabled = cd2401->csr & 0x80 ? TRUE : FALSE;
975
976  if ( (t->c_cflag & CREAD ? TRUE : FALSE ) != read_enabled ) {
977    /* Read enable status is changing */
978    need_reinitialization = TRUE;
979  }
980
981  if ( need_reinitialization ) {
982    /*
983     *  Could not find a way to test whether the CD2401 was done transmitting.
984     *  The TxEmpty interrupt does not seem to indicate that the FIFO is empty
985     *  in DMA mode. So, just wait a while for output to drain. May not be
986     *  enough, but it will have to do (should be long enough for 1 char at
987     *  9600 bsp)...
988     */
989    cd2401_udelay( 2000L );
990
991    /* Clear channel */
992    cd2401_chan_cmd (minor, 0x40, 1);
993
994    cd2401->car = minor;    /* Select channel */
995    cd2401->cmr = 0x42;     /* Interrupt Rx, DMA Tx, async mode */
996    cd2401->cor1 = parodd | parenb | ignpar | csize;
997    cd2401->cor2 = sw_flow_ctl | hw_flow_ctl;
998    cd2401->cor3 = extra_flow_ctl | cstopb;
999    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 */
1075int 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 */
1116int 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 */
1155ssize_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 */
1219int 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 */
1252int _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 */
1310ssize_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 */
1353static 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 */
1391static 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 */
1414void _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  if ( c == '\n' )
1430      _167Bug_pollWrite(printk_minor, &cr_char, 1);
1431}
1432
1433/*
1434 ***************
1435 * BOILERPLATE *
1436 ***************
1437 *
1438 *  All these functions are prototyped in rtems/c/src/lib/include/console.h.
1439 */
1440
1441/*
1442 * Initialize and register the device
1443 */
1444rtems_device_driver console_initialize(
1445  rtems_device_major_number  major,
1446  rtems_device_minor_number  minor,
1447  void                      *arg
1448)
1449{
1450  rtems_status_code status;
1451  rtems_device_minor_number console_minor;
1452
1453  /*
1454   * Set up TERMIOS if needed
1455   */
1456  if ( NVRAM_CONFIGURE ) {
1457    /* J1-4 is on, use NVRAM info for configuration */
1458    console_minor = nvram->console_printk_port & 0x03;
1459
1460    if ( nvram->console_mode & 0x01 )
1461      /* termios */
1462      rtems_termios_initialize ();
1463  }
1464  else {
1465    console_minor = CONSOLE_MINOR;
1466#if CD2401_USE_TERMIOS == 1
1467    rtems_termios_initialize ();
1468#endif
1469  }
1470
1471  /*
1472   * Do device-specific initialization
1473   * Does not affect 167-Bug.
1474   */
1475  cd2401_initialize ();
1476
1477  /*
1478   * Register the devices
1479   */
1480  status = rtems_io_register_name ("/dev/tty0", major, 0);
1481  if (status != RTEMS_SUCCESSFUL)
1482    rtems_fatal_error_occurred (status);
1483
1484  status = rtems_io_register_name ("/dev/tty1", major, 1);
1485  if (status != RTEMS_SUCCESSFUL)
1486    rtems_fatal_error_occurred (status);
1487
1488  status = rtems_io_register_name ("/dev/console", major, console_minor);
1489  if (status != RTEMS_SUCCESSFUL)
1490    rtems_fatal_error_occurred (status);
1491
1492  status = rtems_io_register_name ("/dev/tty2", major, 2);
1493  if (status != RTEMS_SUCCESSFUL)
1494    rtems_fatal_error_occurred (status);
1495
1496  status = rtems_io_register_name ("/dev/tty3", major, 3);
1497  if (status != RTEMS_SUCCESSFUL)
1498    rtems_fatal_error_occurred (status);
1499
1500  return RTEMS_SUCCESSFUL;
1501}
1502
1503/*
1504 * Open the device
1505 */
1506rtems_device_driver console_open(
1507  rtems_device_major_number major,
1508  rtems_device_minor_number minor,
1509  void                    * arg
1510)
1511{
1512  static const rtems_termios_callbacks pollCallbacks = {
1513    NULL,                       /* firstOpen */
1514    NULL,                       /* lastClose */
1515    _167Bug_pollRead,           /* pollRead */
1516    _167Bug_pollWrite,          /* write */
1517    NULL,                       /* setAttributes */
1518    NULL,                       /* stopRemoteTx */
1519    NULL,                       /* startRemoteTx */
1520    0                           /* outputUsesInterrupts */
1521  };
1522
1523  static const rtems_termios_callbacks intrCallbacks = {
1524    cd2401_firstOpen,           /* firstOpen */
1525    cd2401_lastClose,           /* lastClose */
1526    NULL,                       /* pollRead */
1527    cd2401_write,               /* write */
1528    cd2401_setAttributes,       /* setAttributes */
1529    cd2401_stopRemoteTx,        /* stopRemoteTx */
1530    cd2401_startRemoteTx,       /* startRemoteTx */
1531    1                           /* outputUsesInterrupts */
1532  };
1533
1534  if ( NVRAM_CONFIGURE )
1535    /* J1-4 is on, use NVRAM info for configuration */
1536    if ( nvram->console_mode & 0x01 )
1537      /* termios */
1538      if ( nvram->console_mode & 0x02 )
1539        /* interrupt-driven I/O */
1540        return rtems_termios_open (major, minor, arg, &intrCallbacks);
1541            else
1542        /* polled I/O */
1543        return rtems_termios_open (major, minor, arg, &pollCallbacks);
1544          else
1545            /* no termios -- default to polled I/O */
1546            return RTEMS_SUCCESSFUL;
1547#if CD2401_USE_TERMIOS == 1
1548#if CD2401_IO_MODE != 1
1549  else
1550    /* termios & polled I/O*/
1551    return rtems_termios_open (major, minor, arg, &pollCallbacks);
1552#else
1553  else
1554    /* termios & interrupt-driven I/O*/
1555    return rtems_termios_open (major, minor, arg, &intrCallbacks);
1556#endif
1557#else
1558  else
1559    /* no termios -- default to polled I/O */
1560    return RTEMS_SUCCESSFUL;
1561#endif
1562}
1563
1564/*
1565 * Close the device
1566 */
1567rtems_device_driver console_close(
1568  rtems_device_major_number major,
1569  rtems_device_minor_number minor,
1570  void                    * arg
1571)
1572{
1573  if ( NVRAM_CONFIGURE ) {
1574    /* J1-4 is on, use NVRAM info for configuration */
1575    if ( nvram->console_mode & 0x01 )
1576      /* termios */
1577      return rtems_termios_close (arg);
1578    else
1579      /* no termios */
1580      return RTEMS_SUCCESSFUL;
1581  }
1582#if CD2401_USE_TERMIOS == 1
1583  else
1584    /* termios */
1585    return rtems_termios_close (arg);
1586#else
1587  else
1588    /* no termios */
1589    return RTEMS_SUCCESSFUL;
1590#endif
1591}
1592
1593/*
1594 * Read from the device
1595 */
1596rtems_device_driver console_read(
1597  rtems_device_major_number major,
1598  rtems_device_minor_number minor,
1599  void                    * arg
1600)
1601{
1602  if ( NVRAM_CONFIGURE ) {
1603    /* J1-4 is on, use NVRAM info for configuration */
1604    if ( nvram->console_mode & 0x01 )
1605      /* termios */
1606      return rtems_termios_read (arg);
1607    else
1608      /* no termios -- default to polled */
1609      return do_poll_read (major, minor, arg);
1610  }
1611#if CD2401_USE_TERMIOS == 1
1612  else
1613    /* termios */
1614    return rtems_termios_read (arg);
1615#else
1616  else
1617    /* no termios -- default to polled */
1618    return do_poll_read (major, minor, arg);
1619#endif
1620}
1621
1622/*
1623 * Write to the device
1624 */
1625rtems_device_driver console_write(
1626  rtems_device_major_number major,
1627  rtems_device_minor_number minor,
1628  void                    * arg
1629)
1630{
1631  if ( NVRAM_CONFIGURE ) {
1632    /* J1-4 is on, use NVRAM info for configuration */
1633    if ( nvram->console_mode & 0x01 )
1634      /* termios */
1635      return rtems_termios_write (arg);
1636    else
1637      /* no termios -- default to polled */
1638      return do_poll_write (major, minor, arg);
1639  }
1640#if CD2401_USE_TERMIOS == 1
1641  else
1642    /* termios */
1643    return rtems_termios_write (arg);
1644#else
1645  else
1646    /* no termios -- default to polled */
1647    return do_poll_write (major, minor, arg);
1648#endif
1649}
1650
1651/*
1652 * Handle ioctl request.
1653 */
1654rtems_device_driver console_control(
1655  rtems_device_major_number major,
1656  rtems_device_minor_number minor,
1657  void                    * arg
1658)
1659{
1660  if ( NVRAM_CONFIGURE ) {
1661    /* J1-4 is on, use NVRAM info for configuration */
1662    if ( nvram->console_mode & 0x01 )
1663      /* termios */
1664      return rtems_termios_ioctl (arg);
1665    else
1666      /* no termios -- default to polled */
1667      return RTEMS_SUCCESSFUL;
1668  }
1669#if CD2401_USE_TERMIOS == 1
1670  else
1671    /* termios */
1672    return rtems_termios_ioctl (arg);
1673#else
1674  else
1675    /* no termios -- default to polled */
1676    return RTEMS_SUCCESSFUL;
1677#endif
1678}
Note: See TracBrowser for help on using the repository browser.