OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [or1ksim/] [peripheral/] [eth.c] - Diff between revs 538 and 566

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 538 Rev 566
/* ethernet.c -- Simulation of Ethernet MAC
/* ethernet.c -- Simulation of Ethernet MAC
 
 
   Copyright (C) 2001 by Erez Volk, erez@opencores.org
   Copyright (C) 2001 by Erez Volk, erez@opencores.org
                         Ivan Guzvinec, ivang@opencores.org
                         Ivan Guzvinec, ivang@opencores.org
   Copyright (C) 2008, 2001 Embecosm Limited
   Copyright (C) 2008, 2001 Embecosm Limited
   Copyright (C) 2010 ORSoC
   Copyright (C) 2010 ORSoC
 
 
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
   Contributor Julius Baxter <julius@orsoc.se>
   Contributor Julius Baxter <julius@orsoc.se>
 
 
   This file is part of Or1ksim, the OpenRISC 1000 Architectural Simulator.
   This file is part of Or1ksim, the OpenRISC 1000 Architectural Simulator.
 
 
   This program is free software; you can redistribute it and/or modify it
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the Free
   under the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3 of the License, or (at your option)
   Software Foundation; either version 3 of the License, or (at your option)
   any later version.
   any later version.
 
 
   This program is distributed in the hope that it will be useful, but WITHOUT
   This program is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   more details.
   more details.
 
 
   You should have received a copy of the GNU General Public License along
   You should have received a copy of the GNU General Public License along
   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
/* This program is commented throughout in a fashion suitable for processing
/* This program is commented throughout in a fashion suitable for processing
   with Doxygen. */
   with Doxygen. */
 
 
 
 
/* Autoconf and/or portability configuration */
/* Autoconf and/or portability configuration */
#include "config.h"
#include "config.h"
#include "port.h"
#include "port.h"
 
 
/* System includes */
/* System includes */
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
 
 
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#include <fcntl.h>
#include <fcntl.h>
 
 
#include <sys/poll.h>
#include <sys/poll.h>
#include <unistd.h>
#include <unistd.h>
#include <errno.h>
#include <errno.h>
 
 
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
#include <linux/if.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_tun.h>
#endif
#endif
 
 
/* Package includes */
/* Package includes */
#include "arch.h"
#include "arch.h"
#include "config.h"
#include "config.h"
#include "abstract.h"
#include "abstract.h"
#include "eth.h"
#include "eth.h"
#include "dma.h"
#include "dma.h"
#include "sim-config.h"
#include "sim-config.h"
#include "fields.h"
#include "fields.h"
#include "crc32.h"
#include "crc32.h"
#include "vapi.h"
#include "vapi.h"
#include "pic.h"
#include "pic.h"
#include "sched.h"
#include "sched.h"
#include "toplevel-support.h"
#include "toplevel-support.h"
#include "sim-cmd.h"
#include "sim-cmd.h"
 
 
 
 
/* Control debug messages */
/* Control debug messages */
#define ETH_DEBUG 0
#define ETH_DEBUG 0
#ifndef ETH_DEBUG
#ifndef ETH_DEBUG
# define ETH_DEBUG  1
# define ETH_DEBUG  1
#endif
#endif
 
 
/*! Period (clock cycles) for rescheduling Rx and Tx controllers.
/*! Period (clock cycles) for rescheduling Rx and Tx controllers.
    Experimenting with FTP on one machine suggests that a value of 500-2000 is
    Experimenting with FTP on one machine suggests that a value of 500-2000 is
    optimal. It's a trade off between prompt response and extra computational
    optimal. It's a trade off between prompt response and extra computational
    load for Or1ksim. */
    load for Or1ksim. */
#define  RTX_RESCHED_PERIOD  2000
#define  RTX_RESCHED_PERIOD  2000
 
 
/*! MAC address that is always accepted. */
/*! MAC address that is always accepted. */
static const unsigned char mac_broadcast[ETHER_ADDR_LEN] =
static const unsigned char mac_broadcast[ETHER_ADDR_LEN] =
  { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Structure describing the Ethernet device                                   */
/*!Structure describing the Ethernet device                                   */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
struct eth_device
struct eth_device
{
{
  /* Basic stuff about the device */
  /* Basic stuff about the device */
  int                enabled;           /* Is peripheral enabled */
  int                enabled;           /* Is peripheral enabled */
  oraddr_t           baseaddr;          /* Base address in memory */
  oraddr_t           baseaddr;          /* Base address in memory */
  unsigned long int  base_vapi_id;      /* Start of VAPI ID block */
  unsigned long int  base_vapi_id;      /* Start of VAPI ID block */
 
 
  /* DMA controller this MAC is connected to, and associated channels */
  /* DMA controller this MAC is connected to, and associated channels */
  unsigned dma;
  unsigned dma;
  unsigned tx_channel;
  unsigned tx_channel;
  unsigned rx_channel;
  unsigned rx_channel;
 
 
  /* Details of the hardware */
  /* Details of the hardware */
  unsigned char      mac_address[ETHER_ADDR_LEN];  /* Ext HW address */
  unsigned char      mac_address[ETHER_ADDR_LEN];  /* Ext HW address */
  unsigned long int  phy_addr;          /* Int HW address */
  unsigned long int  phy_addr;          /* Int HW address */
  unsigned long int  mac_int;           /* interrupt line number */
  unsigned long int  mac_int;           /* interrupt line number */
  int                dummy_crc;         /* Flag indicating if we add CRC */
  int                dummy_crc;         /* Flag indicating if we add CRC */
  int                int_line_stat;     /* interrupt line status */
  int                int_line_stat;     /* interrupt line status */
 
 
  /* External interface deatils */
  /* External interface deatils */
  int rtx_type;                         /* Type of external i/f: FILE or TAP */
  int rtx_type;                         /* Type of external i/f: FILE or TAP */
 
 
  /* RX and TX file names and handles for FILE type connection. */
  /* RX and TX file names and handles for FILE type connection. */
  char  *rxfile;                        /* Rx filename */
  char  *rxfile;                        /* Rx filename */
  char  *txfile;                        /* Tx filename */
  char  *txfile;                        /* Tx filename */
  int    txfd;                          /* Rx file handle */
  int    txfd;                          /* Rx file handle */
  int    rxfd;                          /* Tx file handle */
  int    rxfd;                          /* Tx file handle */
 
 
  /* Info for TAP type connections */
  /* Info for TAP type connections */
  char *tap_dev;                        /* The TAP device */
  char *tap_dev;                        /* The TAP device */
  int   rtx_fd;                         /* TAP device handle */
  int   rtx_fd;                         /* TAP device handle */
 
 
  /* Indices into the buffer descriptors. */
  /* Indices into the buffer descriptors. */
  unsigned long int  tx_bd_index;
  unsigned long int  tx_bd_index;
  unsigned long int  rx_bd_index;
  unsigned long int  rx_bd_index;
 
 
  /* Visible registers */
  /* Visible registers */
  struct
  struct
  {
  {
    unsigned long int  moder;
    unsigned long int  moder;
    unsigned long int  int_source;
    unsigned long int  int_source;
    unsigned long int  int_mask;
    unsigned long int  int_mask;
    unsigned long int  ipgt;
    unsigned long int  ipgt;
    unsigned long int  ipgr1;
    unsigned long int  ipgr1;
    unsigned long int  ipgr2;
    unsigned long int  ipgr2;
    unsigned long int  packetlen;
    unsigned long int  packetlen;
    unsigned long int  collconf;
    unsigned long int  collconf;
    unsigned long int  tx_bd_num;
    unsigned long int  tx_bd_num;
    unsigned long int  controlmoder;
    unsigned long int  controlmoder;
    unsigned long int  miimoder;
    unsigned long int  miimoder;
    unsigned long int  miicommand;
    unsigned long int  miicommand;
    unsigned long int  miiaddress;
    unsigned long int  miiaddress;
    unsigned long int  miitx_data;
    unsigned long int  miitx_data;
    unsigned long int  miirx_data;
    unsigned long int  miirx_data;
    unsigned long int  miistatus;
    unsigned long int  miistatus;
    unsigned long int  hash0;
    unsigned long int  hash0;
    unsigned long int  hash1;
    unsigned long int  hash1;
 
 
    /* Buffer descriptors */
    /* Buffer descriptors */
    unsigned long int  bd_ram[ETH_BD_SPACE / 4];
    unsigned long int  bd_ram[ETH_BD_SPACE / 4];
  } regs;
  } regs;
};
};
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Write an Ethernet packet to a FILE interface.
/*!Write an Ethernet packet to a FILE interface.
 
 
   This writes a single Ethernet packet to a FILE interface. The format is to
   This writes a single Ethernet packet to a FILE interface. The format is to
   write the length, then the data.
   write the length, then the data.
 
 
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] buf     Where to get the data.
   @param[in] buf     Where to get the data.
   @param[in] length  Length of data to write.
   @param[in] length  Length of data to write.
 
 
   @return  The length if successful, a negative value otherwise.             */
   @return  The length if successful, a negative value otherwise.             */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_write_file_packet (struct eth_device *eth,
eth_write_file_packet (struct eth_device *eth,
                       unsigned char     *buf,
                       unsigned char     *buf,
                       unsigned long int  length)
                       int32_t            length)
{
{
  ssize_t  nwritten;
  ssize_t  nwritten;
 
 
  /* Write length to file. */
  /* Write length to file. */
  nwritten = write (eth->txfd, &(length), sizeof (length));
  nwritten = write (eth->txfd, &(length), sizeof (length));
  if (nwritten != sizeof (length))
  if (nwritten != sizeof (length))
    {
    {
      fprintf (stderr, "ERROR: Failed to write Ethernet packet length: %s.\n",
      fprintf (stderr, "ERROR: Failed to write Ethernet packet length: %s.\n",
               strerror (errno));
               strerror (errno));
      return  -1;
      return  -1;
    }
    }
 
 
  /* write data to file */
  /* write data to file */
  nwritten = write (eth->txfd, buf, length);
  nwritten = write (eth->txfd, buf, length);
  if (nwritten != length)
  if (nwritten != length)
    {
    {
      fprintf (stderr, "ERROR: Failed to write Ethernet packet data: %s.\n",
      fprintf (stderr, "ERROR: Failed to write Ethernet packet data: %s.\n",
               strerror (errno));
               strerror (errno));
      return  -1;
      return  -1;
    }
    }
 
 
  return  nwritten;
  return  nwritten;
 
 
}       /* eth_write_file_packet () */
}       /* eth_write_file_packet () */
 
 
 
 
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Write an Ethernet packet to a TAP interface.
/*!Write an Ethernet packet to a TAP interface.
 
 
   This writes a single Ethernet packet to a TAP interface.
   This writes a single Ethernet packet to a TAP interface.
 
 
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] buf     Where to get the data.
   @param[in] buf     Where to get the data.
   @param[in] length  Length of data to write.
   @param[in] length  Length of data to write.
 
 
   @return  The length if successful, a negative value otherwise.             */
   @return  The length if successful, a negative value otherwise.             */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_write_tap_packet (struct eth_device *eth,
eth_write_tap_packet (struct eth_device *eth,
                      unsigned char     *buf,
                      unsigned char     *buf,
                      unsigned long int  length)
                      unsigned long int  length)
{
{
  ssize_t  nwritten = 0;
  ssize_t  nwritten = 0;
 
 
 
 
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  int  j;
  int  j;
 
 
  printf ("Writing TAP\n");
  printf ("Writing TAP\n");
  printf ("  packet %d bytes:", (int) length);
  printf ("  packet %d bytes:", (int) length);
 
 
  for (j = 0; j < length; j++)
  for (j = 0; j < length; j++)
    {
    {
      if (0 == (j % 16))
      if (0 == (j % 16))
        {
        {
          printf ("\n");
          printf ("\n");
        }
        }
      else if (0 == (j % 8))
      else if (0 == (j % 8))
        {
        {
          printf (" ");
          printf (" ");
        }
        }
 
 
      printf ("%.2x ", buf[j]);
      printf ("%.2x ", buf[j]);
    }
    }
 
 
  printf("\nend packet:\n");
  printf("\nend packet:\n");
#endif    
#endif    
 
 
  /* Write the data to the TAP */
  /* Write the data to the TAP */
  nwritten = write (eth->rtx_fd, buf, length);
  nwritten = write (eth->rtx_fd, buf, length);
  if (nwritten != length)
  if (nwritten != length)
    {
    {
      fprintf (stderr, "ERROR: Failed to write Ethernet packet data: %s.\n",
      fprintf (stderr, "ERROR: Failed to write Ethernet packet data: %s.\n",
               strerror (errno));
               strerror (errno));
      return  -1;
      return  -1;
    }
    }
 
 
  return  nwritten;
  return  nwritten;
 
 
}       /* eth_write_tap_packet () */
}       /* eth_write_tap_packet () */
#endif
#endif
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Write an Ethernet packet.
/*!Write an Ethernet packet.
 
 
   This writes a single Ethernet packet to the outside world from the supplied
   This writes a single Ethernet packet to the outside world from the supplied
   buffer. It deals with the different types of external interface.
   buffer. It deals with the different types of external interface.
 
 
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] eth     Pointer to the relevant Ethernet data structure.
   @param[in] buf     Where to get the data.
   @param[in] buf     Where to get the data.
   @param[in] length  Length of data to write.
   @param[in] length  Length of data to write.
 
 
   @return  The length if successful, zero if no packet was available,
   @return  The length if successful, zero if no packet was available,
            a negative value otherwise.                                       */
            a negative value otherwise.                                       */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_write_packet (struct eth_device *eth,
eth_write_packet (struct eth_device *eth,
                  unsigned char     *buf,
                  unsigned char     *buf,
                  ssize_t            length)
                  ssize_t            length)
{
{
  /* Send packet according to interface type. */
  /* Send packet according to interface type. */
  switch (eth->rtx_type)
  switch (eth->rtx_type)
    {
    {
    case ETH_RTX_FILE: return  eth_write_file_packet (eth, buf, length);
    case ETH_RTX_FILE: return  eth_write_file_packet (eth, buf, length);
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
    case ETH_RTX_TAP:  return  eth_write_tap_packet (eth, buf, length);
    case ETH_RTX_TAP:  return  eth_write_tap_packet (eth, buf, length);
#endif
#endif
 
 
    default:
    default:
      fprintf (stderr, "Unknown Ethernet write interface: ignored.\n");
      fprintf (stderr, "Unknown Ethernet write interface: ignored.\n");
      return  (ssize_t) -1;
      return  (ssize_t) -1;
    }
    }
}       /* eth_write_packet () */
}       /* eth_write_packet () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Flush a Tx buffer descriptor to the outside world.
/*!Flush a Tx buffer descriptor to the outside world.
 
 
   We know the buffer descriptor is full, so write it to the appropriate
   We know the buffer descriptor is full, so write it to the appropriate
   outside interface.
   outside interface.
 
 
   @param[in] eth  The Ethernet data structure.                               */
   @param[in] eth  The Ethernet data structure.                               */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_flush_bd (struct eth_device *eth)
eth_flush_bd (struct eth_device *eth)
{
{
  /* First word of BD is flags and length, second is pointer to buffer */
  /* First word of BD is flags and length, second is pointer to buffer */
  unsigned long int  bd_info = eth->regs.bd_ram[eth->tx_bd_index];
  unsigned long int  bd_info = eth->regs.bd_ram[eth->tx_bd_index];
  unsigned long int  bd_addr = eth->regs.bd_ram[eth->tx_bd_index + 1];
  unsigned long int  bd_addr = eth->regs.bd_ram[eth->tx_bd_index + 1];
  unsigned char      buf[ETH_MAXPL];
  unsigned char      buf[ETH_MAXPL];
  long int           packet_length;
  long int           packet_length;
  long int           bytes_sent;
  long int           bytes_sent;
  int                ok_to_int_p;
  int                ok_to_int_p;
 
 
  /* Get the packet length */
  /* Get the packet length */
  packet_length = GET_FIELD (bd_info, ETH_TX_BD, LENGTH);
  packet_length = GET_FIELD (bd_info, ETH_TX_BD, LENGTH);
 
 
  /* Clear error status bits and retry count. */
  /* Clear error status bits and retry count. */
  CLEAR_FLAG (bd_info, ETH_TX_BD, DEFER);
  CLEAR_FLAG (bd_info, ETH_TX_BD, DEFER);
  CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
  CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
  CLEAR_FLAG (bd_info, ETH_TX_BD, RETRANSMIT);
  CLEAR_FLAG (bd_info, ETH_TX_BD, RETRANSMIT);
  CLEAR_FLAG (bd_info, ETH_TX_BD, UNDERRUN);
  CLEAR_FLAG (bd_info, ETH_TX_BD, UNDERRUN);
  CLEAR_FLAG (bd_info, ETH_TX_BD, NO_CARRIER);
  CLEAR_FLAG (bd_info, ETH_TX_BD, NO_CARRIER);
 
 
  SET_FIELD (bd_info, ETH_TX_BD, RETRY, 0);
  SET_FIELD (bd_info, ETH_TX_BD, RETRY, 0);
 
 
  /* Copy data from buffer descriptor address into our local buf. */
  /* Copy data from buffer descriptor address into our local buf. */
  for (bytes_sent = 0; bytes_sent < packet_length; bytes_sent +=4)
  for (bytes_sent = 0; bytes_sent < packet_length; bytes_sent +=4)
    {
    {
      unsigned long int  read_word =
      unsigned long int  read_word =
        eval_direct32 (bytes_sent + bd_addr, 0, 0);
        eval_direct32 (bytes_sent + bd_addr, 0, 0);
 
 
      buf[bytes_sent]     = (unsigned char) (read_word >> 24);
      buf[bytes_sent]     = (unsigned char) (read_word >> 24);
      buf[bytes_sent + 1] = (unsigned char) (read_word >> 16);
      buf[bytes_sent + 1] = (unsigned char) (read_word >> 16);
      buf[bytes_sent + 2] = (unsigned char) (read_word >> 8);
      buf[bytes_sent + 2] = (unsigned char) (read_word >> 8);
      buf[bytes_sent + 3] = (unsigned char) (read_word);
      buf[bytes_sent + 3] = (unsigned char) (read_word);
    }
    }
 
 
  /* Send packet according to interface type and set BD status. If we didn't
  /* Send packet according to interface type and set BD status. If we didn't
     write the whole packet, then we retry. */
     write the whole packet, then we retry. */
  if (eth_write_packet (eth, buf, packet_length) == packet_length)
  if (eth_write_packet (eth, buf, packet_length) == packet_length)
    {
    {
      CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
      CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
      SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
      SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
      ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M);
      ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M);
    }
    }
  else
  else
    {
    {
      /* Does this retry mechanism really work? */
      /* Does this retry mechanism really work? */
      CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
      CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
      CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
      CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
      SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXE);
      SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXE);
      ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXE_M);
      ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXE_M);
#if ETH_DEBUG
#if ETH_DEBUG
      printf ("Transmit retry request.\n");
      printf ("Transmit retry request.\n");
#endif
#endif
    }
    }
 
 
  /* Update the flags in the buffer descriptor */
  /* Update the flags in the buffer descriptor */
  eth->regs.bd_ram[eth->tx_bd_index] = bd_info;
  eth->regs.bd_ram[eth->tx_bd_index] = bd_info;
 
 
  /* Generate interrupt to indicate transfer complete, under the
  /* Generate interrupt to indicate transfer complete, under the
     following criteria all being met:
     following criteria all being met:
     - either INT_MASK flag for Tx (OK or error) is set
     - either INT_MASK flag for Tx (OK or error) is set
     - the buffer descriptor has its IRQ flag set
     - the buffer descriptor has its IRQ flag set
     - there is no interrupt in progress.
     - there is no interrupt in progress.
 
 
     @todo We ought to warn if we get here and fail to set an IRQ. */
     @todo We ought to warn if we get here and fail to set an IRQ. */
  if (ok_to_int_p && TEST_FLAG (bd_info, ETH_TX_BD, IRQ))
  if (ok_to_int_p && TEST_FLAG (bd_info, ETH_TX_BD, IRQ))
    {
    {
      if (eth->int_line_stat)
      if (eth->int_line_stat)
        {
        {
          fprintf (stderr, "Warning: Interrupt active during Tx.\n");
          fprintf (stderr, "Warning: Interrupt active during Tx.\n");
        }
        }
      else
      else
        {
        {
#if ETH_DEBUG
#if ETH_DEBUG
          printf ("TRANSMIT interrupt\n");
          printf ("TRANSMIT interrupt\n");
#endif
#endif
          report_interrupt (eth->mac_int);
          report_interrupt (eth->mac_int);
          eth->int_line_stat = 1;
          eth->int_line_stat = 1;
        }
        }
    }
    }
 
 
  /* Advance to next BD, wrapping around if appropriate. */
  /* Advance to next BD, wrapping around if appropriate. */
  if (TEST_FLAG (bd_info, ETH_TX_BD, WRAP) ||
  if (TEST_FLAG (bd_info, ETH_TX_BD, WRAP) ||
      eth->tx_bd_index >= ((eth->regs.tx_bd_num - 1) * 2))
      eth->tx_bd_index >= ((eth->regs.tx_bd_num - 1) * 2))
    {
    {
      eth->tx_bd_index = 0;
      eth->tx_bd_index = 0;
    }
    }
  else
  else
    {
    {
      eth->tx_bd_index += 2;
      eth->tx_bd_index += 2;
    }
    }
}       /* eth_flush_bd () */
}       /* eth_flush_bd () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Tx clock function.
/*!Tx clock function.
 
 
   Responsible for starting and completing any TX actions.
   Responsible for starting and completing any TX actions.
 
 
   The original version had 4 states, which allowed modeling the transfer of
   The original version had 4 states, which allowed modeling the transfer of
   data one byte per cycle.  For now we use only the one state for
   data one byte per cycle.  For now we use only the one state for
   efficiency. When we find something in a buffer descriptor, we transmit
   efficiency. When we find something in a buffer descriptor, we transmit
   it.
   it.
 
 
   We reschedule every cycle. There is no point in trying to do anything if
   We reschedule every cycle. There is no point in trying to do anything if
   there is an interrupt still being processed by the core.
   there is an interrupt still being processed by the core.
 
 
   @todo We should eventually reinstate the one byte per cycle transfer.
   @todo We should eventually reinstate the one byte per cycle transfer.
 
 
   @param[in] dat  The Ethernet data structure, passed as a void pointer.    */
   @param[in] dat  The Ethernet data structure, passed as a void pointer.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_controller_tx_clock (void *dat)
eth_controller_tx_clock (void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  /* Only do anything if there is not an interrupt outstanding. */
  /* Only do anything if there is not an interrupt outstanding. */
  if (!eth->int_line_stat)
  if (!eth->int_line_stat)
    {
    {
      /* First word of BD is flags. If we have a buffer ready, get it and
      /* First word of BD is flags. If we have a buffer ready, get it and
         transmit it. */
         transmit it. */
      if (TEST_FLAG (eth->regs.bd_ram[eth->tx_bd_index], ETH_TX_BD, READY))
      if (TEST_FLAG (eth->regs.bd_ram[eth->tx_bd_index], ETH_TX_BD, READY))
        {
        {
          eth_flush_bd (eth);
          eth_flush_bd (eth);
        }
        }
    }
    }
 
 
  /* Reschedule to wake up again. */
  /* Reschedule to wake up again. */
  SCHED_ADD (eth_controller_tx_clock, dat, RTX_RESCHED_PERIOD);
  SCHED_ADD (eth_controller_tx_clock, dat, RTX_RESCHED_PERIOD);
 
 
}       /* eth_controller_tx_clock () */
}       /* eth_controller_tx_clock () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Read an Ethernet packet from a FILE interface.
/*!Read an Ethernet packet from a FILE interface.
 
 
   This reads a single Ethernet packet from the outside world via a FILE
   This reads a single Ethernet packet from the outside world via a FILE
   interface.
   interface.
 
 
   The format is 4 bytes of packet length, followed by the packet data.
   The format is 4 bytes of packet length, followed by the packet data.
 
 
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[out] buf  Where to put the data
   @param[out] buf  Where to put the data
 
 
   @return  The length if successful, zero if no packet was available
   @return  The length if successful, zero if no packet was available
            (i.e. EOF), a negative value otherwise.                           */
            (i.e. EOF), a negative value otherwise.                           */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_read_file_packet (struct eth_device *eth,
eth_read_file_packet (struct eth_device *eth,
                      unsigned char     *buf)
                      unsigned char     *buf)
{
{
  ssize_t  packet_length;
  int32_t  packet_length;
  ssize_t  nread;
  ssize_t  nread;
 
 
  /* Read packet length. We may be at EOF. */
  /* Read packet length. We may be at EOF. */
  nread = read (eth->rxfd, &(packet_length), sizeof (packet_length));
  nread = read (eth->rxfd, &(packet_length), sizeof (packet_length));
 
 
  if (0 == nread)
  if (0 == nread)
    {
    {
      return  0;                 /* No more packets */
      return  0;                 /* No more packets */
    }
    }
  else if (nread < sizeof (packet_length))
  else if (nread < sizeof (packet_length))
    {
    {
      fprintf (stderr, "ERROR: Failed to read length from file.\n");
      fprintf (stderr, "ERROR: Failed to read length from file.\n");
      return  -1;
      return  -1;
    }
    }
 
 
  /* Packet must be big enough to hold a header */
  /* Packet must be big enough to hold a header */
  if (packet_length < ETHER_HDR_LEN)
  if (packet_length < ETHER_HDR_LEN)
    {
    {
      fprintf (stderr, "Warning: Ethernet packet length %zd too small.\n",
      fprintf (stderr, "Warning: Ethernet packet length %d too small.\n",
               packet_length);
               packet_length);
      return  -1;
      return  -1;
    }
    }
 
 
  /* Read the packet proper. */
  /* Read the packet proper. */
  nread = read (eth->rxfd, buf, packet_length);
  nread = read (eth->rxfd, buf, packet_length);
 
 
  if (nread != packet_length)
  if (nread != packet_length)
    {
    {
      fprintf (stderr, "ERROR: Failed to read packet from file.\n");
      fprintf (stderr, "ERROR: Failed to read packet from file.\n");
      return  -1;
      return  -1;
    }
    }
 
 
  return  packet_length;
  return  (ssize_t)packet_length;
 
 
}       /* eth_read_file_packet () */
}       /* eth_read_file_packet () */
 
 
 
 
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Read an Ethernet packet from a FILE interface.
/*!Read an Ethernet packet from a FILE interface.
 
 
   This reads a single Ethernet packet from the outside world via a TAP
   This reads a single Ethernet packet from the outside world via a TAP
   interface.
   interface.
 
 
   A complete packet is always read, so its length (minus CRC) is the amount
   A complete packet is always read, so its length (minus CRC) is the amount
   read.
   read.
 
 
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[out] buf  Where to put the data
   @param[out] buf  Where to put the data
 
 
   @return  The length if successful, zero if no packet was available,
   @return  The length if successful, zero if no packet was available,
            a negative value otherwise.                                       */
            a negative value otherwise.                                       */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_read_tap_packet (struct eth_device *eth,
eth_read_tap_packet (struct eth_device *eth,
                     unsigned char     *buf)
                     unsigned char     *buf)
{
{
 
 
  struct pollfd  fds[1];
  struct pollfd  fds[1];
  int            n;
  int            n;
  ssize_t        packet_length;
  ssize_t        packet_length;
 
 
  /* Poll to see if there is data to read */
  /* Poll to see if there is data to read */
  fds[0].fd     = eth->rtx_fd;
  fds[0].fd     = eth->rtx_fd;
  fds[0].events = POLLIN;
  fds[0].events = POLLIN;
 
 
  n = poll (fds, 1, 0);
  n = poll (fds, 1, 0);
  if (n < 0)
  if (n < 0)
    {
    {
      fprintf (stderr, "Warning: Poll for TAP receive failed %s: ignored.\n",
      fprintf (stderr, "Warning: Poll for TAP receive failed %s: ignored.\n",
               strerror (errno));
               strerror (errno));
      return  -1;
      return  -1;
    }
    }
  else if ((n > 0) && ((fds[0].revents & POLLIN) == POLLIN))
  else if ((n > 0) && ((fds[0].revents & POLLIN) == POLLIN))
    {
    {
      /* Data to be read from TAP */
      /* Data to be read from TAP */
      packet_length = read (eth->rtx_fd, buf, ETH_MAXPL);
      packet_length = read (eth->rtx_fd, buf, ETH_MAXPL);
#if ETH_DEBUG
#if ETH_DEBUG
      printf ("%d bytes read from TAP.\n", (int) packet_length);
      printf ("%d bytes read from TAP.\n", (int) packet_length);
#endif
#endif
      if (packet_length < 0)
      if (packet_length < 0)
        {
        {
          fprintf (stderr, "Warning: Read of RXTATE_RECV failed: %s.\n",
          fprintf (stderr, "Warning: Read of RXTATE_RECV failed: %s.\n",
                   strerror (errno));
                   strerror (errno));
          SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, RXE);
          SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, RXE);
 
 
          /* Signal interrupt if enabled, and no interrupt currently in
          /* Signal interrupt if enabled, and no interrupt currently in
             progress. */
             progress. */
          if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, RXE_M) &&
          if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, RXE_M) &&
              !eth->int_line_stat)
              !eth->int_line_stat)
            {
            {
#if ETH_DEBUG
#if ETH_DEBUG
              printf ("Ethernet failed receive interrupt\n");
              printf ("Ethernet failed receive interrupt\n");
#endif
#endif
              report_interrupt (eth->mac_int);
              report_interrupt (eth->mac_int);
              eth->int_line_stat = 1;
              eth->int_line_stat = 1;
            }
            }
        }
        }
 
 
      return  packet_length;
      return  packet_length;
    }
    }
  else
  else
    {
    {
      return  0;                 /* No packet */
      return  0;                 /* No packet */
    }
    }
}       /* eth_read_tap_packet () */
}       /* eth_read_tap_packet () */
 
 
#endif
#endif
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Read an Ethernet packet.
/*!Read an Ethernet packet.
 
 
   This reads a single Ethernet packet from the outside world into the
   This reads a single Ethernet packet from the outside world into the
   supplied buffer. It deals with the different types of external interface.
   supplied buffer. It deals with the different types of external interface.
 
 
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[in]  eth  Pointer to the relevant Ethernet data structure
   @param[out] buf  Where to put the data
   @param[out] buf  Where to put the data
 
 
   @return  The length if successful, zero if no packet was available,
   @return  The length if successful, zero if no packet was available,
            a negative value otherwise.                                       */
            a negative value otherwise.                                       */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static ssize_t
static ssize_t
eth_read_packet (struct eth_device *eth,
eth_read_packet (struct eth_device *eth,
                 unsigned char     *buf)
                 unsigned char     *buf)
{
{
  switch (eth->rtx_type)
  switch (eth->rtx_type)
    {
    {
    case ETH_RTX_FILE: return  eth_read_file_packet (eth, buf);
    case ETH_RTX_FILE: return  eth_read_file_packet (eth, buf);
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
    case ETH_RTX_TAP:  return  eth_read_tap_packet (eth, buf);
    case ETH_RTX_TAP:  return  eth_read_tap_packet (eth, buf);
#endif
#endif
 
 
    default:
    default:
      fprintf (stderr, "Unknown Ethernet read interface: ignored.\n");
      fprintf (stderr, "Unknown Ethernet read interface: ignored.\n");
      return  (ssize_t) -1;
      return  (ssize_t) -1;
    }
    }
}       /* eth_read_packet () */
}       /* eth_read_packet () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Fill a buffer descriptor
/*!Fill a buffer descriptor
 
 
   A buffer descriptor is empty. Attempt to fill it from the outside world.
   A buffer descriptor is empty. Attempt to fill it from the outside world.
 
 
   @param[in] eth  The Ethernet data structure, passed as a void pointer.    */
   @param[in] eth  The Ethernet data structure, passed as a void pointer.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_fill_bd (struct eth_device *eth)
eth_fill_bd (struct eth_device *eth)
{
{
 
 
  /* First word of BD is flags and length, second is pointer to buffer */
  /* First word of BD is flags and length, second is pointer to buffer */
  unsigned long int  bd_info = eth->regs.bd_ram[eth->rx_bd_index];
  unsigned long int  bd_info = eth->regs.bd_ram[eth->rx_bd_index];
  unsigned long int  bd_addr = eth->regs.bd_ram[eth->rx_bd_index + 1];
  unsigned long int  bd_addr = eth->regs.bd_ram[eth->rx_bd_index + 1];
 
 
  long int           packet_length;
  long int           packet_length;
  long int           bytes_read;
  long int           bytes_read;
  unsigned char      buf[ETH_MAXPL];
  unsigned char      buf[ETH_MAXPL];
 
 
  /* Clear various status bits */
  /* Clear various status bits */
  CLEAR_FLAG (bd_info, ETH_RX_BD, MISS);
  CLEAR_FLAG (bd_info, ETH_RX_BD, MISS);
  CLEAR_FLAG (bd_info, ETH_RX_BD, INVALID);
  CLEAR_FLAG (bd_info, ETH_RX_BD, INVALID);
  CLEAR_FLAG (bd_info, ETH_RX_BD, DRIBBLE);
  CLEAR_FLAG (bd_info, ETH_RX_BD, DRIBBLE);
  CLEAR_FLAG (bd_info, ETH_RX_BD, UVERRUN);
  CLEAR_FLAG (bd_info, ETH_RX_BD, UVERRUN);
  CLEAR_FLAG (bd_info, ETH_RX_BD, COLLISION);
  CLEAR_FLAG (bd_info, ETH_RX_BD, COLLISION);
  CLEAR_FLAG (bd_info, ETH_RX_BD, TOOBIG);
  CLEAR_FLAG (bd_info, ETH_RX_BD, TOOBIG);
  CLEAR_FLAG (bd_info, ETH_RX_BD, TOOSHORT);
  CLEAR_FLAG (bd_info, ETH_RX_BD, TOOSHORT);
 
 
  /* Loopback is permitted. We believe that Linux never uses it, so we'll
  /* Loopback is permitted. We believe that Linux never uses it, so we'll
     note the attempt and ignore.
     note the attempt and ignore.
 
 
     @todo We should support this. */
     @todo We should support this. */
  if (TEST_FLAG (eth->regs.moder, ETH_MODER, LOOPBCK))
  if (TEST_FLAG (eth->regs.moder, ETH_MODER, LOOPBCK))
    {
    {
      PRINTF ("Ethernet loopback requested.\n");
      PRINTF ("Ethernet loopback requested.\n");
      fprintf (stderr, "ERROR: Loopback not supported. Ignored.\n");
      fprintf (stderr, "ERROR: Loopback not supported. Ignored.\n");
    }
    }
 
 
  packet_length = eth_read_packet (eth, buf);
  packet_length = eth_read_packet (eth, buf);
  if (packet_length <= 0)
  if (packet_length <= 0)
    {
    {
      /* Empty packet or error. No more to do here. */
      /* Empty packet or error. No more to do here. */
      return;
      return;
    }
    }
 
 
/* Got a packet successfully. If not promiscuous mode, check the destination
/* Got a packet successfully. If not promiscuous mode, check the destination
   address is meant for us. */
   address is meant for us. */
  if (!TEST_FLAG (eth->regs.moder, ETH_MODER, PRO))
  if (!TEST_FLAG (eth->regs.moder, ETH_MODER, PRO))
    {
    {
      if (TEST_FLAG (eth->regs.moder, ETH_MODER, IAM))
      if (TEST_FLAG (eth->regs.moder, ETH_MODER, IAM))
        {
        {
          /* There is little documentation of how IAM is supposed to work. It
          /* There is little documentation of how IAM is supposed to work. It
             seems that some mapping function (not defined) maps the address
             seems that some mapping function (not defined) maps the address
             down to a number in the range 0-63. If that bit is set in
             down to a number in the range 0-63. If that bit is set in
             HASH0/HASH1 registers, the packet is accepted. */
             HASH0/HASH1 registers, the packet is accepted. */
          fprintf (stderr, "Warning: Individual Address Mode ignored.\n");
          fprintf (stderr, "Warning: Individual Address Mode ignored.\n");
        }
        }
 
 
      /* Check for HW address match. */
      /* Check for HW address match. */
      if ((0 != bcmp (eth->mac_address, buf, ETHER_ADDR_LEN)) &&
      if ((0 != bcmp (eth->mac_address, buf, ETHER_ADDR_LEN)) &&
          (0 != bcmp (mac_broadcast,    buf, ETHER_ADDR_LEN)))
          (0 != bcmp (mac_broadcast,    buf, ETHER_ADDR_LEN)))
        {
        {
#if ETH_DEBUG           
#if ETH_DEBUG           
          printf ("packet for %.2x:%.2x:%.2x:%.2x:%.2x:%.2x ignored.\n",
          printf ("packet for %.2x:%.2x:%.2x:%.2x:%.2x:%.2x ignored.\n",
                  buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
                  buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
#endif
#endif
          /* Not for us. No more to do here. */
          /* Not for us. No more to do here. */
          return;
          return;
        }
        }
    }
    }
 
 
  /* Transfer the buffer into the BD. */
  /* Transfer the buffer into the BD. */
#if ETH_DEBUG
#if ETH_DEBUG
  printf ("writing to Rx BD%d: %d bytes @ 0x%.8x\n",
  printf ("writing to Rx BD%d: %d bytes @ 0x%.8x\n",
          (int) eth->rx_bd_index / 2,  (int) packet_length,
          (int) eth->rx_bd_index / 2,  (int) packet_length,
          (unsigned int)bd_addr);
          (unsigned int)bd_addr);
#endif
#endif
 
 
  for (bytes_read = 0; bytes_read < packet_length; bytes_read +=4)
  for (bytes_read = 0; bytes_read < packet_length; bytes_read +=4)
    {
    {
      unsigned long int  send_word =
      unsigned long int  send_word =
        ((unsigned long) buf[bytes_read]     << 24) |
        ((unsigned long) buf[bytes_read]     << 24) |
        ((unsigned long) buf[bytes_read + 1] << 16) |
        ((unsigned long) buf[bytes_read + 1] << 16) |
        ((unsigned long) buf[bytes_read + 2] <<  8) |
        ((unsigned long) buf[bytes_read + 2] <<  8) |
        ((unsigned long) buf[bytes_read + 3]      );
        ((unsigned long) buf[bytes_read + 3]      );
      set_direct32 (bd_addr + bytes_read, send_word, 0, 0);
      set_direct32 (bd_addr + bytes_read, send_word, 0, 0);
    }
    }
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  printf("BD filled with 0x%08lx bytes.\n", bytes_read);
  printf("BD filled with 0x%08lx bytes.\n", bytes_read);
#endif
#endif
 
 
  /* Write result to BD.
  /* Write result to BD.
 
 
     The OpenRISC MAC hardware passes on the CRC (which it should not). The
     The OpenRISC MAC hardware passes on the CRC (which it should not). The
     Linux drivers have been written to expect a value (which they ignore). So
     Linux drivers have been written to expect a value (which they ignore). So
     for consistency, we pretend the length is 4 bytes longer.
     for consistency, we pretend the length is 4 bytes longer.
 
 
     This is now controlled by a configuration parameter, dummy_crc. For
     This is now controlled by a configuration parameter, dummy_crc. For
     backwards compatibility, it defaults to TRUE. */
     backwards compatibility, it defaults to TRUE. */
  SET_FIELD (bd_info, ETH_RX_BD, LENGTH,
  SET_FIELD (bd_info, ETH_RX_BD, LENGTH,
             packet_length + (eth->dummy_crc ? 4 : 0));
             packet_length + (eth->dummy_crc ? 4 : 0));
  CLEAR_FLAG (bd_info, ETH_RX_BD, READY);
  CLEAR_FLAG (bd_info, ETH_RX_BD, READY);
  SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, RXB);
  SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, RXB);
 
 
  eth->regs.bd_ram[eth->rx_bd_index] = bd_info;
  eth->regs.bd_ram[eth->rx_bd_index] = bd_info;
 
 
  /* Advance to next BD. The Rx BDs start after the Tx BDs. */
  /* Advance to next BD. The Rx BDs start after the Tx BDs. */
  if (TEST_FLAG (bd_info, ETH_RX_BD, WRAP) ||
  if (TEST_FLAG (bd_info, ETH_RX_BD, WRAP) ||
      (eth->rx_bd_index >= ETH_BD_COUNT))
      (eth->rx_bd_index >= ETH_BD_COUNT))
    {
    {
      eth->rx_bd_index = eth->regs.tx_bd_num * 2;
      eth->rx_bd_index = eth->regs.tx_bd_num * 2;
    }
    }
  else
  else
    {
    {
      eth->rx_bd_index += 2;
      eth->rx_bd_index += 2;
    }
    }
 
 
  /* Raise an interrupt if necessary. */
  /* Raise an interrupt if necessary. */
  if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, RXB_M) &&
  if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, RXB_M) &&
      TEST_FLAG (bd_info, ETH_RX_BD, IRQ))
      TEST_FLAG (bd_info, ETH_RX_BD, IRQ))
    {
    {
      if (eth->int_line_stat)
      if (eth->int_line_stat)
        {
        {
          fprintf (stderr, "Warning: Interrupt active during Rx.\n");
          fprintf (stderr, "Warning: Interrupt active during Rx.\n");
        }
        }
      else
      else
        {
        {
#if ETH_DEBUG
#if ETH_DEBUG
          printf ("Rx successful receive interrupt\n");
          printf ("Rx successful receive interrupt\n");
#endif
#endif
          report_interrupt (eth->mac_int);
          report_interrupt (eth->mac_int);
          eth->int_line_stat = 1;
          eth->int_line_stat = 1;
        }
        }
    }
    }
}       /* eth_fill_bd () */
}       /* eth_fill_bd () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Ignore a packet from the TAP interface.
/*!Ignore a packet from the TAP interface.
 
 
   We don't have a BD ready, so any packets waiting should be thrown away.
   We don't have a BD ready, so any packets waiting should be thrown away.
 
 
   @param[in] eth  The Ethernet data structure.
   @param[in] eth  The Ethernet data structure.
 
 
   @return  1 (TRUE) if more or more packets were discarded, zero otherwise.  */
   @return  1 (TRUE) if more or more packets were discarded, zero otherwise.  */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static int
static int
eth_ignore_tap_packets (struct eth_device *eth)
eth_ignore_tap_packets (struct eth_device *eth)
{
{
  int  result = 0;
  int  result = 0;
 
 
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
 
 
  int  n;
  int  n;
 
 
  /* Read packets until there are none left. */
  /* Read packets until there are none left. */
  do
  do
    {
    {
      struct pollfd  fds[1];
      struct pollfd  fds[1];
 
 
      /* Poll to see if there is anything to be read. */
      /* Poll to see if there is anything to be read. */
      fds[0].fd     = eth->rtx_fd;
      fds[0].fd     = eth->rtx_fd;
      fds[0].events = POLLIN;
      fds[0].events = POLLIN;
 
 
      n = poll (fds, 1, 0);
      n = poll (fds, 1, 0);
      if (n < 0)
      if (n < 0)
        {
        {
          /* Give up with a warning if poll fails */
          /* Give up with a warning if poll fails */
          fprintf (stderr,
          fprintf (stderr,
                   "Warning: Poll error while emptying TAP: %s: ignored.\n",
                   "Warning: Poll error while emptying TAP: %s: ignored.\n",
                   strerror (errno));
                   strerror (errno));
          return  result;
          return  result;
        }
        }
      else if ((n > 0) && ((fds[0].revents & POLLIN) == POLLIN))
      else if ((n > 0) && ((fds[0].revents & POLLIN) == POLLIN))
        {
        {
          unsigned char  buf[ETH_MAXPL];
          unsigned char  buf[ETH_MAXPL];
          ssize_t        nread = eth_read_packet (eth, buf);
          ssize_t        nread = eth_read_packet (eth, buf);
 
 
          if (nread < 0)
          if (nread < 0)
            {
            {
              /* Give up with a warning if read fails */
              /* Give up with a warning if read fails */
              fprintf (stderr,
              fprintf (stderr,
                       "Warning: Read of when Ethernet busy failed %s.\n",
                       "Warning: Read of when Ethernet busy failed %s.\n",
                       strerror (errno));
                       strerror (errno));
              return  result;
              return  result;
            }
            }
          else if (nread > 0)
          else if (nread > 0)
            {
            {
              /* Record that a packet was thrown away. */
              /* Record that a packet was thrown away. */
              result = 1;
              result = 1;
#if ETH_DEBUG
#if ETH_DEBUG
              printf ("Ethernet discarding %d bytes from TAP while BD full.\n",
              printf ("Ethernet discarding %d bytes from TAP while BD full.\n",
                      nread);
                      nread);
#endif
#endif
            }
            }
        }
        }
    }
    }
  while (n > 0);
  while (n > 0);
 
 
#endif
#endif
 
 
  return  result;
  return  result;
 
 
}       /* eth_ignore_tap_packets () */
}       /* eth_ignore_tap_packets () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Rx clock function.
/*!Rx clock function.
 
 
   Responsible for starting and completing any RX actions.
   Responsible for starting and completing any RX actions.
 
 
   The original version had 4 states, which allowed modeling the transfer of
   The original version had 4 states, which allowed modeling the transfer of
   data one byte per cycle.  For now we use only the one state for
   data one byte per cycle.  For now we use only the one state for
   efficiency. When the buffer is empty, we fill it from the external world.
   efficiency. When the buffer is empty, we fill it from the external world.
 
 
   We schedule to wake up again each cycle. This means we will get called when
   We schedule to wake up again each cycle. This means we will get called when
   the core is still processing the previous interrupt. To avoid races, we do
   the core is still processing the previous interrupt. To avoid races, we do
   nothing until the interrupt is cleared.
   nothing until the interrupt is cleared.
 
 
   @todo We should eventually reinstate the one byte per cycle transfer.
   @todo We should eventually reinstate the one byte per cycle transfer.
 
 
   @param[in] dat  The Ethernet data structure, passed as a void pointer.    */
   @param[in] dat  The Ethernet data structure, passed as a void pointer.    */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_controller_rx_clock (void *dat)
eth_controller_rx_clock (void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  /* Only do anything if there is not an interrupt outstanding. */
  /* Only do anything if there is not an interrupt outstanding. */
  if (!eth->int_line_stat)
  if (!eth->int_line_stat)
    {
    {
      /* First word of the BD is flags, where we can test if it's ready. */
      /* First word of the BD is flags, where we can test if it's ready. */
      if (TEST_FLAG (eth->regs.bd_ram[eth->rx_bd_index], ETH_RX_BD, READY))
      if (TEST_FLAG (eth->regs.bd_ram[eth->rx_bd_index], ETH_RX_BD, READY))
        {
        {
          /* The BD is empty, so we try to fill it with data from the outside
          /* The BD is empty, so we try to fill it with data from the outside
             world. */
             world. */
          eth_fill_bd (eth);    /* BD ready to be filled. */
          eth_fill_bd (eth);    /* BD ready to be filled. */
        }
        }
      else if ((TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN)) &&
      else if ((TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN)) &&
               (ETH_RTX_FILE == eth->rtx_type))
               (ETH_RTX_FILE == eth->rtx_type))
        {
        {
          /* The BD is full, Rx is enabled and we are reading from an external
          /* The BD is full, Rx is enabled and we are reading from an external
             TAP interface. We can't take any more, so we'll throw oustanding
             TAP interface. We can't take any more, so we'll throw oustanding
             input packets on the floor.
             input packets on the floor.
 
 
             @note We don't do this for file I/O, since it would discard
             @note We don't do this for file I/O, since it would discard
             everything immediately! */
             everything immediately! */
          if (eth_ignore_tap_packets (eth))
          if (eth_ignore_tap_packets (eth))
            {
            {
              /* A packet has been thrown away, so mark the INT_SOURCE
              /* A packet has been thrown away, so mark the INT_SOURCE
                 register accordingly. */
                 register accordingly. */
              SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, BUSY);
              SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, BUSY);
 
 
              /* Raise an interrupt if necessary. */
              /* Raise an interrupt if necessary. */
              if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, BUSY_M))
              if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, BUSY_M))
                {
                {
                  if (eth->int_line_stat)
                  if (eth->int_line_stat)
                    {
                    {
                      fprintf (stderr,
                      fprintf (stderr,
                               "Warning: Interrupt active during ignore.\n");
                               "Warning: Interrupt active during ignore.\n");
                    }
                    }
                  else
                  else
                    {
                    {
#if ETH_DEBUG
#if ETH_DEBUG
                      printf ("Ethernet Rx BUSY interrupt\n");
                      printf ("Ethernet Rx BUSY interrupt\n");
#endif
#endif
                      report_interrupt (eth->mac_int);
                      report_interrupt (eth->mac_int);
                      eth->int_line_stat = 1;
                      eth->int_line_stat = 1;
                    }
                    }
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  /* Whatever happens, we reschedule a wake up in the future. */
  /* Whatever happens, we reschedule a wake up in the future. */
  SCHED_ADD (eth_controller_rx_clock, dat, RTX_RESCHED_PERIOD);
  SCHED_ADD (eth_controller_rx_clock, dat, RTX_RESCHED_PERIOD);
 
 
}       /* eth_controller_rx_clock () */
}       /* eth_controller_rx_clock () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!VAPI connection to outside.
/*!VAPI connection to outside.
 
 
   Used for remote testing of the interface. Currently does nothing.
   Used for remote testing of the interface. Currently does nothing.
 
 
   @param[in] id  The VAPI ID to use.
   @param[in] id  The VAPI ID to use.
   @param[in] data  Any data associated (unused here).
   @param[in] data  Any data associated (unused here).
   @param[in] dat   The Ethernet data structure, cast to a void pointer.      */
   @param[in] dat   The Ethernet data structure, cast to a void pointer.      */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_vapi_read (unsigned long int  id,
eth_vapi_read (unsigned long int  id,
               unsigned long int  data,
               unsigned long int  data,
               void              *dat)
               void              *dat)
{
{
  unsigned long int  which;
  unsigned long int  which;
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  which = id - eth->base_vapi_id;
  which = id - eth->base_vapi_id;
 
 
  if (!eth)
  if (!eth)
    {
    {
      return;
      return;
    }
    }
 
 
  switch (which)
  switch (which)
    {
    {
    case ETH_VAPI_DATA:
    case ETH_VAPI_DATA:
      break;
      break;
    case ETH_VAPI_CTRL:
    case ETH_VAPI_CTRL:
      break;
      break;
    }
    }
}
}
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Print register values on stdout
/*!Print register values on stdout
 
 
   @param[in] dat  The Ethernet interface data structure.                     */
   @param[in] dat  The Ethernet interface data structure.                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_status (void *dat)
eth_status (void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  PRINTF ("\nEthernet MAC at 0x%" PRIxADDR ":\n", eth->baseaddr);
  PRINTF ("\nEthernet MAC at 0x%" PRIxADDR ":\n", eth->baseaddr);
  PRINTF ("MODER        : 0x%08lX\n", eth->regs.moder);
  PRINTF ("MODER        : 0x%08lX\n", eth->regs.moder);
  PRINTF ("INT_SOURCE   : 0x%08lX\n", eth->regs.int_source);
  PRINTF ("INT_SOURCE   : 0x%08lX\n", eth->regs.int_source);
  PRINTF ("INT_MASK     : 0x%08lX\n", eth->regs.int_mask);
  PRINTF ("INT_MASK     : 0x%08lX\n", eth->regs.int_mask);
  PRINTF ("IPGT         : 0x%08lX\n", eth->regs.ipgt);
  PRINTF ("IPGT         : 0x%08lX\n", eth->regs.ipgt);
  PRINTF ("IPGR1        : 0x%08lX\n", eth->regs.ipgr1);
  PRINTF ("IPGR1        : 0x%08lX\n", eth->regs.ipgr1);
  PRINTF ("IPGR2        : 0x%08lX\n", eth->regs.ipgr2);
  PRINTF ("IPGR2        : 0x%08lX\n", eth->regs.ipgr2);
  PRINTF ("PACKETLEN    : 0x%08lX\n", eth->regs.packetlen);
  PRINTF ("PACKETLEN    : 0x%08lX\n", eth->regs.packetlen);
  PRINTF ("COLLCONF     : 0x%08lX\n", eth->regs.collconf);
  PRINTF ("COLLCONF     : 0x%08lX\n", eth->regs.collconf);
  PRINTF ("TX_BD_NUM    : 0x%08lX\n", eth->regs.tx_bd_num);
  PRINTF ("TX_BD_NUM    : 0x%08lX\n", eth->regs.tx_bd_num);
  PRINTF ("CTRLMODER    : 0x%08lX\n", eth->regs.controlmoder);
  PRINTF ("CTRLMODER    : 0x%08lX\n", eth->regs.controlmoder);
  PRINTF ("MIIMODER     : 0x%08lX\n", eth->regs.miimoder);
  PRINTF ("MIIMODER     : 0x%08lX\n", eth->regs.miimoder);
  PRINTF ("MIICOMMAND   : 0x%08lX\n", eth->regs.miicommand);
  PRINTF ("MIICOMMAND   : 0x%08lX\n", eth->regs.miicommand);
  PRINTF ("MIIADDRESS   : 0x%08lX\n", eth->regs.miiaddress);
  PRINTF ("MIIADDRESS   : 0x%08lX\n", eth->regs.miiaddress);
  PRINTF ("MIITX_DATA   : 0x%08lX\n", eth->regs.miitx_data);
  PRINTF ("MIITX_DATA   : 0x%08lX\n", eth->regs.miitx_data);
  PRINTF ("MIIRX_DATA   : 0x%08lX\n", eth->regs.miirx_data);
  PRINTF ("MIIRX_DATA   : 0x%08lX\n", eth->regs.miirx_data);
  PRINTF ("MIISTATUS    : 0x%08lX\n", eth->regs.miistatus);
  PRINTF ("MIISTATUS    : 0x%08lX\n", eth->regs.miistatus);
  PRINTF ("MAC Address  : %02X:%02X:%02X:%02X:%02X:%02X\n",
  PRINTF ("MAC Address  : %02X:%02X:%02X:%02X:%02X:%02X\n",
          eth->mac_address[5], eth->mac_address[4], eth->mac_address[3],
          eth->mac_address[5], eth->mac_address[4], eth->mac_address[3],
          eth->mac_address[2], eth->mac_address[1], eth->mac_address[0]);
          eth->mac_address[2], eth->mac_address[1], eth->mac_address[0]);
  PRINTF ("HASH0        : 0x%08lX\n", eth->regs.hash0);
  PRINTF ("HASH0        : 0x%08lX\n", eth->regs.hash0);
  PRINTF ("HASH1        : 0x%08lX\n", eth->regs.hash1);
  PRINTF ("HASH1        : 0x%08lX\n", eth->regs.hash1);
 
 
}       /* eth_status () */
}       /* eth_status () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Open the external file interface to the Ethernet
/*!Open the external file interface to the Ethernet
 
 
   The data is represented by an input and an output file.
   The data is represented by an input and an output file.
 
 
   @param[in] eth  The Ethernet interface data structure.                     */
   @param[in] eth  The Ethernet interface data structure.                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_open_file_if (struct eth_device *eth)
eth_open_file_if (struct eth_device *eth)
{
{
  /* (Re-)open TX/RX files */
  /* (Re-)open TX/RX files */
  if (eth->rxfd >= 0)
  if (eth->rxfd >= 0)
    {
    {
      close (eth->rxfd);
      close (eth->rxfd);
    }
    }
 
 
  if (eth->txfd >= 0)
  if (eth->txfd >= 0)
    {
    {
      close (eth->txfd);
      close (eth->txfd);
    }
    }
 
 
  eth->rxfd = -1;
  eth->rxfd = -1;
  eth->txfd = -1;
  eth->txfd = -1;
 
 
  eth->rxfd = open (eth->rxfile, O_RDONLY);
  eth->rxfd = open (eth->rxfile, O_RDONLY);
  if (eth->rxfd < 0)
  if (eth->rxfd < 0)
    {
    {
      fprintf (stderr, "Warning: Cannot open Ethernet RX file \"%s\": %s\n",
      fprintf (stderr, "Warning: Cannot open Ethernet RX file \"%s\": %s\n",
               eth->rxfile, strerror (errno));
               eth->rxfile, strerror (errno));
    }
    }
 
 
  eth->txfd = open (eth->txfile,
  eth->txfd = open (eth->txfile,
#if defined(O_SYNC)             /* BSD/MacOS X doesn't know about O_SYNC */
#if defined(O_SYNC)             /* BSD/MacOS X doesn't know about O_SYNC */
                    O_SYNC |
                    O_SYNC |
#endif
#endif
                    O_RDWR | O_CREAT | O_APPEND,
                    O_RDWR | O_CREAT | O_APPEND,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  if (eth->txfd < 0)
  if (eth->txfd < 0)
    {
    {
      fprintf (stderr, "Warning: Cannot open Ethernet TX file \"%s\": %s\n",
      fprintf (stderr, "Warning: Cannot open Ethernet TX file \"%s\": %s\n",
               eth->txfile, strerror (errno));
               eth->txfile, strerror (errno));
    }
    }
}       /* eth_open_file_if () */
}       /* eth_open_file_if () */
 
 
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Open the external TAP interface to the Ethernet
/*!Open the external TAP interface to the Ethernet
 
 
   Packets are transferred over a TAP/TUN interface. We assume a persistent
   Packets are transferred over a TAP/TUN interface. We assume a persistent
   tap interface has been set up and is owned by the user, so they can open
   tap interface has been set up and is owned by the user, so they can open
   and manipulate it. See the User Guide for details of setting this up.
   and manipulate it. See the User Guide for details of setting this up.
 
 
   @todo We don't flush the TAP interface. Should we?
   @todo We don't flush the TAP interface. Should we?
 
 
   @param[in] eth  The Ethernet interface data structure.                     */
   @param[in] eth  The Ethernet interface data structure.                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_open_tap_if (struct eth_device *eth)
eth_open_tap_if (struct eth_device *eth)
{
{
 
 
  struct ifreq       ifr;
  struct ifreq       ifr;
 
 
  /* We don't support re-opening. If it's open, it stays open. */
  /* We don't support re-opening. If it's open, it stays open. */
  if (eth->rtx_fd >= 0)
  if (eth->rtx_fd >= 0)
    {
    {
      return;
      return;
    }
    }
 
 
  /* Open the TUN/TAP device */
  /* Open the TUN/TAP device */
  eth->rtx_fd = open ("/dev/net/tun", O_RDWR);
  eth->rtx_fd = open ("/dev/net/tun", O_RDWR);
  if( eth->rtx_fd < 0 )
  if( eth->rtx_fd < 0 )
    {
    {
      fprintf (stderr, "Warning: Failed to open TUN/TAP device: %s\n",
      fprintf (stderr, "Warning: Failed to open TUN/TAP device: %s\n",
               strerror (errno));
               strerror (errno));
      eth->rtx_fd = -1;
      eth->rtx_fd = -1;
      return;
      return;
    }
    }
 
 
  /* Turn it into a specific TAP device. If we haven't specified a specific
  /* Turn it into a specific TAP device. If we haven't specified a specific
     (persistent) device, one will be created, but that requires superuser, or
     (persistent) device, one will be created, but that requires superuser, or
     at least CAP_NET_ADMIN capabilities. */
     at least CAP_NET_ADMIN capabilities. */
  memset (&ifr, 0, sizeof(ifr));
  memset (&ifr, 0, sizeof(ifr));
  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
  strncpy (ifr.ifr_name, eth->tap_dev, IFNAMSIZ);
  strncpy (ifr.ifr_name, eth->tap_dev, IFNAMSIZ);
 
 
  if (ioctl (eth->rtx_fd, TUNSETIFF, (void *) &ifr) < 0)
  if (ioctl (eth->rtx_fd, TUNSETIFF, (void *) &ifr) < 0)
    {
    {
      fprintf (stderr, "Warning: Failed to set TAP device %s: %s\n",
      fprintf (stderr, "Warning: Failed to set TAP device %s: %s\n",
               eth->tap_dev, strerror (errno));
               eth->tap_dev, strerror (errno));
      close (eth->rtx_fd);
      close (eth->rtx_fd);
      eth->rtx_fd = -1;
      eth->rtx_fd = -1;
      return;
      return;
    }
    }
#if ETH_DEBUG
#if ETH_DEBUG
      PRINTF ("Opened TAP %s\n", ifr.ifr_name);
      PRINTF ("Opened TAP %s\n", ifr.ifr_name);
#endif
#endif
 
 
}       /* eth_open_tap_if () */
}       /* eth_open_tap_if () */
 
 
#endif
#endif
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Open the external interface to the Ethernet
/*!Open the external interface to the Ethernet
 
 
   Calls the appropriate function for the interface type.
   Calls the appropriate function for the interface type.
 
 
   @param[in] eth  The Ethernet interface data structure.                     */
   @param[in] eth  The Ethernet interface data structure.                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_open_if (struct eth_device *eth)
eth_open_if (struct eth_device *eth)
{
{
  switch (eth->rtx_type)
  switch (eth->rtx_type)
    {
    {
    case ETH_RTX_FILE: eth_open_file_if (eth); break;
    case ETH_RTX_FILE: eth_open_file_if (eth); break;
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
    case ETH_RTX_TAP:  eth_open_tap_if (eth);  break;
    case ETH_RTX_TAP:  eth_open_tap_if (eth);  break;
#endif
#endif
 
 
    default:
    default:
      fprintf (stderr, "Unknown Ethernet interface: ignored.\n");
      fprintf (stderr, "Unknown Ethernet interface: ignored.\n");
      break;
      break;
    }
    }
}       /* eth_open_if () */
}       /* eth_open_if () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Reset the Ethernet.
/*!Reset the Ethernet.
 
 
   Open the correct type of simulation interface to the outside world.
   Open the correct type of simulation interface to the outside world.
 
 
   Initialize all registers to default and places devices in memory address
   Initialize all registers to default and places devices in memory address
   space.
   space.
 
 
   @param[in] dat  The Ethernet interface data structure.                     */
   @param[in] dat  The Ethernet interface data structure.                     */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_reset (void *dat)
eth_reset (void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  printf ("Resetting Ethernet\n");
  printf ("Resetting Ethernet\n");
#endif
#endif
 
 
  /* Nothing to do if we do not have a base address set, or we are not
  /* Nothing to do if we do not have a base address set, or we are not
     enabled. */
     enabled. */
  if (!eth->enabled || (0 == eth->baseaddr))
  if (!eth->enabled || (0 == eth->baseaddr))
    {
    {
      return;
      return;
    }
    }
 
 
  eth_open_if (eth);
  eth_open_if (eth);
 
 
  /* Set registers to default values */
  /* Set registers to default values */
  memset (&(eth->regs), 0, sizeof (eth->regs));
  memset (&(eth->regs), 0, sizeof (eth->regs));
 
 
  eth->regs.moder     = 0x0000A000;     /* Padding & CRC enabled */
  eth->regs.moder     = 0x0000A000;     /* Padding & CRC enabled */
  eth->regs.ipgt      = 0x00000012;     /* Half duplex (matches MODER) */
  eth->regs.ipgt      = 0x00000012;     /* Half duplex (matches MODER) */
  eth->regs.ipgr1     = 0x0000000C;     /* Recommended value */
  eth->regs.ipgr1     = 0x0000000C;     /* Recommended value */
  eth->regs.ipgr2     = 0x00000012;     /* Recommended value */
  eth->regs.ipgr2     = 0x00000012;     /* Recommended value */
  eth->regs.packetlen = 0x003C0600;     /* MINFL 60, MAXFL 1,536 bytes */
  eth->regs.packetlen = 0x003C0600;     /* MINFL 60, MAXFL 1,536 bytes */
  eth->regs.collconf  = 0x000F003F;     /* MAXRET 15, COLLVALID 63 */
  eth->regs.collconf  = 0x000F003F;     /* MAXRET 15, COLLVALID 63 */
  eth->regs.tx_bd_num = 0x00000040;     /* Max Tx BD */
  eth->regs.tx_bd_num = 0x00000040;     /* Max Tx BD */
  eth->regs.miimoder  = 0x00000064;     /* Send preamble, CLKDIV 100 */
  eth->regs.miimoder  = 0x00000064;     /* Send preamble, CLKDIV 100 */
 
 
  /* Reset TX/RX BD indexes. The Rx BD indexes start after the Tx BD indexes. */
  /* Reset TX/RX BD indexes. The Rx BD indexes start after the Tx BD indexes. */
  eth->tx_bd_index = 0;
  eth->tx_bd_index = 0;
  eth->rx_bd_index = eth->regs.tx_bd_num * 2;
  eth->rx_bd_index = eth->regs.tx_bd_num * 2;
 
 
  /* Reset IRQ line status */
  /* Reset IRQ line status */
  eth->int_line_stat = 0;
  eth->int_line_stat = 0;
 
 
  /* Initialize VAPI */
  /* Initialize VAPI */
  if (eth->base_vapi_id)
  if (eth->base_vapi_id)
    {
    {
      vapi_install_multi_handler (eth->base_vapi_id, ETH_NUM_VAPI_IDS,
      vapi_install_multi_handler (eth->base_vapi_id, ETH_NUM_VAPI_IDS,
                                  eth_vapi_read, dat);
                                  eth_vapi_read, dat);
    }
    }
}       /* eth_reset () */
}       /* eth_reset () */
 
 
 
 
#if ETH_DEBUG
#if ETH_DEBUG
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Map a register address to its name
/*!Map a register address to its name
 
 
   @param[in[ addr  The address of the register to name (offset from base).
   @param[in[ addr  The address of the register to name (offset from base).
 
 
   @return  The name of the register.                                         */
   @return  The name of the register.                                         */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static char *
static char *
eth_regname (oraddr_t  addr)
eth_regname (oraddr_t  addr)
{
{
  static char  bdstr[8];                /* For "BD[nnn]" */
  static char  bdstr[8];                /* For "BD[nnn]" */
 
 
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:      return  "MODER";
    case ETH_MODER:      return  "MODER";
    case ETH_INT_SOURCE: return  "INT_SOURCE";
    case ETH_INT_SOURCE: return  "INT_SOURCE";
    case ETH_INT_MASK:   return  "INT_MASK";
    case ETH_INT_MASK:   return  "INT_MASK";
    case ETH_IPGT:       return  "IPGT";
    case ETH_IPGT:       return  "IPGT";
    case ETH_IPGR1:      return  "IPGR1";
    case ETH_IPGR1:      return  "IPGR1";
    case ETH_IPGR2:      return  "IPGR2";
    case ETH_IPGR2:      return  "IPGR2";
    case ETH_PACKETLEN:  return  "PACKETLEN";
    case ETH_PACKETLEN:  return  "PACKETLEN";
    case ETH_COLLCONF:   return  "COLLCONF";
    case ETH_COLLCONF:   return  "COLLCONF";
    case ETH_TX_BD_NUM:  return  "TX_BD_NUM";
    case ETH_TX_BD_NUM:  return  "TX_BD_NUM";
    case ETH_CTRLMODER:  return  "CTRLMODER";
    case ETH_CTRLMODER:  return  "CTRLMODER";
    case ETH_MIIMODER:   return  "MIIMODER";
    case ETH_MIIMODER:   return  "MIIMODER";
    case ETH_MIICOMMAND: return  "MIICOMMAND";
    case ETH_MIICOMMAND: return  "MIICOMMAND";
    case ETH_MIIADDRESS: return  "MIIADDRESS";
    case ETH_MIIADDRESS: return  "MIIADDRESS";
    case ETH_MIITX_DATA: return  "MIITX_DATA";
    case ETH_MIITX_DATA: return  "MIITX_DATA";
    case ETH_MIIRX_DATA: return  "MIIRX_DATA";
    case ETH_MIIRX_DATA: return  "MIIRX_DATA";
    case ETH_MIISTATUS:  return  "MIISTATUS";
    case ETH_MIISTATUS:  return  "MIISTATUS";
    case ETH_MAC_ADDR0:  return  "MAC_ADDR0";
    case ETH_MAC_ADDR0:  return  "MAC_ADDR0";
    case ETH_MAC_ADDR1:  return  "MAC_ADDR1";
    case ETH_MAC_ADDR1:  return  "MAC_ADDR1";
    case ETH_HASH0:      return  "HASH0";
    case ETH_HASH0:      return  "HASH0";
    case ETH_HASH1:      return  "HASH1";
    case ETH_HASH1:      return  "HASH1";
 
 
    default:
    default:
      /* Buffer descriptors are a special case. */
      /* Buffer descriptors are a special case. */
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
        {
        {
          sprintf (bdstr, "BD[%.3d]", (addr - ETH_BD_BASE) / 4);
          sprintf (bdstr, "BD[%.3d]", (addr - ETH_BD_BASE) / 4);
          return  bdstr;
          return  bdstr;
        }
        }
      else
      else
        {
        {
          return  "INVALID";
          return  "INVALID";
        }
        }
    }
    }
}       /* eth_regname () */
}       /* eth_regname () */
#endif
#endif
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Read a register
/*!Read a register
 
 
   @param[in] addr  The address of the register to read (offset from base).
   @param[in] addr  The address of the register to read (offset from base).
   @param[in] dat   The Ethernet interface data structure, cast to a void
   @param[in] dat   The Ethernet interface data structure, cast to a void
                    pointer.
                    pointer.
 
 
   @return  The value read.                                                   */
   @return  The value read.                                                   */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static uint32_t
static uint32_t
eth_read32 (oraddr_t  addr,
eth_read32 (oraddr_t  addr,
            void     *dat)
            void     *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
  uint32_t           res;
  uint32_t           res;
 
 
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:      res = eth->regs.moder;        break;
    case ETH_MODER:      res = eth->regs.moder;        break;
    case ETH_INT_SOURCE: res = eth->regs.int_source;   break;
    case ETH_INT_SOURCE: res = eth->regs.int_source;   break;
    case ETH_INT_MASK:   res = eth->regs.int_mask;     break;
    case ETH_INT_MASK:   res = eth->regs.int_mask;     break;
    case ETH_IPGT:       res = eth->regs.ipgt;         break;
    case ETH_IPGT:       res = eth->regs.ipgt;         break;
    case ETH_IPGR1:      res = eth->regs.ipgr1;        break;
    case ETH_IPGR1:      res = eth->regs.ipgr1;        break;
    case ETH_IPGR2:      res = eth->regs.ipgr2;        break;
    case ETH_IPGR2:      res = eth->regs.ipgr2;        break;
    case ETH_PACKETLEN:  res = eth->regs.packetlen;    break;
    case ETH_PACKETLEN:  res = eth->regs.packetlen;    break;
    case ETH_COLLCONF:   res = eth->regs.collconf;     break;
    case ETH_COLLCONF:   res = eth->regs.collconf;     break;
    case ETH_TX_BD_NUM:  res = eth->regs.tx_bd_num;    break;
    case ETH_TX_BD_NUM:  res = eth->regs.tx_bd_num;    break;
    case ETH_CTRLMODER:  res = eth->regs.controlmoder; break;
    case ETH_CTRLMODER:  res = eth->regs.controlmoder; break;
    case ETH_MIIMODER:   res = eth->regs.miimoder;     break;
    case ETH_MIIMODER:   res = eth->regs.miimoder;     break;
    case ETH_MIICOMMAND: res = eth->regs.miicommand;   break;
    case ETH_MIICOMMAND: res = eth->regs.miicommand;   break;
    case ETH_MIIADDRESS: res = eth->regs.miiaddress;   break;
    case ETH_MIIADDRESS: res = eth->regs.miiaddress;   break;
    case ETH_MIITX_DATA: res = eth->regs.miitx_data;   break;
    case ETH_MIITX_DATA: res = eth->regs.miitx_data;   break;
    case ETH_MIIRX_DATA: res = eth->regs.miirx_data;   break;
    case ETH_MIIRX_DATA: res = eth->regs.miirx_data;   break;
    case ETH_MIISTATUS:  res = eth->regs.miistatus;    break;
    case ETH_MIISTATUS:  res = eth->regs.miistatus;    break;
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR0:
      res =
      res =
        (((unsigned long) eth->mac_address[2]) << 24) |
        (((unsigned long) eth->mac_address[2]) << 24) |
        (((unsigned long) eth->mac_address[3]) << 16) |
        (((unsigned long) eth->mac_address[3]) << 16) |
        (((unsigned long) eth->mac_address[4]) <<  8) |
        (((unsigned long) eth->mac_address[4]) <<  8) |
          (unsigned long) eth->mac_address[5];
          (unsigned long) eth->mac_address[5];
      break;
      break;
    case ETH_MAC_ADDR1:
    case ETH_MAC_ADDR1:
      res =
      res =
        (((unsigned long) eth->mac_address[0]) <<  8) |
        (((unsigned long) eth->mac_address[0]) <<  8) |
          (unsigned long) eth->mac_address[1];
          (unsigned long) eth->mac_address[1];
      break;
      break;
    case ETH_HASH0:      res = eth->regs.hash0;        break;
    case ETH_HASH0:      res = eth->regs.hash0;        break;
    case ETH_HASH1:      res = eth->regs.hash1;        break;
    case ETH_HASH1:      res = eth->regs.hash1;        break;
 
 
    default:
    default:
      /* Buffer descriptors are a special case. */
      /* Buffer descriptors are a special case. */
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
        {
        {
          res = eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4];
          res = eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4];
          break;
          break;
        }
        }
      else
      else
        {
        {
          fprintf (stderr,
          fprintf (stderr,
                   "Warning: eth_read32( 0x%" PRIxADDR " ): Illegal address\n",
                   "Warning: eth_read32( 0x%" PRIxADDR " ): Illegal address\n",
                   addr + eth->baseaddr);
                   addr + eth->baseaddr);
          res = 0;
          res = 0;
        }
        }
    }
    }
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  /* Only trace registers of particular interest */
  /* Only trace registers of particular interest */
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:
    case ETH_MODER:
    case ETH_INT_SOURCE:
    case ETH_INT_SOURCE:
    case ETH_INT_MASK:
    case ETH_INT_MASK:
    case ETH_IPGT:
    case ETH_IPGT:
    case ETH_IPGR1:
    case ETH_IPGR1:
    case ETH_IPGR2:
    case ETH_IPGR2:
    case ETH_PACKETLEN:
    case ETH_PACKETLEN:
    case ETH_COLLCONF:
    case ETH_COLLCONF:
    case ETH_TX_BD_NUM:
    case ETH_TX_BD_NUM:
    case ETH_CTRLMODER:
    case ETH_CTRLMODER:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR1:
    case ETH_MAC_ADDR1:
      printf ("eth_read32: %s = 0x%08lx\n", eth_regname (addr),
      printf ("eth_read32: %s = 0x%08lx\n", eth_regname (addr),
              (unsigned long int) res);
              (unsigned long int) res);
    }
    }
#endif
#endif
 
 
  return  res;
  return  res;
 
 
}       /* eth_read32 () */
}       /* eth_read32 () */
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Emulate MIIM transaction to ethernet PHY
/*!Emulate MIIM transaction to ethernet PHY
 
 
   @param[in] eth  Ethernet device datastruture.                              */
   @param[in] eth  Ethernet device datastruture.                              */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_miim_trans (struct eth_device *eth)
eth_miim_trans (struct eth_device *eth)
{
{
  switch (eth->regs.miicommand)
  switch (eth->regs.miicommand)
    {
    {
    case ((1 << ETH_MIICOMM_WCDATA_OFFSET)):
    case ((1 << ETH_MIICOMM_WCDATA_OFFSET)):
      /* Perhaps something to emulate here later, but for now do nothing */
      /* Perhaps something to emulate here later, but for now do nothing */
      break;
      break;
 
 
    case ((1 << ETH_MIICOMM_RSTAT_OFFSET)):
    case ((1 << ETH_MIICOMM_RSTAT_OFFSET)):
      /*First check if it's the correct PHY to address */
      /*First check if it's the correct PHY to address */
      if (((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
      if (((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
           ETH_MIIADDR_FIAD_MASK) == eth->phy_addr)
           ETH_MIIADDR_FIAD_MASK) == eth->phy_addr)
        {
        {
          /* Correct PHY - now switch based on the register address in the PHY*/
          /* Correct PHY - now switch based on the register address in the PHY*/
          switch ((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
          switch ((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
                  ETH_MIIADDR_RGAD_MASK)
                  ETH_MIIADDR_RGAD_MASK)
            {
            {
            case MII_BMCR:
            case MII_BMCR:
              eth->regs.miirx_data = BMCR_FULLDPLX;
              eth->regs.miirx_data = BMCR_FULLDPLX;
              break;
              break;
            case MII_BMSR:
            case MII_BMSR:
              eth->regs.miirx_data = BMSR_LSTATUS | BMSR_ANEGCOMPLETE |
              eth->regs.miirx_data = BMSR_LSTATUS | BMSR_ANEGCOMPLETE |
                BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL;
                BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL;
              break;
              break;
            case MII_PHYSID1:
            case MII_PHYSID1:
              eth->regs.miirx_data = 0x22; /* Micrel PHYID */
              eth->regs.miirx_data = 0x22; /* Micrel PHYID */
              break;
              break;
            case MII_PHYSID2:
            case MII_PHYSID2:
              eth->regs.miirx_data = 0x1613; /* Micrel PHYID */
              eth->regs.miirx_data = 0x1613; /* Micrel PHYID */
              break;
              break;
            case MII_ADVERTISE:
            case MII_ADVERTISE:
              eth->regs.miirx_data = ADVERTISE_FULL;
              eth->regs.miirx_data = ADVERTISE_FULL;
              break;
              break;
            case MII_LPA:
            case MII_LPA:
              eth->regs.miirx_data = LPA_DUPLEX | LPA_100;
              eth->regs.miirx_data = LPA_DUPLEX | LPA_100;
              break;
              break;
            case MII_EXPANSION:
            case MII_EXPANSION:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_CTRL1000:
            case MII_CTRL1000:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_STAT1000:
            case MII_STAT1000:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_ESTATUS:
            case MII_ESTATUS:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_DCOUNTER:
            case MII_DCOUNTER:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_FCSCOUNTER:
            case MII_FCSCOUNTER:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_NWAYTEST:
            case MII_NWAYTEST:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_RERRCOUNTER:
            case MII_RERRCOUNTER:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_SREVISION:
            case MII_SREVISION:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_RESV1:
            case MII_RESV1:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_LBRERROR:
            case MII_LBRERROR:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_PHYADDR:
            case MII_PHYADDR:
              eth->regs.miirx_data = eth->phy_addr;
              eth->regs.miirx_data = eth->phy_addr;
              break;
              break;
            case MII_RESV2:
            case MII_RESV2:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_TPISTATUS:
            case MII_TPISTATUS:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            case MII_NCONFIG:
            case MII_NCONFIG:
              eth->regs.miirx_data = 0;
              eth->regs.miirx_data = 0;
              break;
              break;
            default:
            default:
              eth->regs.miirx_data = 0xffff;
              eth->regs.miirx_data = 0xffff;
              break;
              break;
            }
            }
        }
        }
      else
      else
        {
        {
          eth->regs.miirx_data = 0xffff; /* PHY doesn't exist, read all 1's */
          eth->regs.miirx_data = 0xffff; /* PHY doesn't exist, read all 1's */
        }
        }
 
 
      break;
      break;
 
 
    case ((1 << ETH_MIICOMM_SCANS_OFFSET)):
    case ((1 << ETH_MIICOMM_SCANS_OFFSET)):
      /* From MAC's datasheet:
      /* From MAC's datasheet:
         A host initiates the Scan Status Operation by asserting the SCANSTAT
         A host initiates the Scan Status Operation by asserting the SCANSTAT
         signal. The MIIM performs a continuous read operation of the PHY
         signal. The MIIM performs a continuous read operation of the PHY
         Status register. The PHY is selected by the FIAD[4:0] signals. The
         Status register. The PHY is selected by the FIAD[4:0] signals. The
         link status LinkFail signal is asserted/deasserted by the MIIM module
         link status LinkFail signal is asserted/deasserted by the MIIM module
         and reflects the link status bit of the PHY Status register. The
         and reflects the link status bit of the PHY Status register. The
         signal NVALID is used for qualifying the validity of the LinkFail
         signal NVALID is used for qualifying the validity of the LinkFail
         signals and the status data PRSD[15:0]. These signals are invalid
         signals and the status data PRSD[15:0]. These signals are invalid
         until the first scan status operation ends. During the scan status
         until the first scan status operation ends. During the scan status
         operation, the BUSY signal is asserted until the last read is
         operation, the BUSY signal is asserted until the last read is
         performed (the scan status operation is stopped).
         performed (the scan status operation is stopped).
 
 
         So for now - do nothing, leave link status indicator as permanently
         So for now - do nothing, leave link status indicator as permanently
         with link.
         with link.
      */
      */
 
 
      break;
      break;
 
 
    default:
    default:
      break;
      break;
    }
    }
}       /* eth_miim_trans () */
}       /* eth_miim_trans () */
 
 
 
 
 
 
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*!Write a register
/*!Write a register
 
 
   @note Earlier versions of this code treated ETH_INT_SOURCE as an "interrupt
   @note Earlier versions of this code treated ETH_INT_SOURCE as an "interrupt
         pending" register and reissued interrupts if ETH_INT_MASK was
         pending" register and reissued interrupts if ETH_INT_MASK was
         changed, enabling an interrupt that had previously been cleared. This
         changed, enabling an interrupt that had previously been cleared. This
         led to spurious double interrupt. In the present version, the only
         led to spurious double interrupt. In the present version, the only
         way an interrupt can be generated is at the time ETH_INT_SOURCE is
         way an interrupt can be generated is at the time ETH_INT_SOURCE is
         set in the Tx/Rx controllers and the only way an interrupt can be
         set in the Tx/Rx controllers and the only way an interrupt can be
         cleared is by writing to ETH_INT_SOURCE.
         cleared is by writing to ETH_INT_SOURCE.
 
 
   @param[in] addr   The address of the register to read (offset from base).
   @param[in] addr   The address of the register to read (offset from base).
   @param[in] value  The value to write.
   @param[in] value  The value to write.
   @param[in] dat    The Ethernet interface data structure, cast to a void
   @param[in] dat    The Ethernet interface data structure, cast to a void
                     pointer.                                                 */
                     pointer.                                                 */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void
static void
eth_write32 (oraddr_t addr, uint32_t value, void *dat)
eth_write32 (oraddr_t addr, uint32_t value, void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  /* Only trace registers of particular interest */
  /* Only trace registers of particular interest */
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:
    case ETH_MODER:
    case ETH_INT_SOURCE:
    case ETH_INT_SOURCE:
    case ETH_INT_MASK:
    case ETH_INT_MASK:
    case ETH_IPGT:
    case ETH_IPGT:
    case ETH_IPGR1:
    case ETH_IPGR1:
    case ETH_IPGR2:
    case ETH_IPGR2:
    case ETH_PACKETLEN:
    case ETH_PACKETLEN:
    case ETH_COLLCONF:
    case ETH_COLLCONF:
    case ETH_TX_BD_NUM:
    case ETH_TX_BD_NUM:
    case ETH_CTRLMODER:
    case ETH_CTRLMODER:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR1:
    case ETH_MAC_ADDR1:
      printf ("eth_write32: 0x%08lx to %s ", (unsigned long int) value,
      printf ("eth_write32: 0x%08lx to %s ", (unsigned long int) value,
              eth_regname (addr));
              eth_regname (addr));
    }
    }
 
 
  /* Detail register transitions on MODER, INT_SOURCE AND INT_MASK */
  /* Detail register transitions on MODER, INT_SOURCE AND INT_MASK */
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:
    case ETH_MODER:
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.moder);
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.moder);
      break;
      break;
    case ETH_INT_SOURCE:
    case ETH_INT_SOURCE:
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.int_source);
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.int_source);
      break;
      break;
    case ETH_INT_MASK:
    case ETH_INT_MASK:
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.int_mask);
      printf (" 0x%08lx -> ", (unsigned long) eth->regs.int_mask);
      break;
      break;
    }
    }
#endif
#endif
 
 
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:
    case ETH_MODER:
      if (!TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN) &&
      if (!TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN) &&
          TEST_FLAG (value, ETH_MODER, RXEN))
          TEST_FLAG (value, ETH_MODER, RXEN))
        {
        {
          /* Enabling receive, flush any oustanding input (TAP only), reset
          /* Enabling receive, flush any oustanding input (TAP only), reset
             the BDs and schedule the Rx controller on the next clock
             the BDs and schedule the Rx controller on the next clock
             cycle. */
             cycle. */
          if (ETH_RTX_TAP == eth->rtx_type)
          if (ETH_RTX_TAP == eth->rtx_type)
            {
            {
              (void) eth_ignore_tap_packets (eth);
              (void) eth_ignore_tap_packets (eth);
            }
            }
 
 
          eth->rx_bd_index = eth->regs.tx_bd_num * 2;
          eth->rx_bd_index = eth->regs.tx_bd_num * 2;
          SCHED_ADD (eth_controller_rx_clock, dat, 1);
          SCHED_ADD (eth_controller_rx_clock, dat, 1);
        }
        }
      else if (!TEST_FLAG (value, ETH_MODER, RXEN) &&
      else if (!TEST_FLAG (value, ETH_MODER, RXEN) &&
               TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN))
               TEST_FLAG (eth->regs.moder, ETH_MODER, RXEN))
        {
        {
          /* Disabling Rx, so stop scheduling the Rx controller. */
          /* Disabling Rx, so stop scheduling the Rx controller. */
          SCHED_FIND_REMOVE (eth_controller_rx_clock, dat);
          SCHED_FIND_REMOVE (eth_controller_rx_clock, dat);
        }
        }
 
 
      if (!TEST_FLAG (eth->regs.moder, ETH_MODER, TXEN) &&
      if (!TEST_FLAG (eth->regs.moder, ETH_MODER, TXEN) &&
          TEST_FLAG (value, ETH_MODER, TXEN))
          TEST_FLAG (value, ETH_MODER, TXEN))
        {
        {
          /* Enabling transmit, reset the BD and schedule the Tx controller on
          /* Enabling transmit, reset the BD and schedule the Tx controller on
             the next clock cycle. */
             the next clock cycle. */
          eth->tx_bd_index = 0;
          eth->tx_bd_index = 0;
          SCHED_ADD (eth_controller_tx_clock, dat, 1);
          SCHED_ADD (eth_controller_tx_clock, dat, 1);
        }
        }
      else if (!TEST_FLAG (value, ETH_MODER, TXEN) &&
      else if (!TEST_FLAG (value, ETH_MODER, TXEN) &&
               TEST_FLAG (eth->regs.moder, ETH_MODER, TXEN))
               TEST_FLAG (eth->regs.moder, ETH_MODER, TXEN))
        {
        {
          /* Disabling Tx, so stop scheduling the Tx controller. */
          /* Disabling Tx, so stop scheduling the Tx controller. */
          SCHED_FIND_REMOVE (eth_controller_tx_clock, dat);
          SCHED_FIND_REMOVE (eth_controller_tx_clock, dat);
        }
        }
 
 
      /* Reset the interface if so requested. */
      /* Reset the interface if so requested. */
      if (TEST_FLAG (value, ETH_MODER, RST))
      if (TEST_FLAG (value, ETH_MODER, RST))
        {
        {
          eth_reset (dat);
          eth_reset (dat);
        }
        }
 
 
      eth->regs.moder = value;          /* Update the register */
      eth->regs.moder = value;          /* Update the register */
      break;
      break;
 
 
    case ETH_INT_SOURCE:
    case ETH_INT_SOURCE:
      eth->regs.int_source &= ~value;
      eth->regs.int_source &= ~value;
 
 
      /* Clear IRQ line if all interrupt sources have been dealt with
      /* Clear IRQ line if all interrupt sources have been dealt with
 
 
         @todo  Is this really right? */
         @todo  Is this really right? */
      if (!(eth->regs.int_source & eth->regs.int_mask) && eth->int_line_stat)
      if (!(eth->regs.int_source & eth->regs.int_mask) && eth->int_line_stat)
        {
        {
          clear_interrupt (eth->mac_int);
          clear_interrupt (eth->mac_int);
          eth->int_line_stat = 0;
          eth->int_line_stat = 0;
        }
        }
      break;
      break;
 
 
    case ETH_INT_MASK:
    case ETH_INT_MASK:
      eth->regs.int_mask = value;
      eth->regs.int_mask = value;
 
 
      /* The old code would report an interrupt if we enabled an interrupt
      /* The old code would report an interrupt if we enabled an interrupt
         when if we enabled interrupts and the core was not currently
         when if we enabled interrupts and the core was not currently
         processing an interrupt, and there was an interrupt pending.
         processing an interrupt, and there was an interrupt pending.
 
 
         However this led (at least on some machines) to orphaned interrupts
         However this led (at least on some machines) to orphaned interrupts
         in the device driver. So in this version of the code we do not report
         in the device driver. So in this version of the code we do not report
         interrupts on a mask change.
         interrupts on a mask change.
 
 
         This is not apparently consistent with the Verilog, but it does mean
         This is not apparently consistent with the Verilog, but it does mean
         that the orphaned interrupts problem does not occur, and has no
         that the orphaned interrupts problem does not occur, and has no
         apparent effect on Ethernet performance. More investigation is needed
         apparent effect on Ethernet performance. More investigation is needed
         to determine if this is a bug in Or1ksim interrupt handling, or a bug
         to determine if this is a bug in Or1ksim interrupt handling, or a bug
         in the device driver, which does not manifest with real HW.
         in the device driver, which does not manifest with real HW.
 
 
         Otherwise clear down the interrupt.
         Otherwise clear down the interrupt.
 
 
         @todo  Is this really right. */
         @todo  Is this really right. */
      if ((eth->regs.int_source & eth->regs.int_mask) && !eth->int_line_stat)
      if ((eth->regs.int_source & eth->regs.int_mask) && !eth->int_line_stat)
        {
        {
#if ETH_DEBUG
#if ETH_DEBUG
          printf ("ETH_MASK changed with apparent pending interrupt.\n");
          printf ("ETH_MASK changed with apparent pending interrupt.\n");
#endif
#endif
        }
        }
      else if (eth->int_line_stat)
      else if (eth->int_line_stat)
        {
        {
          clear_interrupt (eth->mac_int);
          clear_interrupt (eth->mac_int);
          eth->int_line_stat = 0;
          eth->int_line_stat = 0;
        }
        }
 
 
      break;
      break;
 
 
    case ETH_IPGT:       eth->regs.ipgt         = value; break;
    case ETH_IPGT:       eth->regs.ipgt         = value; break;
    case ETH_IPGR1:      eth->regs.ipgr1        = value; break;
    case ETH_IPGR1:      eth->regs.ipgr1        = value; break;
    case ETH_IPGR2:      eth->regs.ipgr2        = value; break;
    case ETH_IPGR2:      eth->regs.ipgr2        = value; break;
    case ETH_PACKETLEN:  eth->regs.packetlen    = value; break;
    case ETH_PACKETLEN:  eth->regs.packetlen    = value; break;
    case ETH_COLLCONF:   eth->regs.collconf     = value; break;
    case ETH_COLLCONF:   eth->regs.collconf     = value; break;
 
 
    case ETH_TX_BD_NUM:
    case ETH_TX_BD_NUM:
      /* When TX_BD_NUM is written, also reset current RX BD index */
      /* When TX_BD_NUM is written, also reset current RX BD index */
      eth->regs.tx_bd_num = value & 0xFF;
      eth->regs.tx_bd_num = value & 0xFF;
      eth->rx_bd_index = eth->regs.tx_bd_num * 2;
      eth->rx_bd_index = eth->regs.tx_bd_num * 2;
      break;
      break;
 
 
    case ETH_CTRLMODER:  eth->regs.controlmoder = value; break;
    case ETH_CTRLMODER:  eth->regs.controlmoder = value; break;
    case ETH_MIIMODER:   eth->regs.miimoder     = value; break;
    case ETH_MIIMODER:   eth->regs.miimoder     = value; break;
 
 
    case ETH_MIICOMMAND:
    case ETH_MIICOMMAND:
      eth->regs.miicommand = value;
      eth->regs.miicommand = value;
      /* Perform MIIM transaction, if required */
      /* Perform MIIM transaction, if required */
      eth_miim_trans (eth);
      eth_miim_trans (eth);
      break;
      break;
 
 
    case ETH_MIIADDRESS: eth->regs.miiaddress   = value; break;
    case ETH_MIIADDRESS: eth->regs.miiaddress   = value; break;
    case ETH_MIITX_DATA: eth->regs.miitx_data   = value; break;
    case ETH_MIITX_DATA: eth->regs.miitx_data   = value; break;
    case ETH_MIIRX_DATA: /* Register is R/O */           break;
    case ETH_MIIRX_DATA: /* Register is R/O */           break;
    case ETH_MIISTATUS:  /* Register is R/O */           break;
    case ETH_MIISTATUS:  /* Register is R/O */           break;
 
 
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR0:
      eth->mac_address[5] =  value        & 0xFF;
      eth->mac_address[5] =  value        & 0xFF;
      eth->mac_address[4] = (value >>  8) & 0xFF;
      eth->mac_address[4] = (value >>  8) & 0xFF;
      eth->mac_address[3] = (value >> 16) & 0xFF;
      eth->mac_address[3] = (value >> 16) & 0xFF;
      eth->mac_address[2] = (value >> 24) & 0xFF;
      eth->mac_address[2] = (value >> 24) & 0xFF;
      break;
      break;
 
 
    case ETH_MAC_ADDR1:
    case ETH_MAC_ADDR1:
      eth->mac_address[1] =  value        & 0xFF;
      eth->mac_address[1] =  value        & 0xFF;
      eth->mac_address[0] = (value >>  8) & 0xFF;
      eth->mac_address[0] = (value >>  8) & 0xFF;
      break;
      break;
 
 
    case ETH_HASH0:      eth->regs.hash0 = value;        break;
    case ETH_HASH0:      eth->regs.hash0 = value;        break;
    case ETH_HASH1:      eth->regs.hash1 = value;        break;
    case ETH_HASH1:      eth->regs.hash1 = value;        break;
 
 
    default:
    default:
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
      if ((addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE))
        {
        {
          eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4] = value;
          eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4] = value;
        }
        }
      else
      else
        {
        {
          fprintf (stderr,
          fprintf (stderr,
                   "Warning: eth_write32( 0x%" PRIxADDR " ): Illegal address\n",
                   "Warning: eth_write32( 0x%" PRIxADDR " ): Illegal address\n",
                   addr + eth->baseaddr);
                   addr + eth->baseaddr);
        }
        }
      break;
      break;
    }
    }
 
 
#if ETH_DEBUG
#if ETH_DEBUG
  switch (addr)
  switch (addr)
    {
    {
    case ETH_MODER:
    case ETH_MODER:
      printf ("0x%08lx\n", (unsigned long) eth->regs.moder);
      printf ("0x%08lx\n", (unsigned long) eth->regs.moder);
      break;
      break;
    case ETH_INT_SOURCE:
    case ETH_INT_SOURCE:
      printf ("0x%08lx\n", (unsigned long) eth->regs.int_source);
      printf ("0x%08lx\n", (unsigned long) eth->regs.int_source);
      break;
      break;
    case ETH_INT_MASK:
    case ETH_INT_MASK:
      printf ("0x%08lx\n", (unsigned long) eth->regs.int_mask);
      printf ("0x%08lx\n", (unsigned long) eth->regs.int_mask);
      break;
      break;
    case ETH_IPGT:
    case ETH_IPGT:
    case ETH_IPGR1:
    case ETH_IPGR1:
    case ETH_IPGR2:
    case ETH_IPGR2:
    case ETH_PACKETLEN:
    case ETH_PACKETLEN:
    case ETH_COLLCONF:
    case ETH_COLLCONF:
    case ETH_TX_BD_NUM:
    case ETH_TX_BD_NUM:
    case ETH_CTRLMODER:
    case ETH_CTRLMODER:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR0:
    case ETH_MAC_ADDR1:
    case ETH_MAC_ADDR1:
      printf("\n");
      printf("\n");
      break;
      break;
    }
    }
 
 
#endif
#endif
 
 
}       /* eth_write32 () */
}       /* eth_write32 () */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Enable or disable the Ethernet interface
/*!Enable or disable the Ethernet interface
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_enabled (union param_val  val,
eth_enabled (union param_val  val,
             void            *dat)
             void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  eth->enabled = val.int_val;
  eth->enabled = val.int_val;
 
 
}       /* eth_enabled() */
}       /* eth_enabled() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet interface base address
/*!Set the Ethernet interface base address
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_baseaddr (union param_val  val,
eth_baseaddr (union param_val  val,
              void            *dat)
              void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  eth->baseaddr = val.addr_val;
  eth->baseaddr = val.addr_val;
 
 
}       /* eth_baseaddr() */
}       /* eth_baseaddr() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet DMA port
/*!Set the Ethernet DMA port
 
 
   This is not currently supported, so a warning message is printed.
   This is not currently supported, so a warning message is printed.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_dma (union param_val  val,
eth_dma (union param_val  val,
         void            *dat)
         void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported\n");
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported\n");
  eth->dma = val.addr_val;
  eth->dma = val.addr_val;
 
 
}       /* eth_dma() */
}       /* eth_dma() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet IRQ
/*!Set the Ethernet IRQ
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_irq (union param_val  val,
eth_irq (union param_val  val,
         void            *dat)
         void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  eth->mac_int = val.int_val;
  eth->mac_int = val.int_val;
 
 
}       /* eth_irq() */
}       /* eth_irq() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet interface type
/*!Set the Ethernet interface type
 
 
   Currently two types are supported, file and tap.
   Currently two types are supported, file and tap.
 
 
   @param[in] val  The value to use. Currently "file" and "tap" are supported.
   @param[in] val  The value to use. Currently "file" and "tap" are supported.
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_rtx_type (union param_val  val,
eth_rtx_type (union param_val  val,
              void            *dat)
              void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  if (0 == strcasecmp ("file", val.str_val))
  if (0 == strcasecmp ("file", val.str_val))
    {
    {
      printf ("Ethernet FILE type\n");
      printf ("Ethernet FILE type\n");
      eth->rtx_type = ETH_RTX_FILE;
      eth->rtx_type = ETH_RTX_FILE;
    }
    }
#if HAVE_LINUX_IF_TUN_H==1
#if HAVE_LINUX_IF_TUN_H==1
  else if (0 == strcasecmp ("tap", val.str_val))
  else if (0 == strcasecmp ("tap", val.str_val))
    {
    {
      printf ("Ethernet TAP type\n");
      printf ("Ethernet TAP type\n");
      eth->rtx_type = ETH_RTX_TAP;
      eth->rtx_type = ETH_RTX_TAP;
    }
    }
#endif
#endif
  else
  else
    {
    {
      fprintf (stderr, "Warning: Unknown Ethernet type: file assumed.\n");
      fprintf (stderr, "Warning: Unknown Ethernet type: file assumed.\n");
      eth->rtx_type = ETH_RTX_FILE;
      eth->rtx_type = ETH_RTX_FILE;
    }
    }
}       /* eth_rtx_type() */
}       /* eth_rtx_type() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet DMA Rx channel
/*!Set the Ethernet DMA Rx channel
 
 
   External DMA is not currently supported, so a warning message is printed.
   External DMA is not currently supported, so a warning message is printed.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_rx_channel (union param_val  val,
eth_rx_channel (union param_val  val,
                void            *dat)
                void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported: "
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported: "
           "Rx channel ignored\n");
           "Rx channel ignored\n");
  eth->rx_channel = val.int_val;
  eth->rx_channel = val.int_val;
 
 
}       /* eth_rx_channel() */
}       /* eth_rx_channel() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet DMA Tx channel
/*!Set the Ethernet DMA Tx channel
 
 
   External DMA is not currently supported, so a warning message is printed.
   External DMA is not currently supported, so a warning message is printed.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_tx_channel (union param_val  val,
eth_tx_channel (union param_val  val,
                void            *dat)
                void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported: "
  fprintf (stderr, "Warning: External Ethernet DMA not currently supported: "
           "Tx channel ignored\n");
           "Tx channel ignored\n");
  eth->tx_channel = val.int_val;
  eth->tx_channel = val.int_val;
 
 
}       /* eth_tx_channel() */
}       /* eth_tx_channel() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet DMA Rx file
/*!Set the Ethernet DMA Rx file
 
 
   Free any previously allocated value.
   Free any previously allocated value.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_rxfile (union param_val  val,
eth_rxfile (union param_val  val,
            void            *dat)
            void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  if (NULL != eth->rxfile)
  if (NULL != eth->rxfile)
    {
    {
      free (eth->rxfile);
      free (eth->rxfile);
      eth->rxfile = NULL;
      eth->rxfile = NULL;
    }
    }
 
 
  if (!(eth->rxfile = strdup (val.str_val)))
  if (!(eth->rxfile = strdup (val.str_val)))
    {
    {
      fprintf (stderr, "Peripheral Ethernet: Run out of memory\n");
      fprintf (stderr, "Peripheral Ethernet: Run out of memory\n");
      exit (-1);
      exit (-1);
    }
    }
}       /* eth_rxfile() */
}       /* eth_rxfile() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet DMA Tx file
/*!Set the Ethernet DMA Tx file
 
 
   Free any previously allocated value.
   Free any previously allocated value.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_txfile (union param_val  val,
eth_txfile (union param_val  val,
            void            *dat)
            void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  if (NULL != eth->txfile)
  if (NULL != eth->txfile)
    {
    {
      free (eth->txfile);
      free (eth->txfile);
      eth->txfile = NULL;
      eth->txfile = NULL;
    }
    }
 
 
  if (!(eth->txfile = strdup (val.str_val)))
  if (!(eth->txfile = strdup (val.str_val)))
    {
    {
      fprintf (stderr, "Peripheral Ethernet: Run out of memory\n");
      fprintf (stderr, "Peripheral Ethernet: Run out of memory\n");
      exit (-1);
      exit (-1);
    }
    }
}       /* eth_txfile() */
}       /* eth_txfile() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the Ethernet TAP device.
/*!Set the Ethernet TAP device.
 
 
   If we are not superuser (or do not have CAP_NET_ADMIN priviledges), then we
   If we are not superuser (or do not have CAP_NET_ADMIN priviledges), then we
   must work with a persistent TAP device that is already set up. This option
   must work with a persistent TAP device that is already set up. This option
   specifies the device to user.
   specifies the device to user.
 
 
   @param[in] val  The value to use.
   @param[in] val  The value to use.
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_tap_dev (union param_val  val,
eth_tap_dev (union param_val  val,
              void            *dat)
              void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  if (NULL != eth->tap_dev)
  if (NULL != eth->tap_dev)
    {
    {
      free (eth->tap_dev);
      free (eth->tap_dev);
      eth->tap_dev = NULL;
      eth->tap_dev = NULL;
    }
    }
 
 
  eth->tap_dev = strdup (val.str_val);
  eth->tap_dev = strdup (val.str_val);
 
 
  if (NULL == eth->tap_dev)
  if (NULL == eth->tap_dev)
    {
    {
      fprintf (stderr, "ERROR: Peripheral Ethernet: Run out of memory\n");
      fprintf (stderr, "ERROR: Peripheral Ethernet: Run out of memory\n");
      exit (-1);
      exit (-1);
    }
    }
}       /* eth_tap_dev() */
}       /* eth_tap_dev() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the PHY address
/*!Set the PHY address
 
 
   Used to identify different physical interfaces (important for MII).
   Used to identify different physical interfaces (important for MII).
 
 
   @param[in] val  The value to use.
   @param[in] val  The value to use.
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_phy_addr (union param_val  val,
eth_phy_addr (union param_val  val,
              void            *dat)
              void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
  eth->phy_addr = val.int_val & ETH_MIIADDR_FIAD_MASK;
  eth->phy_addr = val.int_val & ETH_MIIADDR_FIAD_MASK;
 
 
}       /* eth_phy_addr () */
}       /* eth_phy_addr () */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Enable or disable a dummy CRC
/*!Enable or disable a dummy CRC
 
 
   The MAC should not report anything about the CRC back to the core. However
   The MAC should not report anything about the CRC back to the core. However
   the hardware implementation of the OpenRISC MAC does, and the Linux drivers
   the hardware implementation of the OpenRISC MAC does, and the Linux drivers
   have been written to expect the value (which they ignore).
   have been written to expect the value (which they ignore).
 
 
   Setting this parameter causes a dummy CRC to be added. For consistency with
   Setting this parameter causes a dummy CRC to be added. For consistency with
   the hardware, its default setting is TRUE.
   the hardware, its default setting is TRUE.
 
 
   @param[in] val  The value to use
   @param[in] val  The value to use
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_dummy_crc (union param_val  val,
eth_dummy_crc (union param_val  val,
             void            *dat)
             void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
 
 
  eth->dummy_crc = val.int_val;
  eth->dummy_crc = val.int_val;
 
 
}       /* eth_dummy_crc() */
}       /* eth_dummy_crc() */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Set the VAPI id
/*!Set the VAPI id
 
 
   Used for remote testing of the interface.
   Used for remote testing of the interface.
 
 
   @param[in] val  The value to use.
   @param[in] val  The value to use.
   @param[in] dat  The config data structure                                 */
   @param[in] dat  The config data structure                                 */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_vapi_id (union param_val  val,
eth_vapi_id (union param_val  val,
             void            *dat)
             void            *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
  eth->base_vapi_id = val.int_val;
  eth->base_vapi_id = val.int_val;
 
 
}       /* eth_vapi_id () */
}       /* eth_vapi_id () */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Start the initialization of a new Ethernet configuration
/*!Start the initialization of a new Ethernet configuration
 
 
   ALL parameters are set explicitly to default values.                      */
   ALL parameters are set explicitly to default values.                      */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void *
static void *
eth_sec_start (void)
eth_sec_start (void)
{
{
  struct eth_device *new = malloc (sizeof (struct eth_device));
  struct eth_device *new = malloc (sizeof (struct eth_device));
 
 
  if (!new)
  if (!new)
    {
    {
      fprintf (stderr, "Peripheral Eth: Run out of memory\n");
      fprintf (stderr, "Peripheral Eth: Run out of memory\n");
      exit (-1);
      exit (-1);
    }
    }
 
 
  memset (new, 0, sizeof (struct eth_device));
  memset (new, 0, sizeof (struct eth_device));
 
 
  new->enabled      = 1;
  new->enabled      = 1;
  new->baseaddr     = 0;
  new->baseaddr     = 0;
  new->dma          = 0;
  new->dma          = 0;
  new->mac_int      = 0;
  new->mac_int      = 0;
  new->int_line_stat= 0;
  new->int_line_stat= 0;
  new->rtx_type     = ETH_RTX_FILE;
  new->rtx_type     = ETH_RTX_FILE;
  new->rx_channel   = 0;
  new->rx_channel   = 0;
  new->tx_channel   = 0;
  new->tx_channel   = 0;
  new->rtx_fd       = -1;
  new->rtx_fd       = -1;
  new->rxfile       = strdup ("eth_rx");
  new->rxfile       = strdup ("eth_rx");
  new->txfile       = strdup ("eth_tx");
  new->txfile       = strdup ("eth_tx");
  new->tap_dev      = strdup ("");
  new->tap_dev      = strdup ("");
  new->phy_addr     = 0;
  new->phy_addr     = 0;
  new->dummy_crc    = 1;
  new->dummy_crc    = 1;
  new->base_vapi_id = 0;
  new->base_vapi_id = 0;
 
 
  return new;
  return new;
 
 
}       /* eth_sec_start () */
}       /* eth_sec_start () */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Complete the initialization of a new Ethernet configuration
/*!Complete the initialization of a new Ethernet configuration
 
 
   ALL parameters are set explicitly to default values.                      */
   ALL parameters are set explicitly to default values.                      */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static void
static void
eth_sec_end (void *dat)
eth_sec_end (void *dat)
{
{
  struct eth_device *eth = dat;
  struct eth_device *eth = dat;
  struct mem_ops ops;
  struct mem_ops ops;
 
 
  if (!eth->enabled)
  if (!eth->enabled)
    {
    {
      free (eth->rxfile);
      free (eth->rxfile);
      free (eth->txfile);
      free (eth->txfile);
      free (eth->tap_dev);
      free (eth->tap_dev);
      free (eth);
      free (eth);
      return;
      return;
    }
    }
 
 
  memset (&ops, 0, sizeof (struct mem_ops));
  memset (&ops, 0, sizeof (struct mem_ops));
 
 
  ops.readfunc32 = eth_read32;
  ops.readfunc32 = eth_read32;
  ops.writefunc32 = eth_write32;
  ops.writefunc32 = eth_write32;
  ops.read_dat32 = dat;
  ops.read_dat32 = dat;
  ops.write_dat32 = dat;
  ops.write_dat32 = dat;
 
 
  /* FIXME: Correct delay? */
  /* FIXME: Correct delay? */
  ops.delayr = 2;
  ops.delayr = 2;
  ops.delayw = 2;
  ops.delayw = 2;
  reg_mem_area (eth->baseaddr, ETH_ADDR_SPACE, 0, &ops);
  reg_mem_area (eth->baseaddr, ETH_ADDR_SPACE, 0, &ops);
  reg_sim_stat (eth_status, dat);
  reg_sim_stat (eth_status, dat);
  reg_sim_reset (eth_reset, dat);
  reg_sim_reset (eth_reset, dat);
 
 
}       /* eth_sec_end () */
}       /* eth_sec_end () */
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*!Register a new Ethernet configuration                                     */
/*!Register a new Ethernet configuration                                     */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
void
reg_ethernet_sec ()
reg_ethernet_sec ()
{
{
  struct config_section *sec =
  struct config_section *sec =
    reg_config_sec ("ethernet", eth_sec_start, eth_sec_end);
    reg_config_sec ("ethernet", eth_sec_start, eth_sec_end);
 
 
  reg_config_param (sec, "enabled",    PARAMT_INT,  eth_enabled);
  reg_config_param (sec, "enabled",    PARAMT_INT,  eth_enabled);
  reg_config_param (sec, "baseaddr",   PARAMT_ADDR, eth_baseaddr);
  reg_config_param (sec, "baseaddr",   PARAMT_ADDR, eth_baseaddr);
  reg_config_param (sec, "dma",        PARAMT_INT,  eth_dma);
  reg_config_param (sec, "dma",        PARAMT_INT,  eth_dma);
  reg_config_param (sec, "irq",        PARAMT_INT,  eth_irq);
  reg_config_param (sec, "irq",        PARAMT_INT,  eth_irq);
  reg_config_param (sec, "rtx_type",   PARAMT_STR,  eth_rtx_type);
  reg_config_param (sec, "rtx_type",   PARAMT_STR,  eth_rtx_type);
  reg_config_param (sec, "rx_channel", PARAMT_INT,  eth_rx_channel);
  reg_config_param (sec, "rx_channel", PARAMT_INT,  eth_rx_channel);
  reg_config_param (sec, "tx_channel", PARAMT_INT,  eth_tx_channel);
  reg_config_param (sec, "tx_channel", PARAMT_INT,  eth_tx_channel);
  reg_config_param (sec, "rxfile",     PARAMT_STR,  eth_rxfile);
  reg_config_param (sec, "rxfile",     PARAMT_STR,  eth_rxfile);
  reg_config_param (sec, "txfile",     PARAMT_STR,  eth_txfile);
  reg_config_param (sec, "txfile",     PARAMT_STR,  eth_txfile);
  reg_config_param (sec, "tap_dev",    PARAMT_STR,  eth_tap_dev);
  reg_config_param (sec, "tap_dev",    PARAMT_STR,  eth_tap_dev);
  reg_config_param (sec, "phy_addr",   PARAMT_INT,  eth_phy_addr);
  reg_config_param (sec, "phy_addr",   PARAMT_INT,  eth_phy_addr);
  reg_config_param (sec, "dummy_crc",  PARAMT_INT,  eth_dummy_crc);
  reg_config_param (sec, "dummy_crc",  PARAMT_INT,  eth_dummy_crc);
  reg_config_param (sec, "vapi_id",    PARAMT_INT,  eth_vapi_id);
  reg_config_param (sec, "vapi_id",    PARAMT_INT,  eth_vapi_id);
 
 
}       /* reg_ethernet_sec() */
}       /* reg_ethernet_sec() */
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.