This is the mail archive of the ecos-discuss@sourceware.cygnus.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Ethernet driver for MPC860


We've talked about this several times here, but unfortunately some
other work has distracted me and is likely to do so for a few more
weeks.

Here is the current state of our network.c, to be used with Tristan's
tcpip stack.

This file has been derived from RTEMS' network.c for PPC[1]. We will of
course appreciate bug fixes :)

Some things will have to be removed, for example calls to LCD display
(that were here for debugging), and some constants may need to be
renamed.

Good luck.

  Sam

Footnotes: 
[1]  And it inherits its license

-- 
Samuel Tardieu -- sam@inf.enst.fr

/***********************************************************************/
/*                   lan.c                                             */
/***********************************************************************/
#include <stdio.h>
#include <stdlib.h>

#include <cyg/hal/lcd.h>
#include <cyg/kernel/kapi.h>
#include <cyg/hal/cpm_init.h>
#include <cyg/hal/mpc860.h>
#include <cyg/hal/masks860.h>
#include <cyg/hal/cpm_init.h>
#include <cyg/hal/kprintf.h>

#include <rtems/rtems_bsdnet.h>

#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

static int scc1_attach (struct rtems_bsdnet_ifconfig *config);

static int scc_ioctl (struct ifnet *, int, caddr_t);

static char scc1_ip_address [30];
static char scc1_ip_netmask [30];
static char scc1_gateway [30];
static unsigned char scc1_hardware_address [6] = {
  0xaa, 0x00, 0x05, 0x00, 0xa0, 0xc7
};

#undef PPCSPIF_USE_BOOTP

#ifdef PPCSPIF_USE_BOOTP

static struct rtems_bsdnet_ifconfig scc1_ifconfig = {
  "scc1",
  scc1_attach,
  NULL,
  NULL,
  NULL,
  scc1_hardware_address,
  0,
  0,
  0,
  0,
  0,
  0,
  0
};

struct rtems_bsdnet_config rtems_bsdnet_config = {
  &scc1_ifconfig,
  rtems_bsdnet_do_bootp,
  5,
  0, /* mbuf_bytecount */
  0, /* mbuf_cluster_bytecount */
  NULL,
  NULL,
  NULL,
  NULL,
  {NULL, NULL, NULL}
};

#else

static struct rtems_bsdnet_ifconfig scc1_ifconfig = {
  "scc1",
  scc1_attach,
  NULL,
  scc1_ip_address,
  scc1_ip_netmask,
  scc1_hardware_address,
  0,
  0,
  0,
  0,
  0,
  0,
  0
};

struct rtems_bsdnet_config rtems_bsdnet_config = {
  &scc1_ifconfig,
  NULL,
  5,
  0, /* mbuf_bytecount */
  0, /* mbuf_cluster_bytecount */
  "ppcspif",
  "enst.fr",
  scc1_gateway,
  NULL,
  {"137.194.161.2", "137.194.160.3", "137.194.2.19"}
};

#endif

/*
 * Number of SCCs supported by this driver
 */
#define NSCCDRIVER	1

/*
 * Default number of buffer descriptors set aside for this driver.
 * The number of transmit buffer descriptors has to be quite large
 * since a single frame often uses four or more buffer descriptors.
 */
#define RX_BUF_COUNT     15
#define TX_BUF_COUNT     4
#define TX_BD_PER_BUF    4

/* Stack size for Tx and Rx threads */
#define RX_TX_STACK_SIZE 4096

/*
** Receive Buffer Descriptor Symbols
*/
#define EMPTY                (1 << 15)      /* Ethernet, UART */
#define WRAP                 (1 << 13)      /* Ethernet, UART */
#define LOG_EVENT            (1 << 12)      /* Ethernet, UART */
#define LAST_IN_FRAME        (1 << 11)      /* Ethernet */
#define FIRST_IN_FRAME       (1 << 10)      /* Ethernet */
#define MISS                 (1 << 8)       /* Ethernet */
#define LONG                 (1 << 5)       /* Ethernet */
#define NOT_ALIGNED          (1 << 4)       /* Ethernet */
#define SHORT                (1 << 3)       /* Ethernet */
#define CRC_ERROR            (1 << 2)       /* Ethernet */
#define OVERRUN              (1 << 1)       /* Ethernet */
#define COLLISION            (1 << 0)       /* Ethernet */

/*
** Transmit Buffer Descriptor Symbols
*/
#define FULL            (1 << 15)           /* Ethernet */
#define PAD             (1 << 14)           /* Ethernet */
#define LOG_EVENT       (1 << 12)           /* Ethernet, UART */
#define TX_CRC          (1 << 10)           /* Ethernet */
#define DEFER           (1 << 9)
#define HEARTBEAT       (1 << 8)            /* Ethernet */
#define LATE_COL        (1 << 7)            /* Ethernet */
#define AT_RETRY_LIMIT  (1 << 6)            /* Ethernet */
#define RETRY_COUNT(x)  (((x) & 0x3C) >> 2) /* Ethernet */
#define UNDERRUN        (1 << 1)            /* Ethernet */
#define CARRIER_LOST    (1 << 0)            /* Ethernet */

#define MOT                 (1 << 4)
#define DMA_SPACE           (1 << 3)

/*
 * Per-device data
 */
struct scc_softc {
  struct arpcom		arpcom;
  struct mbuf		**rxMbuf;
  struct mbuf		**txMbuf;
  int			acceptBroadcast;
  int			rxBdCount;
  int			txBdCount;
  int			txBdHead;
  int			txBdTail;
  int			txBdActiveCount;
  BD *rxBdBase;
  BD *txBdBase;
  cyg_flag_t            scc_Rx_flags;
  cyg_flag_t            scc_Tx_flags;
  cyg_handle_t          scc_intr_handle;
  cyg_interrupt         scc_intr;
  char                  scc_Rx_stack [RX_TX_STACK_SIZE];
  cyg_handle_t          scc_Rx_handle;
  cyg_thread            scc_Rx_thread;
  char                  scc_Tx_stack [RX_TX_STACK_SIZE];
  cyg_handle_t          scc_Tx_handle;
  cyg_thread            scc_Tx_thread;
  int                   init_done;

  /*
   * Statistics
   */
  unsigned long	rxInterrupts;
  unsigned long	rxNotFirst;
  unsigned long	rxNotLast;
  unsigned long	rxGiant;
  unsigned long	rxNonOctet;
  unsigned long	rxRunt;
  unsigned long	rxBadCRC;
  unsigned long	rxOverrun;
  unsigned long	rxCollision;
  
  unsigned long	txInterrupts;
  unsigned long	txDeferred;
  unsigned long	txHeartbeat;
  unsigned long	txLateCollision;
  unsigned long	txRetryLimit;
  unsigned long	txUnderrun;
  unsigned long	txLostCarrier;
  unsigned long	txRawWait;
};
static struct scc_softc scc_softc[NSCCDRIVER];

static cyg_uint32 scc_isr (cyg_vector_t vector, cyg_addrword_t data);
static void scc_dsr (cyg_vector_t vector,
		     cyg_ucount32 count,
		     cyg_addrword_t data);

static void scc_Rx_program (cyg_addrword_t);
static void scc_Tx_program (cyg_addrword_t);
static void scc_start (struct ifnet *);

#define RX_FLAG (1 << 0)
#define TX_FLAG (1 << 1)

#define RX_EVENTS ENET_SCCE_RXF
#define TX_EVENTS (ENET_SCCE_TXB | ENET_SCCE_TXE)

static cyg_uint32
scc_isr (cyg_vector_t vector, cyg_addrword_t data)
{
  /* Mask SCC1 interrupts to prevent cascading calls */
  cyg_interrupt_mask (CYGNUM_HAL_INTERRUPT_CPM_SCC1);

  /* Acknowledge the interrupt */
  cyg_interrupt_acknowledge (vector);

  /* DSR should be executed */
  return CYG_ISR_CALL_DSR;
}

static void
scc_dsr (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
  static int dsr_count = 0;
  struct scc_softc *sc = (struct scc_softc *) data;
  char buffer[30];
  char *dsr_type = "??";
  
  dsr_count ++;

  sprintf (buffer, "SCC DSR %d", dsr_count);
  lcd_clear ();
  lcd_puts (buffer);

  /* Dispatch to the right event */
  if ((mpc860.scc_regs[0].scc_sccm & RX_EVENTS) &&
      (mpc860.scc_regs[0].scc_scce & RX_EVENTS)) {
    mpc860.scc_regs[0].scc_scce = RX_EVENTS;
    mpc860.scc_regs[0].scc_sccm &= ~RX_EVENTS;
    dsr_type = "RX";
    sc->rxInterrupts++;
    cyg_flag_setbits (&sc->scc_Rx_flags, RX_FLAG);
  }

  if ((mpc860.scc_regs[0].scc_sccm & TX_EVENTS) &&
      (mpc860.scc_regs[0].scc_scce & TX_EVENTS)) {
    mpc860.scc_regs[0].scc_scce = TX_EVENTS;
    mpc860.scc_regs[0].scc_sccm &= ~TX_EVENTS;
    dsr_type = "TX";
    sc->txInterrupts++;
    cyg_flag_setbits (&sc->scc_Tx_flags, TX_FLAG);
  }

  sprintf (buffer, "SCC %s DSR %d done", dsr_type, dsr_count);
  lcd_clear ();
  lcd_puts (buffer);

  /* Unmask SCC1 interrupts */
  cyg_interrupt_unmask (CYGNUM_HAL_INTERRUPT_CPM_SCC1);
}

static void
scc_init (void *arg)
{
  struct scc_softc *sc = arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  int i;

  /* Steps of section 28.22 */

  if (!sc->init_done) {

    /* Step 1 */
    mpc860.pio_papar |= PAPAR_DD14 | PAPAR_DD15;
    mpc860.pio_padir &= ~(PADIR_DR14 | PADIR_DR15);
    mpc860.pio_paodr &= ~PAODR_OD14;

    /* Step 2 */
    mpc860.pio_pcpar &= ~(PCPAR_DD10 | PCPAR_DD11);
    mpc860.pio_pcdir &= ~(PCDIR_DR10 | PCDIR_DR11);
    mpc860.pio_pcso |= PCSO_CD1 | PCSO_CTS1;

    /* Step 3 */
    /* Nothing to do */

    /* Step 4 */
    mpc860.pio_papar |= PAPAR_DD6 | PAPAR_DD7;
    mpc860.pio_padir &= ~(PADIR_DR6 | PADIR_DR7);

    /* Step 5 */
    mpc860.si_sicr = (mpc860.si_sicr & 0xffffff00) | 0x25;

    /* Step 6 */
    /* Interrupt is connected in cpm_init.c */
    mpc860.si_sicr &= ~SICR_SC1;

    /* Step 7 */
    mpc860.dma_sdcr = 0x0001;

    /* Step 8 */
    sc->rxBdBase = (BD *)&mpc860.qcp_or_ud.ud;
    sc->txBdBase = sc->rxBdBase + RX_BUF_COUNT; /* Pointer arithmetic works! */
  
    mpc860.PRAM[PAGE1].enet_scc.rbase =
      (char *)sc->rxBdBase - (char *)&mpc860;
    mpc860.PRAM[PAGE1].enet_scc.tbase =
      (char *)sc->txBdBase - (char *)&mpc860;

    /* Step 9 */
    mpc860.cp_cr = CPCR_INIT_TX_RX_PARAMS | CPCR_SCC1_CH | CPCR_FLG;
    while (mpc860.cp_cr & CPCR_FLG);

    /* Step 10 */
    mpc860.PRAM[PAGE1].enet_scc.rfcr = 0x10;
    mpc860.PRAM[PAGE1].enet_scc.tfcr = 0x10;
  
    /* Step 11 */
    mpc860.PRAM[PAGE1].enet_scc.mrblr = 1520;

    /* Step 12 */
    mpc860.PRAM[PAGE1].enet_scc.c_pres = 0xFFFFFFFF;

    /* Step 13 */
    mpc860.PRAM[PAGE1].enet_scc.c_mask = 0xDEBB20E3;

    /* Step 14 */
    mpc860.PRAM[PAGE1].enet_scc.crcec   = 0;
    mpc860.PRAM[PAGE1].enet_scc.alec = 0;
    mpc860.PRAM[PAGE1].enet_scc.disfc = 0;

    /* Step 15 */
    mpc860.PRAM[PAGE1].enet_scc.pads = 0x8888;

    /* Step 16 */
    mpc860.PRAM[PAGE1].enet_scc.ret_lim = 15;

    /* Step 17 */
    mpc860.PRAM[PAGE1].enet_scc.mflr = 1518;

    /* Step 18 */
    mpc860.PRAM[PAGE1].enet_scc.minflr = 64;

    /* Step 19 */
    mpc860.PRAM[PAGE1].enet_scc.maxd1 = 1520;
    mpc860.PRAM[PAGE1].enet_scc.maxd2 = 1520;

    /* Step 20 */
    mpc860.PRAM[PAGE1].enet_scc.gaddr1 = 0;
    mpc860.PRAM[PAGE1].enet_scc.gaddr2 = 0;
    mpc860.PRAM[PAGE1].enet_scc.gaddr3 = 0;
    mpc860.PRAM[PAGE1].enet_scc.gaddr4 = 0;

    /* Step 21 */
    mpc860.PRAM[PAGE1].enet_scc.paddr_h =
      (sc->arpcom.ac_enaddr[5] << 8) | sc->arpcom.ac_enaddr[4];
    mpc860.PRAM[PAGE1].enet_scc.paddr_m =
      (sc->arpcom.ac_enaddr[3] << 8) | sc->arpcom.ac_enaddr[2];
    mpc860.PRAM[PAGE1].enet_scc.paddr_l =
      (sc->arpcom.ac_enaddr[1] << 8) | sc->arpcom.ac_enaddr[0];
  
    /* Step 22 */
    mpc860.PRAM[PAGE1].enet_scc.p_per   = 0;

    /* Step 23 */
    mpc860.PRAM[PAGE1].enet_scc.iaddr1  = 0;
    mpc860.PRAM[PAGE1].enet_scc.iaddr2  = 0;
    mpc860.PRAM[PAGE1].enet_scc.iaddr3  = 0;
    mpc860.PRAM[PAGE1].enet_scc.iaddr4  = 0;

    /* Step 24 */
    mpc860.PRAM[PAGE1].enet_scc.taddr_h = 0;
    mpc860.PRAM[PAGE1].enet_scc.taddr_m = 0;
    mpc860.PRAM[PAGE1].enet_scc.taddr_l = 0;

    /* Step 25 */
    /* The whole initialization will take place in the Rx thread */
    for (i = 0; i < sc->rxBdCount; i++)
      sc->rxBdBase[i].bd_cstatus = 0;

    /* Step 26 */
    for (i = 0; i < sc->txBdCount; i++)
      sc->txBdBase[i].bd_cstatus = 0;

    /* Step 27 */
    mpc860.scc_regs[0].scc_scce = 0xFFFF;
    
    /* Step 28 */
    /* Interrupts for TXE, RXF and TXB are not enabled here and will be
       enabled in specific threads */
    mpc860.scc_regs[0].scc_sccm = 0;
    
    /* Step 29 */

    /* Create flags */
    cyg_flag_init (&sc->scc_Rx_flags);
    cyg_flag_init (&sc->scc_Tx_flags);
    /* Setup and enable SCC1 interrupts */
    cpm_init ();
    cyg_interrupt_create (CYGNUM_HAL_INTERRUPT_CPM_SCC1,
			  1,
			  (cyg_addrword_t) sc,
			  scc_isr,
			  scc_dsr,
			  &sc->scc_intr_handle,
			  &sc->scc_intr);
    cyg_interrupt_attach (sc->scc_intr_handle);
    cyg_interrupt_unmask (CYGNUM_HAL_INTERRUPT_CPM_SCC1);

    /* Step 30 */
    mpc860.scc_regs[0].scc_gsmr_h = 0;

    /* Step 31 */

    mpc860.scc_regs[0].scc_gsmr_l = 0x1088000C;

    /* Step 32 */
    mpc860.scc_regs[0].scc_dsr = 0xD555;

    /* Step 33 */
    mpc860.scc_regs[0].scc_psmr = 0x080A;

    /* Step 34 */
    mpc860.pip_pbpar |= PBPAR_DD19;
    mpc860.pip_pbdir |= PBDIR_DR19;
    mpc860.pip_pbodr &= ~PBODR_OD19;

    /* Step 35 */
    /* Done in individual threads */

    /* Interface is now running */
    ifp->if_flags |= IFF_RUNNING;
  }
  
  /*---------------------------------------------------------------------*/
  /* Initialize statistics                                               */
  /*---------------------------------------------------------------------*/

  sc->rxInterrupts = 0;
  sc->rxNotFirst = 0;
  sc->rxNotLast = 0;
  sc->rxGiant = 0;
  sc->rxNonOctet = 0;
  sc->rxRunt = 0;
  sc->rxBadCRC = 0;
  sc->rxOverrun = 0;
  sc->rxCollision = 0;
  
  sc->txInterrupts = 0;
  sc->txDeferred = 0;
  sc->txHeartbeat = 0;
  sc->txLateCollision = 0;
  sc->txRetryLimit = 0;
  sc->txUnderrun = 0;
  sc->txLostCarrier = 0;
  sc->txRawWait = 0;

  if (!sc->init_done) {
    /* Create Tx and Rx threads associated to this device */
    cyg_thread_create (5,
		       scc_Tx_program,
		       (cyg_addrword_t) sc,
		       "Tx",
		       &sc->scc_Tx_stack,
		       RX_TX_STACK_SIZE,
		       &sc->scc_Tx_handle,
		       &sc->scc_Tx_thread);
    cyg_thread_create (5,
		       scc_Rx_program,
		       (cyg_addrword_t) sc,
		       "Rx",
		       &sc->scc_Rx_stack,
		       RX_TX_STACK_SIZE,
		       &sc->scc_Rx_handle,
		       &sc->scc_Rx_thread);
    cyg_thread_resume (sc->scc_Tx_handle);
    cyg_thread_resume (sc->scc_Rx_handle);
  }

  sc->init_done = 1;
}  /* End scc_init */

/*
 * Attach an SCC driver to the system
 */
int
scc1_attach (struct rtems_bsdnet_ifconfig *config)
{
  struct scc_softc *sc;
  struct ifnet *ifp;
  int mtu;
  int i;

  /*
   * Find a free driver
   */
  for (i = 0 ; i < NSCCDRIVER ; i++) {
    sc = &scc_softc[i];
    ifp = &sc->arpcom.ac_if;
    if (ifp->if_softc == NULL)
      break;
  }
  if (i >= NSCCDRIVER) {
    return 0;
  }
  
  /*
   * Process options
   */
  if (config->hardware_address) {
    memcpy (sc->arpcom.ac_enaddr,
	    config->hardware_address,
	    ETHER_ADDR_LEN);
  }
  else {
#if 0   /* TO BE DONE LATER */
    /*
     * The first 4 bytes of the bootstrap prom
     * contain the value loaded into the stack
     * pointer as part of the CPU32's hardware
     * reset exception handler.  The following
     * 4 bytes contain the value loaded into the
     * program counter.  The boards' Ethernet
     * address is stored in the six bytes
     * immediately preceding this initial
     * program counter value.
     *
     * See start360/start360.s.
     */
    extern void *_RomBase;	/* From linkcmds */
    const unsigned long *ExceptionVectors;
    const unsigned char *entryPoint;
    
    /*
     * Sanity check -- assume entry point must be
     * within 1 MByte of beginning of boot ROM.
     */
    ExceptionVectors = (const unsigned long *)&_RomBase;
    entryPoint = (const unsigned char *)ExceptionVectors[1];
    if (((unsigned long)entryPoint - (unsigned long)ExceptionVectors)
	>= (1 * 1024 * 1024)) {
      kprintf ("Warning -- Ethernet address can not be found in bootstrap PROM.\n");
      sc->arpcom.ac_enaddr[0] = 0x08;
      sc->arpcom.ac_enaddr[1] = 0xF3;
      sc->arpcom.ac_enaddr[2] = 0x3E;
      sc->arpcom.ac_enaddr[3] = 0xC2;
      sc->arpcom.ac_enaddr[4] = 0x7E;
      sc->arpcom.ac_enaddr[5] = 0x38;
    }
    else {
      memcpy (sc->arpcom.ac_enaddr, entryPoint - ETHER_ADDR_LEN, ETHER_ADDR_LEN);
    }
#endif
  }
  if (config->mtu)
    mtu = config->mtu;
  else
    mtu = ETHERMTU;
  if (config->rbuf_count)
    sc->rxBdCount = config->rbuf_count;
  else
    sc->rxBdCount = RX_BUF_COUNT;
  if (config->xbuf_count)
    sc->txBdCount = config->xbuf_count;
  else
    sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
  sc->acceptBroadcast = !config->ignore_broadcast;

  sc->txBdHead = sc->txBdTail = 0;
  sc->txBdActiveCount = 0;
  
  sc->rxMbuf = rtems_bsdnet_malloc
    (sc->rxBdCount * sizeof *sc->rxMbuf, M_MBUF, M_NOWAIT);
  sc->txMbuf = rtems_bsdnet_malloc
    (sc->txBdCount * sizeof *sc->txMbuf, M_MBUF, M_NOWAIT);
  
  /*
   * Set up network interface values
   */
  ifp->if_softc = sc;
  ifp->if_unit = i + 1;
  ifp->if_name = "scc";
  ifp->if_mtu = mtu;
  ifp->if_init = scc_init;
  ifp->if_ioctl = scc_ioctl;
  ifp->if_start = scc_start;
  ifp->if_output = ether_output;
  ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
  if (ifp->if_snd.ifq_maxlen == 0)
    ifp->if_snd.ifq_maxlen = ifqmaxlen;
  
  /* scc_init (sc); */

  /*
   * Attach the interface
   */
  if_attach (ifp);
  ether_ifattach (ifp);

  return 1;
};

/*
 * SCC reader task
 */
static void
scc_Rx_program (cyg_addrword_t arg)
{
  struct scc_softc *sc = (struct scc_softc *)arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  struct mbuf *m;
  cyg_uint16 status;
  BD *rxBd;
  int rxBdIndex;

  rtems_bsdnet_semaphore_obtain ();

  /*
   * Allocate space for incoming packets and start reception
   */
  for (rxBdIndex = 0 ; ;) {
    rxBd = &sc->rxBdBase[rxBdIndex];
    MGETHDR (m, M_WAIT, MT_DATA);
    MCLGET (m, M_WAIT);
    m->m_pkthdr.rcvif = ifp;
    sc->rxMbuf[rxBdIndex] = m;
    rxBd->bd_addr = mtod (m, void *);
    rxBd->bd_cstatus = EMPTY | LOG_EVENT;
    if (++rxBdIndex == sc->rxBdCount) {
      rxBd->bd_cstatus |= WRAP;
      break;
    }
  }

  /*
   * Enable reception
   */

  mpc860.scc_regs[0].scc_gsmr_l |= GSMR_L1_ENR;

  /*
   * Input packet handling loop
   */
  rxBdIndex = 0;
  for (rxBdIndex = 0;; rxBdIndex = (rxBdIndex + 1) % sc->rxBdCount) {
    rxBd = &sc->rxBdBase[rxBdIndex];

    /*
     * Wait for packet if there's not one ready
     */
    if ((status = rxBd->bd_cstatus) & EMPTY) {
      /*
       * Clear old events
       */
      mpc860.scc_regs[0].scc_scce = ENET_SCCE_RXF;

      /*
       * Wait for packet
       * Note that the buffer descriptor is checked
       * *before* the event wait -- this catches the
       * possibility that a packet arrived between the
       * `if' above, and the clearing of the event register.
       */
      while ((status = rxBd->bd_cstatus) & EMPTY) {

	/*
	 * Unmask RXF (Full frame received) event
	 */

	mpc860.scc_regs[0].scc_sccm |= ENET_SCCM_RXF;

	rtems_bsdnet_semaphore_release ();
	cyg_flag_wait (&sc->scc_Rx_flags, RX_FLAG, CYG_FLAG_WAITMODE_CLR);
	rtems_bsdnet_semaphore_obtain ();
      }
    }

    /*
     * Check that packet is valid
     */
    if ((status & (LAST_IN_FRAME |
		   FIRST_IN_FRAME |
		   LONG |
		   NOT_ALIGNED |
		   SHORT |
		   CRC_ERROR |
		   OVERRUN |
		   COLLISION)) ==
	(LAST_IN_FRAME |
	 FIRST_IN_FRAME)) {
      /*
       * Pass the packet up the chain.
       * FIXME: Packet filtering hook could be done here.
       */
      struct ether_header *eh;

      m = sc->rxMbuf[rxBdIndex];
      m->m_len = m->m_pkthdr.len = rxBd->bd_length -
	sizeof(cyg_uint32) -
	sizeof(struct ether_header);
      eh = mtod (m, struct ether_header *);
      m->m_data += sizeof(struct ether_header);
      ether_input (ifp, eh, m);

      /*
       * Allocate a new mbuf
       */
      MGETHDR (m, M_WAIT, MT_DATA);
      MCLGET (m, M_WAIT);
      m->m_pkthdr.rcvif = ifp;
      sc->rxMbuf[rxBdIndex] = m;
      rxBd->bd_addr = mtod (m, void *);
    }
    else {
      /*
       * Something went wrong with the reception
       */
      if (!(status & LAST_IN_FRAME))
	sc->rxNotLast++;
      if (!(status & FIRST_IN_FRAME))
	sc->rxNotFirst++;
      if (status & LONG) {
	sc->rxGiant++;
      }
      if (status & NOT_ALIGNED)
	sc->rxNonOctet++;
      if (status & SHORT)
	sc->rxRunt++;
      if (status & CRC_ERROR)
	sc->rxBadCRC++;
      if (status & OVERRUN)
	sc->rxOverrun++;
      if (status & COLLISION)
	sc->rxCollision++;
    }

    /*
     * Reenable the buffer descriptor
     */
    rxBd->bd_cstatus = (status & (WRAP | LOG_EVENT)) | EMPTY;
  }
}

/*
 * Soak up buffer descriptors that have been sent
 * Note that a buffer descriptor can't be retired as soon as it becomes
 * ready.  The MC68360 Errata (May 96) says that, "If an Ethernet frame is
 *  made up of multiple buffers, the user should not reuse the first buffer
 * descriptor until the last buffer descriptor of the frame has had its
 * ready bit cleared by the CPM".
 */
static void
retire_tx_bd (struct scc_softc *sc)
{
  cyg_uint16 status;
  int i;
  int nRetired;
  struct mbuf *m, *n;

  i = sc->txBdTail;
  nRetired = 0;
  while ((sc->txBdActiveCount != 0)
	 &&  (((status = (sc->txBdBase + i)->bd_cstatus) & FULL) == 0)) {
    /*
     * See if anything went wrong
     */
    if (status & (DEFER |
		  HEARTBEAT |
		  LATE_COL |
		  AT_RETRY_LIMIT |
		  UNDERRUN |
		  CARRIER_LOST)) {
      /*
       * Check for errors which stop the transmitter.
       */
      if (status & (LATE_COL |
		    AT_RETRY_LIMIT |
		    UNDERRUN)) {
	if (status & LATE_COL)
	  scc_softc[0].txLateCollision++;
	if (status & AT_RETRY_LIMIT)
	  scc_softc[0].txRetryLimit++;
	if (status & UNDERRUN)
	  scc_softc[0].txUnderrun++;

	/*
	 * Restart the transmitter
	 */
	mpc860.cp_cr = CPCR_RESTART_TX | CPCR_SCC1_CH | CPCR_FLG;
	while (mpc860.cp_cr & CPCR_FLG);
      }
      if (status & DEFER)
	scc_softc[0].txDeferred++;
      if (status & HEARTBEAT)
	scc_softc[0].txHeartbeat++;
      if (status & CARRIER_LOST)
	scc_softc[0].txLostCarrier++;
    }
    nRetired++;
    if (status & LAST_IN_FRAME) {
      /*
       * A full frame has been transmitted.
       * Free all the associated buffer descriptors.
       */
      sc->txBdActiveCount -= nRetired;
      while (nRetired) {
	nRetired--;
	m = sc->txMbuf[sc->txBdTail];
	MFREE (m, n);
	if (++sc->txBdTail == sc->txBdCount)
	  sc->txBdTail = 0;
      }
    }
    if (++i == sc->txBdCount)
      i = 0;
  }
}

static void
sendpacket (struct ifnet *ifp, struct mbuf *m)
{
  struct scc_softc *sc = ifp->if_softc;
  volatile BD *firstTxBd, *txBd;
  struct mbuf *l = NULL;
  cyg_uint16 status;
  int nAdded;

  /*
   * Free up buffer descriptors
   */
  retire_tx_bd (sc);

  /*
   * Set up the transmit buffer descriptors.
   * No need to pad out short packets since the
   * hardware takes care of that automatically.
   * No need to copy the packet to a contiguous buffer
   * since the hardware is capable of scatter/gather DMA.
   */
  nAdded = 0;
  txBd = firstTxBd = sc->txBdBase + sc->txBdHead;
  for (;;) {
    /*
     * Wait for buffer descriptor to become available.
     */
    if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) {
      /*
       * Clear old events
       */
      mpc860.scc_regs[0].scc_scce = ENET_SCCE_TXB | ENET_SCCE_TXE;

      /*
       * Wait for buffer descriptor to become available.
       * Note that the buffer descriptors are checked
       * *before* * entering the wait loop -- this catches
       * the possibility that a buffer descriptor became
       * available between the `if' above, and the clearing
       * of the event register.
       * This is to catch the case where the transmitter
       * stops in the middle of a frame -- and only the
       * last buffer descriptor in a frame can generate
       * an interrupt.
       */
      retire_tx_bd (sc);
      while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) {
	/*
	 * Unmask TXB (buffer transmitted) and
	 * TXE (transmitter error) events.
	 */

	mpc860.scc_regs[0].scc_sccm |= ENET_SCCM_TXB | ENET_SCCM_TXE;
	rtems_bsdnet_semaphore_release ();
	cyg_flag_wait (&sc->scc_Tx_flags, TX_FLAG, CYG_FLAG_WAITMODE_CLR);
	rtems_bsdnet_semaphore_obtain ();
	retire_tx_bd (sc);
      }
    }

    /*
     * Don't set the READY flag till the
     * whole packet has been readied.
     */
    status = nAdded ? FULL : 0;

    /*
     *  FIXME: Why not deal with empty mbufs at at higher level?
     * The IP fragmentation routine in ip_output
     * can produce packet fragments with zero length.
     * I think that ip_output should be changed to get
     * rid of these zero-length mbufs, but for now,
     * I'll deal with them here.
     */
    if (m->m_len) {
      /*
       * Fill in the buffer descriptor
       */
      txBd->bd_addr = mtod (m, void *);
      txBd->bd_length = m->m_len;
      sc->txMbuf[sc->txBdHead] = m;
      nAdded++;
      if (++sc->txBdHead == sc->txBdCount) {
	status |= WRAP;
	sc->txBdHead = 0;
      }
      l = m;
      m = m->m_next;
    }
    else {
      /*
       * Just toss empty mbufs
       */
      struct mbuf *n;
      MFREE (m, n);
      m = n;
      if (l != NULL)
	l->m_next = m;
    }

    /*
     * Set the transmit buffer status.
     * Break out of the loop if this mbuf is the last in the frame.
     */
    if (m == NULL) {
      if (nAdded) {
	status |= PAD | LAST_IN_FRAME | TX_CRC | LOG_EVENT;
	txBd->bd_cstatus = status;
	firstTxBd->bd_cstatus |= FULL;
	sc->txBdActiveCount += nAdded;
      }
      break;
    }
    txBd->bd_cstatus = status;
    txBd = sc->txBdBase + sc->txBdHead;
  }
}

static void
scc_Tx_program (cyg_addrword_t arg)
{
  struct scc_softc *sc = (struct scc_softc *)arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  struct mbuf *m;

  rtems_bsdnet_semaphore_obtain ();

  /*
   * Enable transmission
   */

  mpc860.scc_regs[0].scc_gsmr_l |= GSMR_L1_ENT;

  for (;;) {
    /*
     * Wait for packet
     */
    rtems_bsdnet_semaphore_release ();
    cyg_flag_wait (&sc->scc_Tx_flags, TX_FLAG, CYG_FLAG_WAITMODE_CLR);
    rtems_bsdnet_semaphore_obtain ();

    /*
     * Send packets till queue is empty
     */
    for (;;) {
      /*
       * Get the next mbuf chain to transmit.
       */
      IF_DEQUEUE(&ifp->if_snd, m);
      if (!m)
	break;
      sendpacket (ifp, m);
    }
    ifp->if_flags &= ~IFF_OACTIVE;
  }
}

/*
 * Send packet (caller provides header).
 */
static void
scc_start (struct ifnet *ifp)
{
  struct scc_softc *sc = ifp->if_softc;

  ifp->if_flags |= IFF_OACTIVE;
  cyg_flag_setbits (&sc->scc_Tx_flags, TX_FLAG);
}

/*
 * Stop the device
 */
static void
scc_stop (struct scc_softc *sc)
{
  struct ifnet *ifp = &sc->arpcom.ac_if;
  
  ifp->if_flags &= ~IFF_RUNNING;

  /*
   * Shut down receiver and transmitter
   */
  mpc860.scc_regs[0].scc_gsmr_l &= ~(GSMR_L1_ENR | GSMR_L1_ENT);

  /*
   * Destroy variables if needed
   */
  if (sc->init_done) {
    cyg_interrupt_delete (sc->scc_intr_handle);
    cyg_thread_kill (sc->scc_Rx_handle);
    cyg_thread_kill (sc->scc_Tx_handle);
    cyg_flag_destroy (&sc->scc_Tx_flags);
    cyg_flag_destroy (&sc->scc_Rx_flags);
  }

  sc->init_done = 0;
}


/*
 * Show interface statistics
 */
static void
scc_stats (struct scc_softc *sc)
{
  kprintf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
  kprintf ("       Not First:%-8lu", sc->rxNotFirst);
  kprintf ("        Not Last:%-8lu\n", sc->rxNotLast);
  kprintf ("              Giant:%-8lu", sc->rxGiant);
  kprintf ("            Runt:%-8lu", sc->rxRunt);
  kprintf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
  kprintf ("            Bad CRC:%-8lu", sc->rxBadCRC);
  kprintf ("         Overrun:%-8lu", sc->rxOverrun);
  kprintf ("       Collision:%-8lu\n", sc->rxCollision);
  kprintf ("          Discarded:%-8lu\n",
	  (unsigned long)mpc860.PRAM[PAGE1].enet_scc.disfc);

  kprintf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
  kprintf ("        Deferred:%-8lu", sc->txDeferred);
  kprintf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
  kprintf ("         No Carrier:%-8lu", sc->txLostCarrier);
  kprintf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
  kprintf ("  Late Collision:%-8lu\n", sc->txLateCollision);
  kprintf ("           Underrun:%-8lu", sc->txUnderrun);
  kprintf (" Raw output wait:%-8lu\n", sc->txRawWait);
}

/*
 * Driver ioctl handler
 */
static int
scc_ioctl (struct ifnet *ifp, int command, caddr_t data)
{
  struct scc_softc *sc = ifp->if_softc;
  int error = 0;

  switch (command) {
  case SIOCGIFADDR:
  case SIOCSIFADDR:
    ether_ioctl (ifp, command, data);
    break;

  case SIOCSIFFLAGS:
    switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
    case IFF_RUNNING:
      scc_stop (sc);
      break;

    case IFF_UP:
      scc_init (sc);
      break;

    case IFF_UP | IFF_RUNNING:
      scc_stop (sc);
      scc_init (sc);
      break;

    default:
      break;
    }
    break;

  case SIO_RTEMS_SHOW_STATS:
    scc_stats (sc);
    break;
		
    /*
     * FIXME: All sorts of multicast commands need to be added here!
     */
  default:
    error = EINVAL;
    break;
  }
  return error;
}

void
cyg_libnetworking_init (char *ip_address,
			char *ip_netmask,
			char *gateway,
			unsigned char *hwaddress)
{
  strcpy (scc1_ifconfig.ip_address,
	  ip_address ? ip_address : "137.194.160.199");
  strcpy (scc1_ifconfig.ip_netmask,
	  ip_netmask ? ip_netmask : "255.255.254.0");
  strcpy (rtems_bsdnet_config.gateway,
	  gateway ? gateway : "137.194.160.121");
  if (hwaddress)
    memcpy (&scc1_ifconfig.hardware_address, hwaddress, 6);
  rtems_bsdnet_initialize_network ();
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]