summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/mpc860/console-generic/console-generic.c
blob: 76c1cb031454779b1bc8a47058fd1db7ae830d47 (plain) (tree)

































                                                                  
                            
                                                    








                                                           
                  








                                                                       




















                                                                      











                                                           

                                                               












                                                                          

                          
























































































































































































































































































                                                                               

                                               









































                                                             
                                           



















































































































































































































































































































































                                                                         






















































                                                                         

                                                












































                                                                       

                                                                  
                                                                  

                                    


















































                                                                            
/*
 *  General Serial I/O functions.
 *
 * This file contains the functions for performing serial I/O.
 * The actual system calls (console_*) should be in the BSP part
 * of the source tree. That way different BSPs can use whichever
 * SMCs and SCCs they want. Originally, all the stuff was in
 * this file, and it caused problems with one BSP using SCC2 
 * as /dev/console, others using SMC1 for /dev/console, etc.
 *
 *  On-chip resources used:
 *   resource   minor                note
 *    SMC1       0
 *    SMC2       1
 *    SCC1       2                   (shared with ethernet driver)
 *    SCC2       3
 *    SCC3       4
 *    SCC4       5
 *    BRG1
 *    BRG2
 *    BRG3
 *    BRG4
 *  Author: Jay Monkman (jmonkman@frasca.com)
 *  Copyright (C) 1998 by Frasca International, Inc.
 *
 *  Derived from c/src/lib/libbsp/m68k/gen360/console/console.c:
 *
 *  Author:
 *    W. Eric Norum
 *    Saskatchewan Accelerator Laboratory
 *    University of Saskatchewan
 *    Saskatoon, Saskatchewan, CANADA
 *    eric@skatter.usask.ca
 *
 *  COPYRIGHT (c) 1989-1999.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 */

#include <rtems.h>
#include <rtems/libio.h>
#include <mpc860.h>
#include <mpc860/console.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>

#define NIFACES 6        /* number of console devices (serial ports) */

static Buf_t *rxBufList[NIFACES];
static Buf_t *rxBufListTail[NIFACES];

/*
 * Interrupt-driven input buffer
 */
#define RXBUFSIZE       16


/*
 * I/O buffers and pointers to buffer descriptors
 */
static volatile char txBuf[NIFACES];

static volatile m860BufferDescriptor_t *RxBd[NIFACES], *TxBd[NIFACES];

/*
 * Device-specific routines
 */
static int m860_get_brg_cd(int);
unsigned char m860_get_brg_clk(int);
unsigned char m860_get_brg_clk(int);


/*
 * Compute baud-rate-generator configuration register value
 */
static int
m860_get_brg_cd (int baud)
{
  int divisor;
  int div16 = 0;
  
  divisor = ((rtems_cpu_configuration_get_clock_speed() / 16) +
                 (baud / 2)) / baud;
  if (divisor > 4096) {
    div16 = 1;
    divisor = (divisor + 8) / 16;
  }
  return M860_BRG_EN | M860_BRG_EXTC_BRGCLK | 
    ((divisor - 1) << 1) | div16;
}


/* this function will fail if more that 4 baud rates have been selected */
/*  at any time since the OS started. It needs to be fixed. FIXME */
unsigned char m860_get_brg_clk(int baud)
{
  static int  brg_spd[4];
  static char brg_used[4];
  int i;

  /* first try to find a BRG that is already at the right speed */
  for (i=0; i<4; i++) {
    if (brg_spd[i] == baud) {
      break;
    }
  }

  if (i==4) { /* I guess we didn't find one */
    for (i=0; i<4; i++) {
      if (brg_used[i] == 0) {
        break;
      }
    }
  }
  if (i != 4) {
    brg_used[i]++;
    brg_spd[i]=baud;
    switch (i) {
    case 0:
      m860.brgc1 = M860_BRG_RST;
      m860.brgc1 = m860_get_brg_cd(baud);
      break;
    case 1:
      m860.brgc2 = M860_BRG_RST;
      m860.brgc2 = m860_get_brg_cd(baud);
      break;
    case 2:
      m860.brgc3 = M860_BRG_RST;
      m860.brgc3 = m860_get_brg_cd(baud);
      break;
    case 3:
      m860.brgc4 = M860_BRG_RST;
      m860.brgc4 = m860_get_brg_cd(baud);
      break;
    }
    return i;
  }

  else 
    return 0xff;
}

/*
 * Hardware-dependent portion of tcsetattr().
 */
int
m860_smc_set_attributes (int minor, const struct termios *t)
{
  /*
   * minor must be 0 or 1
   */
  int baud;
  int brg;
  switch (t->c_cflag & CBAUD) {
  default:      baud = -1;      break;
  case B50:     baud = 50;      break;
  case B75:     baud = 75;      break;
  case B110:    baud = 110;     break;
  case B134:    baud = 134;     break;
  case B150:    baud = 150;     break;
  case B200:    baud = 200;     break;
  case B300:    baud = 300;     break;
  case B600:    baud = 600;     break;
  case B1200:   baud = 1200;    break;
  case B1800:   baud = 1800;    break;
  case B2400:   baud = 2400;    break;
  case B4800:   baud = 4800;    break;
  case B9600:   baud = 9600;    break;
  case B19200:  baud = 19200;   break;
  case B38400:  baud = 38400;   break;
  case B57600:  baud = 57600;   break;
  case B115200: baud = 115200;  break;
  case B230400: baud = 230400;  break;
  case B460800: baud = 460800;  break;
  }
  if (baud > 0) {
    brg = m860_get_brg_clk(baud);     /* 4 BRGs, 6 serial ports - hopefully */ 
                                      /*  at least 2 ports will be the same */
    m860.simode |= brg << (12 + ((minor) * 16));
  }
  return 0;
}

int
m860_scc_set_attributes (int minor, const struct termios *t)
{
  /*
   * minor must be 2, 3, 4 or 5
   */
  int baud;
  int brg;
  switch (t->c_cflag & CBAUD) {
  default:      baud = -1;      break;
  case B50:     baud = 50;      break;
  case B75:     baud = 75;      break;
  case B110:    baud = 110;     break;
  case B134:    baud = 134;     break;
  case B150:    baud = 150;     break;
  case B200:    baud = 200;     break;
  case B300:    baud = 300;     break;
  case B600:    baud = 600;     break;
  case B1200:   baud = 1200;    break;
  case B1800:   baud = 1800;    break;
  case B2400:   baud = 2400;    break;
  case B4800:   baud = 4800;    break;
  case B9600:   baud = 9600;    break;
  case B19200:  baud = 19200;   break;
  case B38400:  baud = 38400;   break;
  case B57600:  baud = 57600;   break;
  case B115200: baud = 115200;  break;
  case B230400: baud = 230400;  break;
  case B460800: baud = 460800;  break;
  }
  if (baud > 0) {
    brg = m860_get_brg_clk(baud);     /* 4 BRGs, 5 serial ports - hopefully */ 
                                      /*  at least 2 ports will be the same */
    m860.sicr |= (brg << (3 + ((minor-2) * 8))) | 
                 (brg << ((minor-2) * 8));
  }
  return 0;
}

void
m860_scc_initialize (int port)  /* port is the SCC # (i.e. 1, 2, 3 or 4) */
{
  unsigned char                brg;
  volatile m860SCCparms_t     *sccparms;
  volatile m860SCCRegisters_t *sccregs;

  /*
   * Allocate buffer descriptors
   */
  RxBd[port+1] = M860AllocateBufferDescriptors(1);
  TxBd[port+1] = M860AllocateBufferDescriptors(1);
  
  /*
   * Configure ports A and B to enable TXDx and RXDx pins
   */
  m860.papar |=  (0xC << ((port-2) * 2));
  m860.padir &= ~(0xC << ((port-2) * 2));
  m860.pbdir |=  (0x04 << (port-2));
  m860.paodr &= ~(0x8 << ((port-2) * 2));
  m860.pbdat &= ~(0x04 << (port-2));
  
  /* SCC2 is the only one with handshaking lines */
  /*
    if (port == 2) {
    m860.pcpar |= (0x02);
    m860.pcpar &= ~(0xc0);
    m860.pcdir &= ~(0xc2);
    m860.pcso  |= (0xc0);
    }
  */
  
  brg = m860_get_brg_clk(9600);     /* 4 BRGs, 5 serial ports - hopefully */ 
                                    /*  at least 2 ports will be the same */
  
  /*
   * Set up SDMA 
   */
  m860.sdcr = 0x01; /* as recommended p 16-80, sec 16.10.2.1 MPC860UM/AD */
  

  m860.sicr &= ~(0xff << ((port-1) * 8));
  m860.sicr |= (brg << (3 + ((port-1) * 8))) | (brg << ((port-1) * 8));
  
  /*
   * Set up SMC1 parameter RAM common to all protocols
   */
  if (port == 1) {
    sccparms = (m860SCCparms_t*)&m860.scc1p;
    sccregs = &m860.scc1;
  }
  else if (port == 2) {
    sccparms = &m860.scc2p;
    sccregs = &m860.scc2;
  }
  else if (port == 3) {
    sccparms = &m860.scc3p;
    sccregs = &m860.scc3;
  }
  else {
    sccparms = &m860.scc4p;
    sccregs = &m860.scc4;
  }
  
  sccparms->rbase = (char *)RxBd[port+1] - (char *)&m860;
  sccparms->tbase = (char *)TxBd[port+1] - (char *)&m860;
  
  if (port == 1)
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SCC1);
  else if (port == 2)
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SCC2);
  else if (port == 3)
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SCC3);
  else if (port == 4)
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SCC4);
  
  sccparms->rfcr = M860_RFCR_MOT | M860_RFCR_DMA_SPACE(0);
  sccparms->tfcr = M860_TFCR_MOT | M860_TFCR_DMA_SPACE(0);
  sccparms->mrblr = RXBUFSIZE;
  
  sccparms->un.uart.max_idl = 10;
  sccparms->un.uart.brklen = 0;
  sccparms->un.uart.brkec = 0;
  sccparms->un.uart.brkcr = 1;
  
  sccparms->un.uart.parec = 0;
  sccparms->un.uart.frmec = 0;
  sccparms->un.uart.nosec = 0;
  
  sccparms->un.uart.uaddr[0] = 0;
  sccparms->un.uart.uaddr[1] = 0;
  sccparms->un.uart.toseq  = 0;
  
  sccparms->un.uart.character[0] = 0x8000;
  sccparms->un.uart.character[1] = 0x8000;
  sccparms->un.uart.character[2] = 0x8000;
  sccparms->un.uart.character[3] = 0x8000;
  sccparms->un.uart.character[4] = 0x8000;
  sccparms->un.uart.character[5] = 0x8000;
  sccparms->un.uart.character[6] = 0x8000;
  sccparms->un.uart.character[7] = 0x8000;
  
  sccparms->un.uart.rccm = 0xc0ff;
  
  /*
   * Set up the Receive Buffer Descriptor
   */
  RxBd[port+1]->status = M860_BD_EMPTY | M860_BD_WRAP | 
    M860_BD_INTERRUPT;
  RxBd[port+1]->length = 0;
  RxBd[port+1]->buffer = malloc(RXBUFSIZE);
  
  /*
   * Setup the Transmit Buffer Descriptor
   */
  TxBd[port+1]->status = M860_BD_WRAP;
  
  /*
   * Set up SCCx general and protocol-specific mode registers
   */
  sccregs->scce = 0xffff;
  sccregs->sccm = 0x0000;
  sccregs->gsmr_h = 0x00000020;
  sccregs->gsmr_l = 0x00028004;
  sccregs->psmr   = 0x3000;
  sccregs->gsmr_l = 0x00028034;
}

void
m860_smc_initialize (int port)  /* port is the SMC number (i.e. 1 or 2) */
{
  unsigned char brg;

  /*
   * Allocate buffer descriptors
   */
  RxBd[port-1] = M860AllocateBufferDescriptors (1);
  TxBd[port-1] = M860AllocateBufferDescriptors (1);

  /*
   * Configure port B pins to enable SMTXDx and SMRXDx pins
   */
  m860.pbpar |=  (0xC0 << ((port-1) * 4));
  m860.pbdir &= ~(0xC0 << ((port-1) * 4));
  m860.pbdir |=  (0x01 << (port-1));
  m860.pbodr &= ~(0xC0 << ((port-1) * 4));
  m860.pbdat &= ~(0x01 << (port-1));
  
  /*
   * Set up BRG1 (9,600 baud)
   */
  brg = m860_get_brg_clk(9600);     /* 4 BRGs, 5 serial ports - hopefully */ 
                                    /*  at least 2 ports will be the same */

  /*
   * Put SMC in NMSI mode, connect SMC to BRG
   */
  m860.simode &= ~(0x7000 << ((port-1) * 16));
  m860.simode |= brg << (12 + ((port-1) * 16));

  /*
   * Set up SMC1 parameter RAM common to all protocols
   */
  if (port == 1) {
    m860.smc1p.rbase = (char *)RxBd[port-1] - (char *)&m860;
    m860.smc1p.tbase = (char *)TxBd[port-1] - (char *)&m860;
    m860.smc1p.rfcr = M860_RFCR_MOT | M860_RFCR_DMA_SPACE(0);
    m860.smc1p.tfcr = M860_TFCR_MOT | M860_TFCR_DMA_SPACE(0);
    m860.smc1p.mrblr = RXBUFSIZE;
    
    /*
     * Set up SMC1 parameter RAM UART-specific parameters
     */
    m860.smc1p.un.uart.max_idl = 10;
    m860.smc1p.un.uart.brklen = 0;
    m860.smc1p.un.uart.brkec = 0;
    m860.smc1p.un.uart.brkcr = 0;
    
  }
  else {
    m860.smc2p.rbase = (char *)RxBd[port-1] - (char *)&m860;
    m860.smc2p.tbase = (char *)TxBd[port-1] - (char *)&m860;
    m860.smc2p.rfcr = M860_RFCR_MOT | M860_RFCR_DMA_SPACE(0);
    m860.smc2p.tfcr = M860_TFCR_MOT | M860_TFCR_DMA_SPACE(0);
    m860.smc2p.mrblr = RXBUFSIZE;
    
    /*
     * Set up SMC2 parameter RAM UART-specific parameters
     */
    m860.smc2p.un.uart.max_idl = 10;
    m860.smc2p.un.uart.brklen = 0;
    m860.smc2p.un.uart.brkec = 0;
    m860.smc2p.un.uart.brkcr = 0;
  }      
  
  /*
   * Set up the Receive Buffer Descriptor
   */
  RxBd[port-1]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                  M860_BD_INTERRUPT;
  RxBd[port-1]->length = 0;
  RxBd[port-1]->buffer = malloc(RXBUFSIZE);
         
  /*
   * Setup the Transmit Buffer Descriptor
   */
  TxBd[port-1]->status = M860_BD_WRAP;
  
  /*
   * Set up SMCx general and protocol-specific mode registers
   */
  if (port == 1) {
    m860.smc1.smce = ~0;        /* Clear any pending events */
    m860.smc1.smcm = 0; /* Mask all interrupt/event sources */
    m860.smc1.smcmr = M860_SMCMR_CLEN(9) | M860_SMCMR_SM_UART;
    
    /*
     * Send "Init parameters" command
     */
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SMC1);
    
    /*
     * Enable receiver and transmitter
     */
    m860.smc1.smcmr |= M860_SMCMR_TEN | M860_SMCMR_REN;
  }
  else {
    m860.smc2.smce = ~0;        /* Clear any pending events */
    m860.smc2.smcm = 0; /* Mask all interrupt/event sources */
    m860.smc2.smcmr = M860_SMCMR_CLEN(9) | M860_SMCMR_SM_UART;
    
    /*
     * Send "Init parameters" command
     */
    M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SMC2);
    
    /*
     * Enable receiver and transmitter
     */
    m860.smc2.smcmr |= M860_SMCMR_TEN | M860_SMCMR_REN;
  }
}

int
m860_char_poll_read (int minor)
{
  unsigned char c;
  rtems_unsigned32 level;

  _CPU_ISR_Disable(level);  
  if (RxBd[minor]->status & M860_BD_EMPTY) {
    _CPU_ISR_Enable(level);
    return -1;
  }
  c = ((char *)RxBd[minor]->buffer)[0];
  RxBd[minor]->status = M860_BD_EMPTY | M860_BD_WRAP;
  _CPU_ISR_Enable(level);
  return c;
}

int
m860_char_poll_write (int minor, const char *buf, int len)
{
  while (len--) {
    while (TxBd[minor]->status & M860_BD_READY)
      continue;
    txBuf[minor] = *buf++;
    TxBd[minor]->buffer = &txBuf[minor];
    TxBd[minor]->length = 1;
    TxBd[minor]->status = M860_BD_READY | M860_BD_WRAP;
  }
  return 0;
}

/*
 * Interrupt handler
 */
rtems_isr
m860_scc1_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if ((m860.scc1.sccm & 0x1) && (m860.scc1.scce & 0x1)) {
    m860.scc1.scce = 0x1;
    /*    m860.scc1.sccm &= ~0x1;*/
    
    while ((RxBd[SCC1_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SCC1_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SCC1_MINOR]->next) {
        rxBufListTail[SCC1_MINOR] = rxBufListTail[SCC1_MINOR]->next;
        rxBufListTail[SCC1_MINOR]->buf = RxBd[SCC1_MINOR]->buffer;
        rxBufListTail[SCC1_MINOR]->len = RxBd[SCC1_MINOR]->length;
        rxBufListTail[SCC1_MINOR]->pos = 0;
        rxBufListTail[SCC1_MINOR]->next = 0;

        RxBd[SCC1_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SCC1_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 30;  /* Clear SCC1 interrupt-in-service bit */
}

rtems_isr
m860_scc2_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if ((m860.scc2.sccm & 0x1) && (m860.scc2.scce & 0x1)) {
    m860.scc2.scce = 0x1;
    /*    m860.scc2.sccm &= ~0x1;*/
    
    while ((RxBd[SCC2_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SCC2_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SCC2_MINOR]->next) {
        rxBufListTail[SCC2_MINOR] = rxBufListTail[SCC2_MINOR]->next;
        rxBufListTail[SCC2_MINOR]->buf = RxBd[SCC2_MINOR]->buffer;
        rxBufListTail[SCC2_MINOR]->len = RxBd[SCC2_MINOR]->length;
        rxBufListTail[SCC2_MINOR]->pos = 0;
        rxBufListTail[SCC2_MINOR]->next = 0;

        RxBd[SCC2_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SCC2_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 29;  /* Clear SCC2 interrupt-in-service bit */
}

rtems_isr
m860_scc3_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if ((m860.scc3.sccm & 0x1) && (m860.scc3.scce & 0x1)) {
    m860.scc3.scce = 0x1;
    /*    m860.scc3.sccm &= ~0x1;*/
    
    while ((RxBd[SCC3_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SCC3_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SCC3_MINOR]->next) {
        rxBufListTail[SCC3_MINOR] = rxBufListTail[SCC3_MINOR]->next;
        rxBufListTail[SCC3_MINOR]->buf = RxBd[SCC3_MINOR]->buffer;
        rxBufListTail[SCC3_MINOR]->len = RxBd[SCC3_MINOR]->length;
        rxBufListTail[SCC3_MINOR]->pos = 0;
        rxBufListTail[SCC3_MINOR]->next = 0;

        RxBd[SCC3_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SCC3_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 28;  /* Clear SCC3 interrupt-in-service bit */
}

rtems_isr
m860_scc4_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if ((m860.scc4.sccm & 0x1) && (m860.scc4.scce & 0x1)) {
    m860.scc4.scce = 0x1;
    /*    m860.scc4.sccm &= ~0x1;*/
    
    while ((RxBd[SCC4_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SCC4_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SCC4_MINOR]->next) {
        rxBufListTail[SCC4_MINOR] = rxBufListTail[SCC4_MINOR]->next;
        rxBufListTail[SCC4_MINOR]->buf = RxBd[SCC4_MINOR]->buffer;
        rxBufListTail[SCC4_MINOR]->len = RxBd[SCC4_MINOR]->length;
        rxBufListTail[SCC4_MINOR]->pos = 0;
        rxBufListTail[SCC4_MINOR]->next = 0;

        RxBd[SCC4_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SCC4_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 27;  /* Clear SCC4 interrupt-in-service bit */
}

rtems_isr
m860_smc1_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if (m860.smc1.smce & 0x1) {
    m860.smc1.smce = 0x1;
    /*    m860.scc2.sccm &= ~0x1;*/
    
    while ((RxBd[SMC1_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SMC1_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SMC1_MINOR]->next) {
        rxBufListTail[SMC1_MINOR] = rxBufListTail[SMC1_MINOR]->next;
        rxBufListTail[SMC1_MINOR]->buf = RxBd[SMC1_MINOR]->buffer;
        rxBufListTail[SMC1_MINOR]->len = RxBd[SMC1_MINOR]->length;
        rxBufListTail[SMC1_MINOR]->pos = 0;
        rxBufListTail[SMC1_MINOR]->next = 0;

        RxBd[SMC1_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SMC1_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 4;  /* Clear SMC1 interrupt-in-service bit */
}

rtems_isr
m860_smc2_console_interrupt_handler (rtems_vector_number v)
{
  /*
   * Buffer received?
   */
  if (m860.smc2.smce & 0x1) {
    m860.smc2.smce = 0x1;
    
    while ((RxBd[SMC2_MINOR]->status & M860_BD_EMPTY) == 0) {
      rxBufListTail[SMC2_MINOR]->next = malloc(sizeof(Buf_t));
      if (rxBufListTail[SMC2_MINOR]->next) {
        rxBufListTail[SMC2_MINOR] = rxBufListTail[SMC2_MINOR]->next;
        rxBufListTail[SMC2_MINOR]->buf = RxBd[SMC2_MINOR]->buffer;
        rxBufListTail[SMC2_MINOR]->len = RxBd[SMC2_MINOR]->length;
        rxBufListTail[SMC2_MINOR]->pos = 0;
        rxBufListTail[SMC2_MINOR]->next = 0;

        RxBd[SMC2_MINOR]->buffer = malloc(RXBUFSIZE);
      }
      RxBd[SMC2_MINOR]->status = M860_BD_EMPTY | M860_BD_WRAP | 
                                 M860_BD_INTERRUPT;
    }
  }
  
  /*
   * Buffer transmitted?
   */
#if 0
  if (m860.smc1.smce & 0x2) {
    m860.smc1.smce = 0x2;
    if ((smcTxBd->status & M360_BD_READY) == 0)
      rtems_termios_dequeue_characters (smc1ttyp, smcTxBd->length);
  }
#endif
  m860.cisr = 1UL << 3;  /* Clear SMC2 interrupt-in-service bit */
}


int
m860_buf_poll_read (int minor, char **buf)
{
  int len;

  if (RxBd[minor]->status & M860_BD_EMPTY)
    return -1;
  
  RxBd[minor]->buffer = malloc(RXBUFSIZE); /* I hope this succeeds ... */
  len = RxBd[minor]->length;
  RxBd[minor]->status = M860_BD_EMPTY | M860_BD_WRAP;
  
  return len;
}

int
m860_buf_poll_write (int minor, char *buf, int len)
{
  static char *last_buf[6];

  while (TxBd[minor]->status & M860_BD_READY)
    continue;
  if (last_buf[minor])
    free(last_buf[minor]);
  last_buf[minor] = buf;
  TxBd[minor]->buffer = buf;
  TxBd[minor]->length = len;
  TxBd[minor]->status = M860_BD_READY | M860_BD_WRAP;
  return 0;
}

void m860_console_initialize(void)
{
  int i;

  for (i=0; i < NIFACES; i++) {
    rxBufList[i] = malloc(sizeof(Buf_t));
    rxBufListTail[i] = rxBufList[i];
    rxBufList[i]->buf  = 0;
    rxBufList[i]->len  = 0;
    rxBufList[i]->pos  = 0;
    rxBufList[i]->next = 0;
  }
}

rtems_device_driver m860_console_read(rtems_device_major_number major,
                                      rtems_device_minor_number minor,
                                      void *arg)
{
  rtems_libio_rw_args_t *rw_args;
  char *buffer;
  int maximum;
  int count;
  Buf_t *tmp_buf;
  rtems_unsigned32 level;

  /*
   * Set up interrupts
   * FIXME: DANGER: WARNING:
   * CICR and SIMASK must be set in any module that uses
   *   the CPM. Currently those are console-generic.c and
   *   network.c. If the registers are not set the same
   *   in both places, strange things may happen.
   *   If they are only set in one place, then an application
   *   that used the other module won't work correctly.
   *   Put this comment in each module that sets these 2 registers
   */
  m860.cicr = 0x00e43e80;   /* SCaP=SCC1, SCbP=SCC2, SCcP=SCC3, 
			       SCdP=SCC4, IRL=1, HP=SCC1, IEN=1 */
  m860.simask |= M860_SIMASK_LVM1; 

  rw_args = (rtems_libio_rw_args_t *) arg;
  buffer = rw_args->buffer;
  maximum = rw_args->count;
  count = 0;

  while (count == 0) {
    if (rxBufList[minor]->len) {
      while ((count < maximum) && 
             (rxBufList[minor]->pos < rxBufList[minor]->len)) {
        buffer[count++] = rxBufList[minor]->buf[rxBufList[minor]->pos++];
      }
      _CPU_ISR_Disable(level);
      if (rxBufList[minor]->pos == rxBufList[minor]->len) {
        if (rxBufList[minor]->next) {
          tmp_buf=rxBufList[minor]->next;
          free ((void *) rxBufList[minor]->buf);
          free ((void *) rxBufList[minor]);
          rxBufList[minor]=tmp_buf;
        }
        else {
          free(rxBufList[minor]->buf);
          rxBufList[minor]->buf=0;
          rxBufList[minor]->len=0;
          rxBufList[minor]->pos=0;
        }
      }
      _CPU_ISR_Enable(level);
    }
    else
      if(rxBufList[minor]->next && !rxBufList[minor]->len) {
        tmp_buf = rxBufList[minor];
        rxBufList[minor] = rxBufList[minor]->next;
        free(tmp_buf);
      }
    /*      sleep(1);*/
  }
  rw_args->bytes_moved = count;
  return (count >= 0) ? RTEMS_SUCCESSFUL : RTEMS_UNSATISFIED;
}
  
rtems_device_driver m860_console_write(rtems_device_major_number major,
                                       rtems_device_minor_number minor,
                                       void *arg)
{
  int count;
  int maximum;
  rtems_libio_rw_args_t *rw_args;
  char *in_buffer;
  char *out_buffer;
  int n;

  /*
   * Set up interrupts
   * FIXME: DANGER: WARNING:
   * CICR and SIMASK must be set in any module that uses
   *   the CPM. Currently those are console-generic.c and
   *   network.c. If the registers are not set the same
   *   in both places, strange things may happen.
   *   If they are only set in one place, then an application
   *   that used the other module won't work correctly.
   *   Put this comment in each module that sets these 2 registers
   */
#if 0
    m860.cicr = 0x00e43e80;   /* SCaP=SCC1, SCbP=SCC2, SCcP=SCC3, 
			       SCdP=SCC4, IRL=1, HP=SCC1, IEN=1 */
    m860.simask |= M860_SIMASK_LVM1;
#endif

  rw_args = (rtems_libio_rw_args_t *) arg;

  in_buffer = rw_args->buffer;
  maximum = rw_args->count;

  out_buffer = malloc(maximum*2);  /* This is wasteful, but it won't */
                                   /*  be too small */

  if (!out_buffer) {
    rw_args->bytes_moved = 0;
    return RTEMS_NO_MEMORY;
  }
  n=0;
  for (count = 0; count < maximum; count++) {
    if ( in_buffer[ count ] == '\n') {
      out_buffer[count + n] = '\r';
      n++;
    }
    out_buffer[count + n] = in_buffer[count];
  }
  m860_buf_poll_write(minor, out_buffer, maximum+n);
  rw_args->bytes_moved = maximum;
  return RTEMS_SUCCESSFUL;
}


/*
 *  How to use the console. 
 *   In your BSP, have the following functions:
 *
 *  rtems_device_driver console_initialize(rtems_device_major_number  major,
 *                                         rtems_device_minor_number  minor,
 *                                         void *arg)
 *  rtems_device_driver console_open(rtems_device_major_number major,
 *                                   rtems_device_minor_number minor,
 *                                   void *arg)
 *  rtems_device_driver console_close(rtems_device_major_number major,
 *                                    rtems_device_minor_number minor,
 *                                    void *arg)
 *  rtems_device_driver console_read(rtems_device_major_number major,
 *                                   rtems_device_minor_number minor,
 *                                   void *arg)
 *  rtems_device_driver console_write(rtems_device_major_number major,
 *                                    rtems_device_minor_number minor,
 *                                    void *arg)
 *  rtems_device_driver console_control(rtems_device_major_number major,
 *                                      rtems_device_minor_number minor,
 *                                      void *arg)
 *
 */