⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 console.c

📁 RTEMS (Real-Time Executive for Multiprocessor Systems) is a free open source real-time operating sys
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  console.c * *  This file contains the MVME167 termios console package. Only asynchronous *  I/O is supported. * *  /dev/tty0 is channel 0, Serial Port 1/Console on the MVME712M. *  /dev/tty1 is channel 1, Serial Port 2/TTY01 on the MVME712M. *  /dev/tty2 is channel 2, Serial Port 3 on the MVME712M. *  /dev/tty3 is channel 3, Serial Port 4 on the MVME712M. * *  Normal I/O uses DMA for output, interrupts for input. /dev/console is *  fixed to be /dev/tty01, Serial Port 2. Very limited support is provided *  for polled I/O. Polled I/O is intended only for running the RTEMS test *  suites. In all cases, Serial Port 1/Console is allocated to 167Bug and *  is the dedicated debugger port. We configure GDB to use 167Bug for *  debugging. When debugging with GDB or 167Bug, do not open /dev/tty00. * *  Modern I/O chips often contain a number of I/O devices that can operate *  almost independently of each other. Typically, in RTEMS, all devices in *  an I/O chip are handled by a single device driver, but that need not be *  always the case. Each device driver must supply six entry points in the *  Device Driver Table: a device initialization function, as well as an open, *  close, read, write and a control function. RTEMS assigns a device major *  number to each device driver. This major device number is the index of the *  device driver entries in the Device Driver Table, and it used to identify *  a particular device driver. To distinguish multiple I/O sub-devices within *  an I/O chip, RTEMS supports device minor numbers. When a I/O device is *  initialized, the major number is supplied to the initialization function. *  That function must register each sub-device with a separate name and minor *  number (as well as the supplied major number). When an application opens a *  device by name, the corresponding major and minor numbers are returned to *  the caller to be used in subsequent I/O operations (although these details *  are typically hidden within the library functions). * *  Such a scheme recognizes that the initialization of the individual *  sub-devices is generally not completely independent. For example, the *  four serial ports of the CD2401 can be configured almost independently *  from each other. One port could be configured to operate in asynchronous *  mode with interrupt-driven I/O, while another port could be configured to *  operate in HDLC mode with DMA I/O. However, a device reset command will *  reset all four channels, and the width of DMA transfers and the number of *  retries following bus errors selected applies to all four channels. *  Consequently, when initializing one channel, one must be careful not to *  destroy the configuration of other channels that are already configured. * *  One problem with the RTEMS I/O initialization model is that no information *  other than a device major number is passed to the initialization function. *  Consequently, the sub-devices must be initialized with some pre-determined *  configuration. To change the configuration of a sub-device, it is *  necessary to either rewrite the initialization function, or to make a *  series of rtems_io_control() calls after initialization. The first *  approach is not very elegant. The second approach is acceptable if an *  application is simply changing baud rates, parity or other such *  asynchronous parameters (as supplied by the termios package). But what if *  an application requires one channel to run in HDLC or Bisync mode and *  another in async mode? With a single driver per I/O chip approach, the *  device driver must support multiple protocols. This is feasible, but it *  often means that an application that only does asynchronous I/O now links *  in code for other unused protocols, thus wasting precious ROM space. *  Worse, it requires that the sub-devices be initialized in some *  configuration, and that configuration then changed through a series of *  device driver control calls. There is no standard API in RTEMS to switch *  a serial line to some synchronous protocol. * *  A better approach is to treat each channel as a separate device, each with *  its own device device driver. The application then supplies its own device *  driver table with only the required protocols (drivers) on each line. The *  problem with this approach is that the device drivers are not really *  independent, given that the I/O sub-devices within a common chip are not *  independent themselves. Consequently, the related device drivers must *  share some information. In RTEMS, there is no standard location in which *  to share information. * *  This driver handles all four channels, i.e. it distinguishes the *  sub-devices using minor device numbers. Only asynchronous I/O is *  supported. The console is currently fixed to be channel 1 on the CD2401, *  which corresponds to the TTY01 port (Serial Port 2) on the MVME712M *  Transition Module. * *  The CD2401 does either interrupt-driven or DMA I/O; it does not support *  polling. In interrupt-driven or DMA I/O modes, interrupts from the CD2401 *  are routed to the MC68040, and the processor generates an interrupt *  acknowledge cycle directly to the CD2401 to obtain an interrupt vector. *  The PCCchip2 supports a pseudo-polling mode in which interrupts from the *  CD2401 are not routed to the MC68040, but can be detected by the processor *  by reading the appropriate CD2401 registers. In this mode, interrupt *  acknowledge cycles must be generated to the CD2401 by reading the *  appropriate PCCchip2 registers. * *  Interrupts from the four channels cannot be routed independently; either *  all channels are used in the pseudo-polling mode, or all channels are used *  in interrupt-driven/DMA mode. There is no advantage in using the speudo- *  polling mode. Consenquently, this driver performs DMA input and output. *  Output is performed directly from the termios raw output buffer, while *  input is accumulated into a separate buffer. * *  THIS MODULE IS NOT RE-ENTRANT! Simultaneous access to a device from *  multiple tasks is likely to cause significant problems! Concurrency *  control is implemented in the termios package. * *  THE INTERRUPT LEVEL IS SET TO 1 FOR ALL CHANNELS. *  If the CD2401 is to be used for high speed synchronous serial I/O, the *  interrupt priority might need to be increased. * *  ALL INTERRUPT HANDLERS ARE SHARED. *  When adding extra device drivers, either rewrite the interrupt handlers *  to demultiplex the interrupts, or install separate vectors. Common vectors *  are currently used to catch spurious interrupts. We could already have *  installed separate vectors for each channel and used the spurious *  interrupt handler defined in some other BSPs, but handling spurious *  interrupts from the CD2401 in this device driver allows us to record more *  information on the source of the interrupts. Furthermore, we have observed *  the occasional spurious interrupt from channel 0. We definitely do not *  to call a debugger for those. * *  All page references are to the MVME166/MVME167/MVME187 Single Board *  Computer Programmer's Reference Guide (MVME187PG/D2) with the April *  1993 supplements/addenda (MVME187PG/D2A1). * *  Copyright (c) 1998, National Research Council of Canada * *  The license and distribution terms for this file may be *  found in the file LICENSE in this distribution or at *  http://www.rtems.com/license/LICENSE. */#define M167_INIT#include <stdarg.h>#include <stdio.h>#include <termios.h>#include <bsp.h>                /* Must be before libio.h */#include <rtems/libio.h>/* Utility functions */void cd2401_udelay( unsigned long delay );void cd2401_chan_cmd( rtems_unsigned8 channel, rtems_unsigned8 cmd, rtems_unsigned8 wait );rtems_unsigned16 cd2401_bitrate_divisor( rtems_unsigned32 clkrate, rtems_unsigned32* bitrate );void cd2401_initialize( void );void cd2401_interrupts_initialize( rtems_boolean enable );/* ISRs */rtems_isr cd2401_modem_isr( rtems_vector_number vector );rtems_isr cd2401_re_isr( rtems_vector_number vector );rtems_isr cd2401_rx_isr( rtems_vector_number vector );rtems_isr cd2401_tx_isr( rtems_vector_number vector );/* Termios callbacks */int cd2401_firstOpen( int major, int minor, void *arg );int cd2401_lastClose( int major, int minor, void *arg );int cd2401_setAttributes( int minor, const struct termios *t );int cd2401_startRemoteTx( int minor );int cd2401_stopRemoteTx( int minor );int cd2401_write( int minor, const char *buf, int len );int cd2401_drainOutput( int minor );int _167Bug_pollRead( int minor );int _167Bug_pollWrite( int minor, const char *buf, int len );/* Printk function */static void _BSP_output_char( char c );BSP_output_char_function_type BSP_output_char = _BSP_output_char;/* Channel info *//* static */ volatile struct {  void *tty;                    /* Really a struct rtems_termios_tty * */  int len;                      /* Record nb of chars being TX'ed */  const char *buf;              /* Record where DMA is coming from */  rtems_unsigned32 spur_cnt;    /* Nb of spurious ints so far */  rtems_unsigned32 spur_dev;    /* Indo on last spurious int */  rtems_unsigned32 buserr_addr; /* Faulting address */  rtems_unsigned32 buserr_type; /* Reason of bus error during DMA */  rtems_unsigned8  own_buf_A;   /* If true, buffer A belongs to the driver */  rtems_unsigned8  own_buf_B;   /* If true, buffer B belongs to the driver */  rtems_unsigned8  txEmpty;     /* If true, the output FIFO should be empty */} CD2401_Channel_Info[4];/* *  The number of channels already opened. If zero, enable the interrupts. The *  initial value must be 0. If initialized explicitly, the variable ends up *  in the .data section. Its value is not re-initialized on system restart. *  Furthermore, because the variable is changed, the .data section would not *  be ROMable. We thus leave the variable uninitialized, which causes it to *  be allocated in the .bss section, and rely on RTEMS to zero the .bss *  section on every startup. */rtems_unsigned8 Init_count;/* Record previous handlers */rtems_isr_entry Prev_re_isr;        /* Previous rx exception isr */rtems_isr_entry Prev_rx_isr;        /* Previous rx isr */rtems_isr_entry Prev_tx_isr;        /* Previous tx isr */rtems_isr_entry Prev_modem_isr;     /* Previous modem/timer isr *//* Define the following symbol to trace the calls to this driver *//* #define CD2401_RECORD_DEBUG_INFO */#include "console-recording.c"/* *  Utility functions. *//* *  Assumes that clock ticks 1 million times per second. * *  MAXIMUM DELAY IS ABOUT 20 ms * *  Input parameters: *    delay: Number of microseconds to delay. * *  Output parameters: NONE * *  Return values: NONE */ void cd2401_udelay(  unsigned long delay){  unsigned long i = 20000;  /* In case clock is off */  rtems_interval ticks_per_second, start_ticks, end_ticks, current_ticks;      rtems_clock_get( RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticks_per_second );  rtems_clock_get( RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &start_ticks );  end_ticks = start_ticks + delay;    do {    rtems_clock_get(RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &current_ticks);  } while ( --i && (current_ticks <= end_ticks) );    CD2401_RECORD_DELAY_INFO(( start_ticks, end_ticks, current_ticks, i )); }/* *  cd2401_chan_cmd * *  Sends a CCR command to the specified channel. Waits for any unfinished *  previous command to complete, then sends the specified command. Optionally *  wait for the current command to finish before returning. * *  Input parameters: *    channel - CD2401 channel number *    cmd  - command byte *    wait - if non-zero, wait for specified command to complete before *          returning. * *  Output parameters: NONE * *  Return values: NONE */void cd2401_chan_cmd(  rtems_unsigned8 channel,  rtems_unsigned8 cmd,  rtems_unsigned8 wait){  if ( channel < 4 ) {    cd2401->car = channel;      /* Select channel */    while ( cd2401->ccr != 0 ); /* Wait for completion of previous command */    cd2401->ccr = cmd;          /* Send command */    if ( wait )      while( cd2401->ccr != 0 );/* Wait for completion */  }  else {    /* This may not be the best error message */    rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );  }}/* *  cd2401_bitrate_divisor * *  Compute the divisor and clock source to use to obtain the desired bitrate. * *  Input parameters: *    clkrate - system clock rate (CLK input frequency) *    bitrate - the desired bitrate * *  Output parameters: *    bitrate - The actual bitrate achievable, to the nearest bps. * *  Return values: *    Returns divisor in lower byte and clock source in upper byte for the *    specified bitrate. */rtems_unsigned16 cd2401_bitrate_divisor(  rtems_unsigned32 clkrate,  rtems_unsigned32* bitrate){  rtems_unsigned32 divisor;  rtems_unsigned16 clksource;  divisor = *bitrate << 3;          /* temporary; multiply by 8 for CLK/8 */  divisor = (clkrate + (divisor>>1)) / divisor; /* divisor for clk0 (CLK/8) */  /* Use highest speed clock source for best precision - try clk0 to clk4 */  for( clksource = 0; clksource < 0x0400 && divisor > 0x100; clksource += 0x0100 )      divisor >>= 2;  divisor--;                        /* adjustment, see specs */  if( divisor < 1 )    divisor = 1;  else if( divisor > 0xFF )    divisor = 0xFF;  *bitrate = clkrate / (1 << ((clksource >> 7)+3)) / (divisor+1);  return( clksource | divisor );}/* *  cd2401_initialize * *  Initializes the CD2401 device. Individual channels on the chip are left in *  their default reset state, and should be subsequently configured. * *  Input parameters: NONE * *  Output parameters:  NONE * *  Return values: NONE */void cd2401_initialize( void ){  int i;  for ( i = 3; i >= 0; i-- ) {    CD2401_Channel_Info[i].tty = NULL;    CD2401_Channel_Info[i].len = 0;    CD2401_Channel_Info[i].buf = NULL;    CD2401_Channel_Info[i].spur_cnt = 0;    CD2401_Channel_Info[i].spur_dev = 0;    CD2401_Channel_Info[i].buserr_type = 0;    CD2401_Channel_Info[i].buserr_addr = 0;    CD2401_Channel_Info[i].own_buf_A = TRUE;    CD2401_Channel_Info[i].own_buf_B = TRUE;    CD2401_Channel_Info[i].txEmpty = TRUE;  } /*  *  Normally, do a device reset here. If we do it, we will most likely clober  *  the port settings for 167Bug on channel 0. So we just shut up all the  *  ports by disabling their interrupts.  */#if 0  cd2401->gfrcr = 0;            /* So we can detect that device init is done */  cd2401_chan_cmd( 0x10, 0);    /* Reset all */  while(cd2401->gfrcr == 0);    /* Wait for reset all */#endif  /*   *  The CL-CD2400/2401 manual (part no 542400-003) states on page 87 that   *  the LICR "contains the number of the interrupting channel being served.   *  The channel number is always that of the current acknowledged interrupt."   *  THE USER MUST PROGRAM CHANNEL NUMBER IN LICR! It is not set automatically   *  by the hardware, as suggested by the manual.   *   *  The updated manual (part no 542400-007) has the story straight. The   *  CD2401 automatically initializes the LICR to contain the channel number   *  in bits 2 and 3. However, these bits are not preserved when the user   *  defined bits are written.   *   *  The same vector number is used for all four channels. Different vector   *  numbers could be programmed for each channel, thus avoiding the need to   *  demultiplex the interrupts in the ISR.   */  for ( i = 0; i < 4; i++ ) {    cd2401->car = i;            /* Select channel */    cd2401->livr = 0x5C;        /* Motorola suggested value p. 3-15 */    cd2401->licr = i << 2;      /* Don't rely on reset value */    cd2401->ier = 0;            /* Disable all interrupts */  }  /*   *  The content of the CD2401 xpilr registers must match the A7-A0 addresses   *  generated by the PCCchip2 during interrupt acknowledge cycles in order   *  for the CD2401 to recognize the IACK cycle and clear its interrupt   *  request.   */  cd2401->mpilr = 0x01;         /* Match pccchip2->modem_piack p. 3-27 */  cd2401->tpilr = 0x02;         /* Match pccchip2->tx_piack p. 3-28 */  cd2401->rpilr = 0x03;         /* Match pccchip2->rx_piack p. 3-29 */  /* Global CD2401 registers */  cd2401->dmr = 0;              /* 16-bit DMA transfers when possible */  cd2401->bercnt = 0;           /* Do not retry DMA upon bus errors */  /*   *  Setup timer prescaler period, which clocks timers 1 and 2 (or rx timeout   *  and tx delay). The prescaler is clocked by the system clock) / 2048. The   *  register must be in the range 0x0A..0xFF, ie. a rescaler period range of   *  about 1ms..26ms for a nominal system clock rate  of 20MHz.   */  cd2401->tpr  = 0x0A;          /* Same value as 167Bug */}/* *  cd2401_interrupts_initialize * *  This routine enables or disables the CD2401 interrupts to the MC68040. *  Interrupts cannot be enabled/disabled on a per-channel basis. * *  Input parameters: *    enable - if true, enable the interrupts, else disable them. * *  Output parameters:  NONE * *  Return values: NONE * *  THE FIRST CD2401 CHANNEL OPENED SHOULD ENABLE INTERRUPTS. *  THE LAST CD2401 CHANNEL CLOSED SHOULD DISABLE INTERRUPTS. */void cd2401_interrupts_initialize(  rtems_boolean enable){  if ( enable ) {   /*    *  Enable interrupts from the CD2401 in the PCCchip2.    *  During DMA transfers, the MC68040 supplies dirty data during read cycles    *  from the CD2401 and leaves the data dirty in its data cache if there is    *  a cache hit. The MC68040 updates the data cache during write cycles from    *  the CD2401 if there is a cache hit.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -