[eepro100] About eepro "out of resources" bug

Joseph Kulig joek@websprocket.com
Tue, 25 Sep 2001 18:17:43 -0400


Alexander Gdalevich wrote:

> What driver are you using?

This device driver is written in Java. It is part of a pure object oriented OS
that is written in Java. This pure Java OS is currently ported to the Strongarm
and Xscale processor platforms.

>
>
> It does not look like eepro100.c from Scyld, nor fxp_if for FreeBSD, nor
> e100 from Intel.  BTW, is it written in C++ !?  Also, how often do you see
> this happening?

The way we reproduce this is to have our target system continually send/receive
a "I am alive" string to the TCP echo port of my SUN workstation. Usually
within an hour this condition occurs.

>
>
> I am working on an embedded system project using 82559 chip and we did have
> a PCI problem that had caused what you are describing.  However, we have a
> custom PCI interface and I don't beleive any chipset on the market would
> have such bugs in it :)
>
> There is another possible problem, first it seemed like a hairsplitting, but
> seeing your post I see that it might actually be a problem.
>
> Here is the line from eepro100.c (Scyld driver)
>
> sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
>
> I am not sure if it does the same thing as your:
>
> last_rxf.status(last_rxf.status() & ~0xc0000000);
>
> but the problem is as follows.  Suppose you've read the status field just as
> the device was about to update it.  When you and it with (~0xC0000000) and
> write back you might write the old value of the status bits overwriting
> whatever the device had set it to.
>
> In fact, something similar is being done for command ring and there a
> different way is being used to clear suspend bit.
>
> #define clear_suspend(cmd)   ((char *)(&(cmd)->cmd_status))[3] &= ~0x40
>
> This way only a single byte is being modified.

I see what your saying that there is a potential race condition. I will have to
take a look at my code closely to see if this could be the problem.

>
>
> P.S.  I am very interested in the driver you are using.  What is it?

Below is the whole driver:


/*
 * Copyright (c) 2001 WebSprocket LLC. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published
 * by the Free Software Foundation. (see COPYING.LIB)
 *
 * WEBSPROCKET MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WEBSPROCKET SHALL NOT BE LIABLE FOR

 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

/* The user-configurable values.
   These may be modified when a driver module is loaded.
   The first five are undocumented and spelled per Intel recommendations.
*/
package devices;

import WebSprocket.Kernel.*;
import WebSprocket.Platform.net.*;
import WebSprocket.net.*;

public class EEPro100 extends NetworkDriver
 implements InterruptHandler, PciConfiguration {
 final static boolean congenb = false;  /* Enable congestion control in the
DP83840. */
 final static byte txfifo = 8;  /* Tx FIFO threshold in 4 byte units, 0-15 */
 final static byte rxfifo = 8;  /* Rx FIFO threshold, default 32 bytes. */
 /* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
 final static int txdmacount = 128;
 final static int rxdmacount = 0;

 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
    Lower values use more memory, but are faster. */
 final static int rx_copybreak = 200;

 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 final static int max_interrupt_work = 20;

 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
 final static int multicast_filter_limit = 64;

 final int debug = 0;   /* The debug level */

 /* A few values that may be tweaked. */
 /* The ring sizes should be a power of two for efficiency. */
 final static int TX_RING_SIZE = 32;  /* Effectively 2 entries fewer. */
 final static int RX_RING_SIZE = 32;
 /* Actual number of TX packets queued, must be <= TX_RING_SIZE-2. */
 final static int  TX_QUEUE_LIMIT =  12;
 final static int TX_QUEUE_UNFULL = 8;  /* Hysteresis marking queue as no
longer full. */

 /* Operational parameters that usually are not changed. */

 /* Time in jiffies before concluding the transmitter is hung. */
 final static int HZ = 1000;
 final static int TX_TIMEOUT = (2*HZ);
 /* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
 final static int PKT_BUF_SZ =  1536;

 // Chip capablities

 final static int ResetMII=1;
 final static int HasChksum=2;

 public final static Object deviceIdTable[] = {
  "Intel PCI EtherExpress Pro100", new Integer(0x12298086), new Integer(0),
  "Intel EtherExpress Pro/100+ i82559ER", new Integer(0x12098086), new
Integer(0),
  "Intel EtherExpress Pro/100 type 1029", new Integer(0x10298086), new
Integer(0),
  "Intel EtherExpress Pro/100 type 1030", new Integer(0x10308086), new
Integer(0),
  "Intel Pro/100 V Network", new Integer(0x24498086), new Integer(0),
  "Cyclone PMC60 Dual Ethernet", new Integer(0x12098086), new
Integer(0x360113c),
  "Cyclone IQ80310 Ethernet", new Integer(0x12098086), new Integer(0x700113c),
 };

 // ethernet address;
 byte deviceAddress[] = new byte[6];
 String name;

 // number of ethernet cards
 static int instance;
 int deviceId;
 int jiffies;
 int trans_start;
 int expires = jiffies+2*HZ;

 /* How to wait for the command unit to accept a command.
    Typically this takes 0 ticks. */

 /* Offsets to the various registers.
    All accesses need not be longword aligned. */
 final static int SCBStatus = 0;
 final static int SCBCmd = 2; /* Rx/Command Unit command and status. */
 final static int SCBPointer = 4;    /* General purpose pointer. */
 final static int SCBPort = 8;    /* Misc. commands and operands.  */
 final static int SCBflash = 12;
 final static int SCBeeprom = 14; /* EEPROM and flash memory control. */
 final static int SCBCtrlMDI = 16;   /* MDI interface control. */
 final static int SCBEarlyRx = 20;   /* Early receive byte count. */

 /* Commands that can be put in a command list entry. */
 final static int CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000;
 final static int CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR =
0x50000;
 final static int CmdDump = 0x60000, CmdDiagnose = 0x70000;
 final static int CmdSuspend = 0x40000000; /* Suspend after completion. */
 final static int CmdIntr = 0x20000000;  /* Interrupt after completion. */
 final static int CmdTxFlex = 0x00080000;  /* Use "Flexible mode" for CmdTx
command. */

 /* Do atomically if possible. */


 final static int SCBMaskCmdDone=0x8000;
 final static int SCBMaskRxDone=0x4000;
 final static int SCBMaskCmdIdle=0x2000;
 final static int SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800,
SCBMaskFlowCtl=0x0400;
 final static int SCBTriggerIntr=0x0200, SCBMaskAll=0x0100;
 /* The rest are Rx and Tx commands. */
 final static int CUStart=0x0010, CUResume=0x0020, CUHiPriStart=0x0030,
CUStatsAddr=0x0040;
 final static int CUShowStats=0x0050;
 final static int CUCmdBase=0x0060;  /* CU Base address (set to zero) . */
 final static int CUDumpStats=0x0070; /* Dump then reset stats counters. */
 final static int CUHiPriResume=0x00b0; /* Resume for the high priority Tx
queue. */
 final static int RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004,
RxAddrLoad=0x0006;
 final static int RxResumeNoResources=0x0007;

 final static int IntrCmdDone=0x8000,  IntrRxDone=0x4000, IntrCmdIdle=0x2000;
 final static int IntrRxSuspend=0x1000, IntrMIIDone=0x0800,
IntrDrvrIntr=0x0400;
 final static int IntrAllNormal=0xfc00;

 final static int PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3;


 static class TxFD {
  public int bufferAddress;
  MemoryObj buffer;

  TxFD() {
   buffer = SystemResource.getMemoryMap().allocateMemObj("PciDRAM", 32, 4);
   bufferAddress = buffer.getPhysicalStartAddress();
   // set
   status(0);
   link(0);
   descriptorAddress(bufferAddress+16);
  }

  // return status of tx buffer
  int status() {
   return buffer.read32(0);
  }

  void status(int v) {
   buffer.write32(0, v);
  }

  void link(int address) {
   buffer.write32(4, address);
  }

  // set
  void descriptorAddress(int address){
   buffer.write32(8, address);
  }

  int bufferAddress0(){
   return 0;
  }

  void bufferAddress0(int address){
   buffer.write32(16, address);
  }

  void bufferSize0(int size){
   buffer.write32(20, size);
  }

  void count(int size){
   buffer.write32(12, size);
  }

  // put paramater array into the cmd buffer
  void params(byte[] p){
   for(int i=0; i<p.length; i++){
    buffer.write8(i+8, p[i]);
   }
  }

  void clearSuspend(){
   int s = status();
   s &= ~CmdSuspend;
   status(s);
  }

 }

 static class RxFD extends EthernetPacket {
  int bufferAddress;

  public RxFD(NetworkDriver netif) {
   super(PKT_BUF_SZ, netif);
   bufferAddress = frameAddress;
   packetOffset = 16;
   write32LE(8, 0xffffffff);
  }

  final int status() {
   return read32LE(0);
  }

  final void status(int value){
   write32LE(0, value);
  }

  final void link(int address){
   write32LE(4, address);
  }

  final void rxBufferAddress(int address){
   write32LE(8, address);
  }

  final void count(int size){
   write32LE(12, size);
  }


  final void flushHeader() {
   SPROCKET_flush64(frameAddress);
   SPROCKET_drainWriteBuffer();
  }

  final void cleanHeader() {
   SPROCKET_clean64(frameAddress);
   SPROCKET_drainWriteBuffer();
  }

  final int count(){
   return read32LE(12);
  }

  final void print() {
   System.out.println(Integer.toHexString(bufferAddress)+": " +
          Integer.toHexString(read32LE(4)) + ' ' +
          Integer.toHexString(read32LE(8)) + ' ' +
          Integer.toHexString(read32LE(12)));
  }

 }

 int txEntry;  // used by EEProTxBuffer to set up TxFD

 class EEProTxBuffer extends Packet implements TxBuffer {
  int type;
  long destinationAddress;

  public EEProTxBuffer(int size){
   super(size+14);
   packetHeader = 14;
  }

  public void add(Packet p) {
   // setup ethernet header
   write32(0, (int)(destinationAddress>>16));
   write32(4, (int)(destinationAddress&0xffff)<<16 |
     (int)(myHwAddr>>>32));
   write32(8, (int)myHwAddr&0xffffffff);
   write16(12, type);

   // copy over packet
   p.push(frameAddress+14);
  }

  public void send() {
  }

  public void type(int t) {
   type = t;
  }

  public void destination(long d){
   destinationAddress = d;
  }

  public void push(){
   txRing[txEntry].bufferAddress0(frameAddress);
   txRing[txEntry].bufferSize0(frameSize);
  }
 }

 final static int PacketReceived = 0xc000;

 // /* The Speedo3 Rx and Tx buffer descriptors. */
 // struct RxFD {     /* Receive frame descriptor. */
 //  int status;
 //  int link;     /* struct RxFD * */
 //  int rx_buf_addr;   /* void * */
 //  int count;
 // };


 /* Selected elements of the Tx/RxFD.status word. */
 final static int RxComplete=0x8000, RxOK=0x2000;
 final static int RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200,
RxErrSymbol=0x0010;
 final static int RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002;
 final static int TxUnderrun=0x1000,  StatusComplete=0x8000;

 // struct TxFD {     /* Transmit frame descriptor set. */
 //  int status;
 //  int link;     /* void * */
 //  int tx_desc_addr;   /* Always points to the tx_buf_addr element. */
 //  int count;     /* # of TBD (=1), Tx start thresh., etc. */
 //  /* This constitutes two "TBD" entries -- we only use one. */
 //  int tx_buf_addr0;   /* void *, frame to be transmitted.  */
 //  int tx_buf_size0;   /* Length of Tx frame. */
 //  int tx_buf_addr1;   /* void *, frame to be transmitted.  */
 //  int tx_buf_size1;   /* Length of Tx frame. */
 // };

 /* Elements of the dump_statistics block. This block must be lword aligned. */

 int tx_aborted_errors;
 int tx_window_errors;
 int tx_fifo_errors;
 int rx_fifo_errors;
 int rx_crc_errors;
 int rx_frame_errors;
 int rx_length_errors;
 int rx_over_errors;
 int collisions;
 int done_marker;
 int tx_errors;
 int rx_errors;
 int rx_packets;

 /* Do not change the position (alignment) of the first few elements!
    The later elements are grouped for cache locality. */
 TxFD txRing[]; /* Commands (usually CmdTxPacket). */
 RxFD rxRing[]; /* Rx descriptor, used as ring. */

 RxFD rxPackets[];   // preallocated ethernet packets
 int rxPacketIndex;

 TxFD lastCmd; /* Last command sent. */
 int cur_tx, dirtyTx; /* The ring entries to be free()ed. */
 int lock;    /* Group with Tx control cache line. */
 int tx_threshold;     /* The value for txdesc.count. */
 long last_cmd_time;
 /* Rx control, one cache line. */
 RxFD last_rxf;    /* Most recent Rx frame. */
 int cur_rx, dirty_rx;  /* The next free ring entry */
 long last_rx_time;   /* Last Rx, in jiffies, to handle Rx hang. */
 int chip_id, drv_flags;
 int mc_setup_frm_len;     /* The length of an allocated.. */
 int mc_setup_busy;     /* Avoid double-use of setup frame. */
 int in_interrupt;     /* Word-aligned dev->interrupt */
 byte rx_mode;      /* Current PROMISC/ALLMULTI setting. */
 boolean txFull;    /* The Tx queue is full. */
 boolean full_duplex;   /* Full-duplex operation requested. */
 boolean flow_ctrl;   /* Use 802.3x flow control. */
 boolean rxBug;    /* Work around receiver hang errata. */
 boolean rx_bug10;   /* Receiver might hang at 10mbps. */
 boolean rx_bug100;   /* Receiver might hang at 100mbps. */
 boolean polling;    /* Hardware blocked interrupt line. */
 boolean medialock;   /* The media speed/duplex is fixed. */
 int phy[];    /* PHY media interfaces available. */
 short advertising;   /* Current PHY advertised caps. */
 short partner;    /* Link partner caps. */
 long last_reset;

 MemoryObj csr;  // control register
 MemoryObj stats; // stats dump area

 int pciInterruptMask;

 StringBuffer sb;

 /* The parameters for a CmdConfigure operation.
    There are so many options that it would be difficult to document each bit.
    We mostly use the default or recommended settings. */
 byte i82557ConfigCmd[] = {
  22, 0x08, 0, 0,  0, 0, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
  0, 0x2E, 0,  0x60, 0,
  (byte)0xf2, 0x48,   0, 0x40, (byte)0xf2, (byte)0x80,   /* 0x40=Force
full-duplex */
  0x3f, 0x05
 };
 byte i82558ConfigCmd[] = {
  22, 0x08, 0, 1,  0, 0, (byte)0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
  0, 0x2E, 0,  0x60, 0x8, (byte)0x88,
  0x68, 0, 0x40, (byte)0xf2, (byte)0x84,   /* 0xBD->0xFD=Force full-duplex */
  0x31, 0x05
 };

 /* PHY media interface chips. */
 final static String phys[] = {
  "None", "i82553-A/B", "i82553-C", "i82503",
  "DP83840", "80c240", "80c24", "i82555",
  "Microlinear", "Level One", "DP83840A", "ICS 1890",
  "unknown-12", "unknown-13", "unknown-14", "unknown-15",
 };

 final static int NonSuchPhy=0, I82553AB=1, I82553C=2, I82503=3, DP83840=4,
S80C240=5;
 final static int S80C24=6, I82555=7, DP83840A=10;
 final static byte is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
 final static int EE_READ_CMD = 6;
 private int eeReadCmd;
 private int eeSize;
 private int eeAddress;
 private int interrupt;

 public EEPro100(){
  sb = new StringBuffer(256);
  interrupt = 12-instance;
  name = "eepro" + instance;
  phy = new int[2];
  txRing = new TxFD[TX_RING_SIZE];
  rxRing = new RxFD[RX_RING_SIZE];
  rxPackets = new RxFD[128];
  stats = SystemResource.getMemoryMap().allocateMemObj("PciDRAM", 68, 4);
  hardwareType = 1;
  hwAddrLength = 6;
  hwBroadcast = 0xffffffffffffL;
  mtu = 1500;
  instance++;
 }

 public boolean busy() {
  return false;
 }

 public String name(){
  return name;
 }

 public boolean myDevice(int id, int id0){
  for(int i=0; i<deviceIdTable.length; i+=3){
   Integer manId = (Integer)deviceIdTable[i+1];
   Integer subId = (Integer)deviceIdTable[i+2];
   if(id == manId.intValue() && id0 == subId.intValue() ){
    deviceId = i;
    return true;
   }
  }
  sb.append("Device: ").append(Integer.toHexString(id));
  sb.append(" ").append(Integer.toHexString(id0)).append(" not found!");
  System.out.println(sb.toString());
  sb.setLength(0);
  return false;
 }

 // we only care about the memory base csr
 public void pciBAR0(MemoryObj reg) {
  csr = reg;
 }

 // we don't care about the other ones for now
 public void pciBAR1(MemoryObj r){
 }

 public void pciBAR2(MemoryObj r){
 }

 public void pciBAR3(MemoryObj r){
 }

 public void pciBAR4(MemoryObj r){
 }

 public void pciInterruptMask(int mask){
  pciInterruptMask = mask;
 }

 public int deviceControl() {
  return MEMORYACCESS|BUSMASTER;
 }

 private final void found()
 {
  int i, option=0;
  int[] eeprom = new int[0x100];
  MemoryObj selfTest = SystemResource.getMemoryMap().allocateMemObj("PciDRAM",
8, 16);
  /* Read the station address EEPROM before doing the reset.
     Nominally his should even be done before accepting the device, but
     then we wouldn't have a device name with which to report the error.
     The size test is for 6 bit vs. 8 bit address serial EEPROMs.
  */
  short sum = 0;
  int j;
  int read_cmd, ee_size;

  sizeEeprom();
  for (j = 0, i = 0; i < eeSize; i++) {
   int value = doEepromCmd(i)&0xffff;
   eeprom[i] = value;
   sum += value;
   if (i < 3) {
    deviceAddress[j++] = (byte)value;
    deviceAddress[j++] = (byte)(value >> 8);
   }
  }
  if (sum != 0xBABA) {
   sb.append(name).append(": Invalid EEPROM checksum ");
   sb.append(Integer.toHexString(sum)).append(", check settings before
activating this device!");
   System.out.println(sb.toString());
   sb.setLength(0);
  }

  /* Reset the chip: stop Tx and Rx processes and clear counters.
     This takes less than 10usec and will easily finish before the next
     action. */
  csr.write32(SCBPort, PortReset);
  SystemResource.getTimer().udelay(50);

  sb.append(name).append(": ").append((String)deviceIdTable[deviceId]);
  sb.append(" at ").append(Integer.toHexString(csr.startAddress));
  System.out.println(sb.toString());
  sb.setLength(0);

  for (i = 0; i < 6; i++) {
   System.out.print(Integer.toHexString(deviceAddress[i]&0xff)+":");
  }
  System.out.println();

  // We have a cyclone PMC52 card, IQ80310 board
  if(((Integer)deviceIdTable[deviceId+2]).intValue() == 0x360113c ||
     ((Integer)deviceIdTable[deviceId+2]).intValue() == 0x700113c) {
   // set the phy address; for 82559 this defaults to 1;
   phy[0] = 1;
   // read and print out the id registers
   sb.append("phy id: ").append(Integer.toHexString(mdioRead(phy[0], 2)));
   sb.append(' ').append(Integer.toHexString(mdioRead(phy[0], 3)));
   System.out.println(sb.toString());
   sb.setLength(0);
   // assuming we are i82555
  }
  else {
   /* OK, this is pure kernel bloat.  I don't like it when other drivers
      waste non-pageable kernel space to emit similar messages, but I need
      them for bug reports. */
   String connectors[] = {" RJ45", " BNC", " AUI", " MII"};
   if ((eeprom[3] & 0x03) != 0)
    System.out.println("Receiver lock-up bug exists -- enabling work-around.");

   sb.append("Board assembly ").append(Integer.toHexString(eeprom[8]));
   sb.append(" ").append(Integer.toHexString(eeprom[9]>>8));
   sb.append(" ").append(eeprom[9]&0xff).append(" connectors present: ");
   System.out.print(sb.toString());
   sb.setLength(0);

   for (i = 0; i < 4; i++)
    if ((eeprom[5] & (1<<i)) != 0)
     System.out.print(connectors[i]);
   sb.append("\r\nPrimary interface chip ").append(phys[(eeprom[6]>>8)&15]);
   sb.append(" PHY #").append(eeprom[6]&0x1f);
   System.out.println(sb.toString());
   sb.setLength(0);
   if ((eeprom[7] & 0x0700) != 0){
    sb.append("Secondary interface chip ").append(phys[(eeprom[7]>>8)&7]);
    System.out.println(sb.toString());
    sb.setLength(0);
   }

   if (((eeprom[6]>>8) & 0x3f) == DP83840
    ||  ((eeprom[6]>>8) & 0x3f) == DP83840A) {
    int mdi_reg23 = mdioRead( eeprom[6] & 0x1f, 23) | 0x0422;
    if (congenb)
     mdi_reg23 |= 0x0100;
    sb.append("DP83840 specific setup, setting register 23 to
").append(Integer.toHexString(mdi_reg23));
    System.out.println(sb.toString());
    sb.setLength(0);
    mdioWrite( eeprom[6] & 0x1f, 23, mdi_reg23);
   }
   if ((option >= 0) && (option & 0x330) != 0) {
    sb.append("  Forcing ").append((option&0x300)!=0?100:10);
    sb.append("Mbs ").append((option & 0x220)!=0 ? "full" : "half");
    sb.append("-duplex operation.");
    System.out.println(sb.toString());
    mdioWrite( eeprom[6] & 0x1f, 0,
         ((option & 0x300)!=0 ? 0x2000 : 0) |  /* 100mbps? */
         ((option & 0x220)!=0 ? 0x0100 : 0)); /* Full duplex? */
   } else {
    int mii_bmcrctrl = mdioRead( eeprom[6] & 0x1f, 0);
    /* Reset out of a transceiver left in 10baseT-fixed mode. */
    if ((mii_bmcrctrl & 0x3100) == 0)
     mdioWrite( eeprom[6] & 0x1f, 0, 0x8000);
   }
  }

  /* Perform a system self-test. */
  System.out.println("self test: " +
Integer.toHexString(selfTest.getPhysicalStartAddress()));
  csr.write32(SCBPort, selfTest.getPhysicalStartAddress() | PortSelfTest);
  selfTest.write16(2, 0);  // rom signature
  selfTest.write16(0, -1); // status
  int boguscnt = 16000; /* Timeout for set-test. */
  do {
   SystemResource.getTimer().udelay(10);
   //    int i0=100;
   //    while(i0-->0)
   //     ;
  } while (selfTest.read16(0) == -1  &&  --boguscnt >= 0);

  if (boguscnt < 0) {  /* Test optimized out. */
   sb.append("Self test failed, status
").append(Integer.toHexString(selfTest.read32(4)));
   sb.append(" Failure to initialize the i82557.\n\r");
   sb.append(" Verify that the card is a bus-master capable slot.");
   System.out.println(sb.toString());
   sb.setLength(0);
  } else {
   int results = selfTest.read16(0);
   sb.append("  General self-test:
").append((results&0x1000)==0?"failed":"passed").append("\r\n");
   sb.append("  Serial sub-system self-test:
").append((results&0x0020)==0?"failed":"passed").append("\r\n");
   sb.append("  Internal registers self-test:
").append((results&0x0008)==0?"failed":"passed").append("\r\n");
   sb.append("  ROM checksum self-test:
").append((results&0x0004)==0?"failed":"passed");
   sb.append("
(").append(Integer.toHexString(selfTest.read16(2))).append(')');
   System.out.println(sb.toString());
   sb.setLength(0);
  }

  csr.write32(SCBPort, PortReset);
  SystemResource.getTimer().udelay(100);
  //  pci_dev = pdev;
  //  chip_id = chip_idx;
  //  drv_flags = pci_id_tbl[chip_idx].drv_flags;
  //  acpi_pwr = acpi_idle_state;

  /*
    full_duplex = option >= 0 && (option & 0x220) ? 1 : 0;
    if (card_idx >= 0) {
    if (full_duplex[card_idx] >= 0)
    full_duplex = full_duplex[card_idx];
    }
    if (full_duplex)
    medialock = 1;
  */

  //   phy[0] = eeprom[6];
  //   phy[1] = eeprom[7];
  //   rxBug = (eeprom[3] & 0x03) == 3;

  //   if (rxBug)
  //    System.out.println("Receiver lock-up workaround activated.");

 }

 final void waitForCmdDone()
 {
  int wait = 0;
  do {
   if (csr.read8(SCBCmd) == 0) return;
  } while(++wait <= 100);
  do {
   if (csr.read8(SCBCmd) == 0) break;
  } while(++wait <= 10000);
  System.out.println("Command was not immediately accepted, " + wait + "
ticks!");
 }

 /* Perform a SCB command known to be slow.
    This function checks the status both before and after command execution. */

 final void doSlowCommand(int cmd)
 {
  int wait = 0;
  do
   if (csr.read8(SCBCmd) == 0) break;
  while(++wait <= 100);
  if (wait > 100){
   sb.append("Command ").append(Integer.toHexString(csr.read16(SCBCmd)));
   sb.append(" was never accepted (").append(wait).append(" ticks)!");
   System.out.println(sb.toString());
   sb.setLength(0);
  }
  csr.write8(SCBCmd, cmd);
  for (wait = 0; wait <= 10000; wait++)
   if (csr.read8(SCBCmd) == 0) return;
  sb.append("Command ").append(Integer.toHexString(csr.read16(SCBCmd)));
  sb.append(" was not accepted after ").append(wait).append(" polls!");
  System.out.println(sb.toString());
  sb.setLength(0);
 }



 /* Serial EEPROM section.
    A "bit" grungy, but we work our way through bit-by-bit :->. */
 /*  EEPROM_Ctrl bits. */
 final static int EE_SHIFT_CLK = 0x01; /* EEPROM shift clock. */
 final static int EE_CS    = 0x02; /* EEPROM chip select. */
 final static int EE_DATA_WRITE = 0x04; /* EEPROM chip data in. */
 final static int EE_DATA_READ = 0x08; /* EEPROM chip data out. */
 final static int EE_WRITE_0 = 0x2; // 0x4802;
 final static int EE_WRITE_1 = 0x6; // 0x4806;

 /* Delay between EEPROM clock transitions.
    The code works with no delay on 33Mhz PCI.  */

 final void eepromDelay() {
  SystemResource.getTimer().udelay(4);
  //   int i=100;
  //   while(i-->0)
  //    ;
 }

 final void sizeEeprom() {

  csr.write16(SCBeeprom, EE_CS);
  int cmd = EE_READ_CMD<<8;
  int addressBits = 0;
  for(int i=10; i>=0; i--, addressBits++){
   int data = (cmd & 1<<i)==0 ? EE_WRITE_0 : EE_WRITE_1;
   csr.write16(SCBeeprom, data);
   csr.write16(SCBeeprom, data|EE_SHIFT_CLK);
   eepromDelay();
   csr.write16(SCBeeprom, data);
   eepromDelay();

   int ee = csr.read16(SCBeeprom);
   if((ee & EE_DATA_READ) == 0){
    if(addressBits == 8){
     // 64 registers
     eeSize = 0x40;
     eeReadCmd = EE_READ_CMD<<6;
     eeAddress = 8;
    } else {
     // 256 registers
     eeSize = 0x100;
     eeReadCmd = EE_READ_CMD<<8;
     eeAddress = 10;
    }
    break;
   }
  }
  // read but discard
  for(int i=0; i<16; i++) {
   csr.write16(SCBeeprom, EE_CS);
   csr.write16(SCBeeprom, EE_CS|EE_SHIFT_CLK);
   eepromDelay();
   csr.write16(SCBeeprom, EE_CS);
   eepromDelay();
  }
  // disable the eeprom
  csr.write16(SCBeeprom, 0);
 }

 final int doEepromCmd(int cmd)
 {
  int data=0;

  csr.write16(SCBeeprom, EE_CS);
  cmd |= eeReadCmd;

  for(int i=eeAddress; true; i--){
   data = (cmd & 1<<i)==0 ? EE_WRITE_0 : EE_WRITE_1;
   csr.write16(SCBeeprom, data);
   csr.write16(SCBeeprom, data | EE_SHIFT_CLK);
   eepromDelay();
   csr.write16(SCBeeprom, data);
   eepromDelay();
   if((csr.read16(SCBeeprom) & EE_DATA_READ) == 0)
    break;
  }
  data = 0;
  // read value
  for(int i=0; i<16; i++) {
   csr.write16(SCBeeprom, EE_CS);
   csr.write16(SCBeeprom, EE_CS|EE_SHIFT_CLK);
   eepromDelay();
   data <<= 1;
   if((csr.read16(SCBeeprom) & EE_DATA_READ) != 0)
    data |= 1;

   csr.write16(SCBeeprom, EE_CS);
   eepromDelay();
  }
  // disable the eeprom
  csr.write16(SCBeeprom, 0);
  return data;
 }

 final int mdioRead(int phy_id, int location){
  int val, boguscnt = 7;  /* <64 usec. to complete, typ 27 ticks */
  csr.write32(SCBCtrlMDI, 0x08000000 | (location<<16) | (phy_id<<21));
  do {
   SystemResource.getTimer().udelay(10);
   val = csr.read32(SCBCtrlMDI);
   if (--boguscnt < 0) {
    sb.append(name).append(": mdioRead() timed out with val = ");
    sb.append(Integer.toHexString(val));
    System.out.println(sb.toString());
    sb.setLength(0);
    break;
   }
  } while ((val & 0x10000000)==0);
  return val & 0xffff;
 }

 final int mdioWrite(int phy_id, int location, int value){
  int val, boguscnt = 7;  /* <64 usec. to complete, typ 27 ticks */
  csr.write32(SCBCtrlMDI, 0x04000000 | (location<<16) | (phy_id<<21) | value);
  do {
   SystemResource.getTimer().udelay(10);
   val = csr.read32(SCBCtrlMDI);
   if (--boguscnt < 0) {
    StringBuffer sb = new StringBuffer();
    sb.append("eepro100: mdioWrite() timed out with val =");
    sb.append(Integer.toHexString(val));
    System.out.println(sb.toString());
    break;
   }
  } while ((val & 0x10000000)==0);
  return val & 0xffff;
 }


 public void init()
 {
  found();
  open();
 }

 private void open() {
  if (debug > 1) {
   StringBuffer sb = new StringBuffer();
   sb.append(name).append(": speedoOpen() ");
   System.out.println(sb.toString());
  }

  /* Set up the Tx queue early.. */
  cur_tx = 0;
  dirtyTx = 0;

  //   if ((phy[0] & 0x8000) == 0)
  //    advertising = (short)mdioRead( phy[0] & 0x1f, 4);
  //   /* With some transceivers we must retrigger negotiation to reset
  //      power-up errors. */
  //   if ((drv_flags & ResetMII)!=0 &&
  //    (phy[0] & 0x8000) == 0) {
  //    int phy_addr = phy[0] & 0x1f ;
  //    /* Use 0x3300 for restarting NWay, other values to force xcvr:
  //       0x0000 10-HD
  //       0x0100 10-FD
  //       0x2000 100-HD
  //       0x2100 100-FD
  //    */
  //    //   mdioWrite( phy_addr, 0, 0x3300);
  //   }

  // setup my ethernet address
  myHwAddr = (deviceAddress[0]&0xff)<<8 |(deviceAddress[1]&0xff);
  myHwAddr<<=32;
  myHwAddr |=  (deviceAddress[2]&0xff)<<24 |
   (deviceAddress[3]&0xff)<<16 |
   (deviceAddress[4]&0xff)<<8 |
   (deviceAddress[5]&0xff);

  System.out.println("mac: " + Long.toHexString(myHwAddr));
  mdioWrite( phy[0], 0, 0x3300);

  initRxRing();
  initTxRing();

  /* We can safely take handler calls during init.
     Doing this after initRxRing() results in a memory leak. */
  setupInterrupt();

  /* Fire up the hardware. */
  resume();

  rx_mode = -1;   /* Invalid -> always reset the mode. */

  setRxMode();

  if (debug > 2) {
   sb.append(name).append(": Done open(), status ");
   sb.append(Integer.toHexString(csr.read16(SCBStatus)));
   System.out.println(sb.toString());
   sb.setLength(0);
  }

  /* Set the timer.  The timer serves a dual purpose:
     1) to monitor the media interface (e.g. link beat) and perhaps switch
     to an alternate media type
     2) to monitor Rx activity, and restart the Rx process if the receiver
     hangs. */
  //  init_timer(&timer);
  //  timer.expires = jiffies + 3*HZ;
  //  timer.data = (unsigned long)dev;
  //  timer.function = &timer;     /* timer handler */
  //  add_timer(&timer);

  /* No need to wait for the command unit to accept here. */
  //   if ((phy[0] & 0x8000) == 0)
  //    mdioRead( phy[0] & 0x1f, 0);
 }

 /* Start the chip hardware after a full reset. */
 final void resume()
 {
  csr.write16(SCBCmd, SCBMaskAll);

  /* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */
  tx_threshold = 0x01200000;

  /* Set the segment registers to '0'. */
  waitForCmdDone();
  csr.write32(SCBPointer,0);
  //  csr.read32(SCBPointer);    /* Flush to PCI. */
  SystemResource.getTimer().udelay(10);     /* Bogus, but it avoids the bug. */

  /* Note: these next two operations can take a while. */
  csr.write8(SCBCmd, RxAddrLoad);
  waitForCmdDone();
  csr.write8(SCBCmd, CUCmdBase);
  waitForCmdDone();

  /* Load the statistics block and rx ring addresses. */
  stats.write32(64, 0);
  csr.write32(SCBPointer, stats.getPhysicalStartAddress());
  csr.write8(SCBCmd, CUStatsAddr);
  waitForCmdDone();

  csr.write32(SCBPointer, rxRing[cur_rx & RX_RING_SIZE-1].bufferAddress);
  csr.write8(SCBCmd, RxStart);
  waitForCmdDone();
  csr.write8(SCBCmd, CUDumpStats);
  SystemResource.getTimer().udelay(30);

  /* Fill the first command with our physical address. */
  int entry = cur_tx++ & TX_RING_SIZE-1;
  TxFD cur_cmd = txRing[entry];
  /* Avoid a bug(?!) here by marking the command already completed. */
  cur_cmd.status((CmdSuspend | CmdIASetup) | 0xa000);
  cur_cmd.link(txRing[cur_tx & TX_RING_SIZE-1].bufferAddress);
  cur_cmd.params(deviceAddress);
  if (lastCmd != null)
   lastCmd.clearSuspend();
  lastCmd = cur_cmd;
  waitForCmdDone();

  /* Start the chip's Tx process and unmask interrupts. */
  csr.write32(SCBPointer, txRing[dirtyTx & TX_RING_SIZE-1].bufferAddress);
  csr.write16(SCBCmd, CUStart|SCBMaskEarlyRx|SCBMaskFlowCtl);
 }

 /* Media monitoring and control. */
 final void timer()
 {
  int phy_num = phy[0] & 0x1f;
  int status = csr.read16(SCBStatus);

  if (debug > 3) {
   sb.append(name).append(": Interface monitor tick, chip status ");
   sb.append(Integer.toHexString(status));
   System.out.println(sb.toString());
   sb.setLength(0);
  }

  /* Normally we check every two seconds. */
  expires = jiffies + 2*HZ;

  if (polling) {
   /* Continue to be annoying. */
   if ((status & 0xfc00)!=0) {
    serviceInterrupt();
    if (jiffies - last_reset > 10*HZ) {
     sb.append(name).append(": is still blocked!");
     System.out.println(sb.toString());
     sb.setLength(0);
     last_reset = jiffies;
    }
   } else if (jiffies - last_reset > 10*HZ)
    polling = false;
   expires = jiffies + 2;
  }
  /* We have MII and lost link beat. */
  if ((phy[0] & 0x8000) == 0) {
   int partner = mdioRead( phy_num, 5);
   if (partner != partner) {
    boolean flow_ctrl = (advertising & partner & 0x0400)!=0;
    partner = partner;
    if (flow_ctrl != flow_ctrl) {
     flow_ctrl = flow_ctrl;
     rx_mode = -1; /* Trigger a reload. */
    }
    /* Clear sticky bit. */
    mdioRead( phy_num, 1);
    /* If link beat has returned... */
    //    if (mdioRead( phy_num, 1) & 0x0004)
    //     netif_link_up(dev);
    //    else
    //     netif_link_down(dev);
   }
  }

  /* This no longer has a false-trigger window. */
  if (cur_tx - dirtyTx > 1 &&
   (jiffies -trans_start) > TX_TIMEOUT  &&
   (jiffies - last_cmd_time) > TX_TIMEOUT) {
   /* Check for blocked interrupts. */
   if ((status & 0xfc00)!=0 && status != 0xffff) {
    /* We have a blocked IRQ line.  This should never happen, but
       we recover as best we can.*/
    if ( ! polling) {
     if (jiffies - last_reset > 10*HZ) {
      sb.append(name).append(": is physically blocked! Falling back to a
low-rate polling.");
      System.out.println(sb.toString());
      sb.setLength(0);
      last_reset = jiffies;
     }
     polling = true;
    }
    serviceInterrupt();
    expires = jiffies + 2; /* Avoid  */
   } else {
    txTimeout();
    last_reset = jiffies;
   }
  }
  if (rx_mode < 0  ||
   (rxBug  && jiffies - last_rx_time > 2*HZ)) {
   /* We haven't received a packet in a Long Time.  We might have been
      bitten by the receiver hang bug.  This can be cleared by sending
      a set multicast list command. */
   setRxMode();
  }
  // add_timer(&timer);
 }

 final void showState()
 {
  int phy_num = phy[0] & 0x1f;
  int i;

  /* Print a few items for debugging. */
  if (debug > 0) {
   sb.append(name).append(": Tx ring dump,  Tx queue ");
   sb.append(cur_tx&0x1f).append('/').append(dirtyTx&0x1f);
   System.out.println(sb.toString());
   sb.setLength(0);
   for (i = 0; i < TX_RING_SIZE; i++) {
    sb.append(name);
    if(i == (dirtyTx & TX_RING_SIZE-1))
     sb.append('*');
    if(i == (cur_tx & TX_RING_SIZE-1))
     sb.append('=');
    sb.append(':').append(i).append('
').append(Integer.toHexString(txRing[i].status()));
    sb.append(' ').append(Integer.toHexString(txRing[i].bufferAddress));
    System.out.println(sb.toString());
    sb.setLength(0);
   }
  }
  sb.append(name).append(":Printing Rx ring (next to receive into ");
  sb.append(cur_rx&0x1f).append(")");
  System.out.println(sb.toString());
  sb.setLength(0);

  for (i = 0; i < RX_RING_SIZE; i++) {
   sb.append("  Rx ring entry ").append(i);
   sb.append("  ").append(Integer.toHexString(rxRing[i].status()));
   sb.append("  ").append(Integer.toHexString(rxRing[i].bufferAddress));
   System.out.println(sb.toString());
   sb.setLength(0);
  }
  for (i = 0; i < 16; i++) {
   if (i == 6) i = 21;
   sb.append("  PHY index ").append(phy_num).append(" register ");
   sb.append(i).append(" is ").append(Integer.toHexString(mdioRead(phy_num,
i)));
   System.out.println(sb.toString());
   sb.setLength(0);
  }

 }

 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 final void initRxRing() {
  RxFD rxf=null;
  int i;

  cur_rx = 0;
  rxPacketIndex=0;

  for(i=0; i<rxPackets.length; i++)
   rxPackets[i] = new RxFD(this);

  if(debug > 5)
   System.out.println("rxPacket 0: " +
Integer.toHexString(rxPackets[0].bufferAddress));
  for (i = 0; i < RX_RING_SIZE; i++) {
   rxf = rxPackets[rxPacketIndex++];
   rxPacketIndex &= (rxPackets.length-1);
   rxRing[i] = rxf;
   if (last_rxf!=null)
    last_rxf.link(rxf.bufferAddress);
   last_rxf = rxf;
   rxf.status(1); /* '1' is flag value only. */
   rxf.link(0);      /* None yet. */
   /* This field unused by i82557, we use it as a consistency check. */
   rxf.rxBufferAddress(0xffffffff);
   rxf.count(PKT_BUF_SZ << 16);
   rxf.cleanHeader();
  }
  dirty_rx = i - RX_RING_SIZE;
  /* Mark the last entry as end-of-list. */
  last_rxf.status(0xC0000002); /* '2' is flag value only. */
  last_rxf.cleanHeader();
  //  last_rxf = last_rxf;
  if(debug > 5) {
   for(i = 0; i<rxRing.length; i++)
    rxRing[i].print();
  }
 }

 final void initTxRing() {
  for(int i=0; i<txRing.length; i++){
   txRing[i] = new TxFD();
  }
  if(debug > 3) {
   sb.append("txRing: ").append(Integer.toHexString(txRing[0].bufferAddress));
   System.out.println(sb.toString());
   sb.setLength(0);
  }
 }

 final void txTimeout() {
  int status = csr.read16(SCBStatus);

  StringBuffer sb = new StringBuffer();
  sb.append(name).append(": Transmit timed out: status ");
  sb.append(Integer.toHexString(status)).append(" ");
  sb.append(Integer.toHexString(csr.read16(SCBCmd))).append(" at ");
  sb.append(dirtyTx).append('/').append(cur_tx).append(" commands ");
  sb.append(Integer.toHexString(txRing[(dirtyTx+0) &
TX_RING_SIZE-1].status()));
  sb.append(Integer.toHexString(txRing[(dirtyTx+1) &
TX_RING_SIZE-1].status()));
  sb.append(Integer.toHexString(txRing[(dirtyTx+2) &
TX_RING_SIZE-1].status()));
  System.out.println(sb.toString());
  sb.setLength(0);

  /* Trigger a stats dump to give time before the reset. */
  getStats();

  showState();
  if ((status & 0x00C0) != 0x0080
   &&  (status & 0x003C) == 0x0010) {
   /* Only the command unit has stopped. */
   sb.append(name).append(": Trying to restart the transmitter...");
   System.out.println(sb.toString());
   sb.setLength(0);

   csr.write32(SCBPointer, txRing[dirtyTx & TX_RING_SIZE-1].bufferAddress);
   csr.write16(SCBCmd, CUStart);
  } else {
   sb.append(name).append(": Restarting the chip...");
   System.out.println(sb.toString());
   sb.setLength(0);
   /* Reset the Tx and Rx units. */
   csr.write32(SCBPort, PortReset);
   if (debug > 0)
    showState();
   SystemResource.getTimer().udelay(10);
   resume();
  }
  /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
  if ((phy[0] & 0x8000) == 0) {
   int phy_addr = phy[0] & 0x1f;
   int advertising = mdioRead( phy_addr, 4);
   int mii_bmcr = mdioRead( phy_addr, 0);
   mdioWrite( phy_addr, 0, 0x0400);
   mdioWrite( phy_addr, 1, 0x0000);
   mdioWrite( phy_addr, 4, 0x0000);
   mdioWrite( phy_addr, 0, 0x8000);

   mdioRead( phy_addr, 0);
   mdioWrite( phy_addr, 0, mii_bmcr);
   mdioWrite( phy_addr, 4, advertising);
  }
  tx_errors++;
  trans_start = jiffies;
  return;
 }

 /* Handle the interrupt cases when something unexpected happens. */
 final void interruptError(int intr_status) {
  if ((intr_status & IntrRxSuspend)!=0) {
   if ((intr_status & 0x003c) == 0x0028) /* No more Rx buffers. */
    csr.write8(SCBCmd, RxResumeNoResources);
   else if ((intr_status & 0x003c) == 0x0008) { /* No resources (why?!) */
    StringBuffer sb = new StringBuffer();
    sb.append(name).append(": Unknown receiver error, status=");
    sb.append(Integer.toHexString(intr_status));
    System.out.println(sb.toString());
    showState();
    SPROCKET_setBreakPoint();
    /* No idea of what went wrong.  Restart the receiver. */
    csr.write32(SCBPointer, rxRing[cur_rx & RX_RING_SIZE-1].bufferAddress);
    csr.write8(SCBCmd, RxStart);
   }
   rx_errors++;
  }
 }

 private void SPROCKET_setBreakPoint(){};

 public void start(){
  /* Block a timer-based transmit from overlapping.  This could better be
     done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
     If this ever occurs the queue layer is doing something evil! */
  while(true) {
   if(txQueue.isEmpty())
    break;
   if (txFull) {
    int tickssofar = jiffies - trans_start;
    if (tickssofar < TX_TIMEOUT - 2)
     return;
    if (tickssofar < TX_TIMEOUT) {
     /* Reap sent packets from the full Tx queue. */
     csr.write16(SCBCmd, SCBTriggerIntr);
     return;
    }
    txTimeout();
    return;
   }


   EEProTxBuffer txPacket = (EEProTxBuffer)txQueue.remove();
   /* Caution: the write order is important here, set the base address
      with the "ownership" bits last. */

   /* Prevent interrupts from changing the Tx ring from underneath us. */
   int flags;

   /* Calculate the Tx descriptor entry. */
   int mask = CpuControl.maskCPUInterrupts();
   txEntry = cur_tx & TX_RING_SIZE-1;

   if(debug > 6){
    sb.append("start: ").append(txEntry).append('
').append(Integer.toHexString(txRing[txEntry].bufferAddress));
    System.out.println(sb.toString());
    sb.setLength(0);
   }
   /* Todo: be a little more clever about setting the interrupt bit. */
   txRing[txEntry].status(CmdSuspend | CmdTx | CmdTxFlex);
   cur_tx++;
   txRing[txEntry].link(txRing[cur_tx & TX_RING_SIZE-1].bufferAddress);
   /* We may nominally release the lock here. */
   txRing[txEntry].descriptorAddress(txRing[txEntry].bufferAddress+16);
   /* The data region is always in one buffer descriptor. */
   txRing[txEntry].count(tx_threshold);

   // setup the TxFD
   txPacket.push();
   /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
      than half full.  Argument against: we should be receiving packets
      and scavenging the queue.  Argument for: if so, it shouldn't
      matter. */
   TxFD lastCmd0 = lastCmd;
   lastCmd = txRing[txEntry];
   lastCmd0.clearSuspend();
   if (cur_tx - dirtyTx >= TX_QUEUE_LIMIT)
    txFull = true;
   else
    //netif_unpause_tx_queue(dev);
    txFull = false;
   CpuControl.umaskCPUInterrupts(mask);
   waitForCmdDone();
   csr.write8(SCBCmd, CUResume);
   trans_start = jiffies;
  }
  return;
 }

 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
 public void serviceInterrupt()
 {
  long boguscnt = max_interrupt_work;
  int status;

  if(cur_rx-dirty_rx>15) {
   System.out.println("cur_rx>dirty_rx: " + cur_rx + ' ' + dirty_rx);
   showState();
   SPROCKET_setBreakPoint();
  }
  do {
   status = csr.read16(SCBStatus);
   /* Acknowledge all of the current interrupt sources ASAP. */
   csr.write16(SCBStatus, status & IntrAllNormal);

   if (debug > 4){
    sb.append(name).append(": interrupt status=");
    sb.append(Integer.toHexString(status));
    System.out.println(sb.toString());
    sb.setLength(0);
   }
   if ((status & IntrAllNormal) == 0)
    break;

   //   refillRxBuffers(0);
   if ((status & (IntrRxDone|IntrRxSuspend)) != 0)
    rx();

   /* The command unit did something, scavenge finished Tx entries. */
   if ((status & (IntrCmdDone | IntrCmdIdle | IntrDrvrIntr))!=0) {
    int dirtyTx0;

    dirtyTx0 = dirtyTx;
    if(debug>5) {
     sb.append("indexes: ").append(dirtyTx).append(' ').append(cur_tx);
     System.out.println(sb.toString());
     sb.setLength(0);
    }
    while (cur_tx - dirtyTx0 > 0) {
     int entry = dirtyTx0 & TX_RING_SIZE-1;
     status = txRing[entry].status();

     if (debug > 5) {
      sb.append("scavenge candidate ").append(entry);
      sb.append(" status ").append(Integer.toHexString(status));
      System.out.println(sb.toString());
      sb.setLength(0);
     }
     if ((status & StatusComplete) == 0) {
      /* Special case error check: look for descriptor that the
         chip skipped(?). */
      if (cur_tx - dirtyTx0 > 2  &&
       (txRing[(dirtyTx0+1) & TX_RING_SIZE-1].status()
        & StatusComplete)!=0) {
       sb.append(name).append(": Command unit failed to mark command ");
       sb.append(Integer.toHexString(status)).append(" as complete at
").append(dirtyTx);
       System.out.println(sb.toString());
       sb.setLength(0);
      } else
       break;   /* It still hasn't been processed. */
     }
     if ((status & TxUnderrun)!=0)
      if (tx_threshold < 0x01e00000)
       tx_threshold += 0x00040000;
     if ((status & 0x70000) == CmdNOp)
      mc_setup_busy = 0;
     dirtyTx0++;
    }

    if (cur_tx - dirtyTx0 > TX_RING_SIZE) {
     sb.append("out-of-sync dirty pointer, ").append(dirtyTx0).append(" vs. ");

     sb.append(cur_tx).append(" full=").append(txFull);
     System.out.println(sb.toString());
     sb.setLength(0);
     dirtyTx0 += TX_RING_SIZE;
    }

    dirtyTx = dirtyTx0;
    if (txFull
     &&  cur_tx - dirtyTx < TX_QUEUE_UNFULL) {
     /* The ring is no longer full, clear tbusy. */
     txFull = false;
     // netif_resume_tx_queue(dev);
    }
   }

   if ((status & IntrRxSuspend)!=0)
    interruptError(status);

   if (--boguscnt < 0) {
    StringBuffer sb = new StringBuffer();
    sb.append(name).append(": Too much work at interrupt, status=");
    sb.append(Integer.toHexString(status));
    System.out.println(sb.toString());
    /* Clear all interrupt sources. */
    csr.write16(SCBStatus, 0xfc00);
    break;
   }
  } while (true);

  if (debug > 3){
   StringBuffer sb = new StringBuffer();
   sb.append(name).append(": exiting interrupt, status=");
   sb.append(Integer.toHexString(csr.read16(SCBStatus)));
   sb.append("\n\r").append("indexes: ").append(dirtyTx).append('
').append(cur_tx);
   System.out.println(sb.toString());
  }

  //  clear_bit(0, (void*)&in_interrupt);
  return;
 }

 final int rx() {
  int entry = cur_rx & RX_RING_SIZE-1;
  int status;
  int rx_work_limit = dirty_rx + RX_RING_SIZE - cur_rx;
  RxFD rxf;

  if (debug > 4)
   System.out.println(" In rx().");
  rxRing[entry].flushHeader();
  int count;
  while (rxRing[entry] != null &&
      ((count = rxRing[entry].count()) & PacketReceived) == PacketReceived) {
   int pkt_len = count & 0x07ff;

   if (--rx_work_limit < 0)
    break;
   status = rxRing[entry].status();
   if (debug > 4)
    System.out.println(" rx() status " + Integer.toHexString(status) +
           " len " + pkt_len);
   if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) {
    if ((status & RxErrTooBig)!=0)
     System.out.println(name +": Ethernet frame overran the Rx buffer, status "
+ Integer.toHexString(status));
    else if ( ! ((status & RxOK)!=0)) {
     /* There was a fatal error.  This *should* be impossible. */
     rx_errors++;
     sb.append(name).append(": Anomalous event in rx(), status ");
     sb.append(Integer.toHexString(status));
     System.out.println(sb.toString());
     sb.setLength(0);
    }
   } else {
    if ((drv_flags & HasChksum)!=0)
     pkt_len -= 2;

    /* Check if the packet is long enough to just accept without
       copying to a properly sized skbuff. */
    //    if (pkt_len < rx_copybreak) {
    //     /* Packet is in one chunk -- we can copy + cksum. */
    //     // eth_copy_and_sum(skb, rx_skbuff[entry]->tail, pkt_len, 0);
    //    } else {
    /* Pass up the already-filled skbuff. */
    addPacket(rxRing[entry]);
    rxRing[entry].size(pkt_len);
    rxRing[entry].flush();
    rxRing[entry] = null;
    if ((drv_flags & HasChksum)!=0) {
     //     u16 csum = get_unaligned((u16*)(skb->head + pkt_len))
     //     if (desc_count & 0x8000)
     //      skb->ip_summed = CHECKSUM_UNNECESSARY;
    }
    rx_packets++;
   }
   entry = (++cur_rx) & RX_RING_SIZE-1;
   rxRing[entry].flushHeader();
  }

  for(; cur_rx-dirty_rx>0; dirty_rx++){
   entry = dirty_rx & RX_RING_SIZE-1;

   rxRing[entry] = rxPackets[rxPacketIndex];
   rxf = rxRing[entry];
   rxPacketIndex++;
   rxPacketIndex &= (rxPackets.length-1);

   rxf.status(0xc0000001);
   rxf.count(PKT_BUF_SZ<<16);
   rxf.link(0);

   last_rxf.link(rxf.bufferAddress);
   last_rxf.status(last_rxf.status() & ~0xc0000000);
   last_rxf.cleanHeader();
   last_rxf = rxf;
   rxf.cleanHeader();
  }

  last_rx_time = jiffies;
  return 0;
 }


 // final int
 // speedo_close()
 // {
 //  int i;

 //  netif_stop_tx_queue(dev);

 //  if (speedo_debug > 1)
 //   printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
 //       dev->name, (int)inw(ioaddr + SCBStatus));

 //  /* Shut off the media monitoring timer. */
 //  del_timer(&timer);

 //  /* Shutting down the chip nicely fails to disable flow control. So.. */
 //  outl(PortPartialReset, ioaddr + SCBPort);

 //  free_irq(dev->irq, dev);

 //  /* Free all the skbuffs in the Rx and Tx queues. */
 //  for (i = 0; i < RX_RING_SIZE; i++) {
 //   struct sk_buff *skb = rx_skbuff[i];
 //   rx_skbuff[i] = 0;
 //   /* Clear the Rx descriptors. */
 //   if (skb) {
 //    dev_free_skb(skb);
 //   }
 //  }

 //  for (i = 0; i < TX_RING_SIZE; i++) {
 //   struct sk_buff *skb = tx_skbuff[i];
 //   tx_skbuff[i] = 0;
 //   /* Clear the Tx descriptors. */
 //   if (skb)
 //    dev_free_skb(skb);
 //  }
 //  if (mc_setup_frm) {
 //   kfree(mc_setup_frm);
 //   mc_setup_frm_len = 0;
 //  }

 //  /* Print a few items for debugging. */
 //  if (speedo_debug > 3)
 //   showState();

 //  /* Alt: acpi_set_pwr_state(pdev, acpi_pwr); */
 //  acpi_set_pwr_state(pci_dev, ACPI_D2);
 //  MOD_DEC_USE_COUNT;

 //  return 0;
 // }

 /* The Speedo-3 has an especially awkward and unusable method of getting
    statistics out of the chip.  It takes an unpredictable length of time
    for the dump-stats command to complete.  To avoid a busy-wait loop we
    update the stats with the previous dump results, and then trigger a
    new dump.

    These problems are mitigated by the current /proc implementation, which
    calls this routine first to judge the output length, and then to emit the
    output.

    Oh, and incoming frames are dropped while executing dump-stats!
 */
 final void getStats()
 {

  /* Update only if the previous dump finished. */
  if (stats.read32(64) == 0xA007) {
   tx_aborted_errors += stats.read32(4);
   tx_window_errors += stats.read32(8);
   tx_fifo_errors += stats.read32(12);
   tx_fifo_errors +=  stats.read32(16);
   /*stats.tx_deferred += le32_to_cpu(lstats.tx_deferred);*/
   collisions +=  stats.read32(32);
   rx_crc_errors +=  stats.read32(40);
   rx_frame_errors +=  stats.read32(44);
   rx_over_errors += stats.read32(48);
   rx_fifo_errors += stats.read32(52);
   rx_length_errors += stats.read32(60);
   stats.write32(64, 0);
   waitForCmdDone();
   csr.write8(SCBCmd, CUDumpStats);
  }
 }

 /* Set or clear the multicast filter for this adaptor.
    This is very ugly with Intel chips -- we usually have to execute an
    entire configuration command, plus process a multicast command.
    This is complicated.  We must put a large configuration command and
    an arbitrarily-sized multicast command in the transmit list.
    To minimize the disruption -- the previous command might have already
    loaded the link -- we convert the current command block, normally a Tx
    command, into a no-op and link it to the new command.
 */
 final void setRxMode()
 {
  TxFD lastCmd0;
  byte new_rx_mode=0;
  int flags;
  int entry;
  byte[] configData = new byte[22];

  //   if (flags & IFF_PROMISC) {   /* Set promiscuous. */
  //    new_rx_mode = 3;
  //   } else if ((flags & IFF_ALLMULTI)  ||
  //        >mc_count > multicast_filter_limit) {
  //    new_rx_mode = 1;
  //   } else
  //    new_rx_mode = 0;


  if (cur_tx - dirtyTx >= TX_RING_SIZE - 1) {
   /* The Tx ring is full -- don't add anything!  Presumably the new mode
      is in config_cmd_data and will be added anyway, otherwise we wait
      for a timer tick or the mode to change again. */
   rx_mode = -1;
   return;
  }

  //  if (new_rx_mode != rx_mode) {
  //  int mask=CpuControl.maskCPUInterrupts();
  entry = cur_tx & TX_RING_SIZE-1;
  lastCmd0 = lastCmd;
  lastCmd = txRing[entry];

  txRing[entry].status(CmdSuspend | CmdConfigure);
  cur_tx++;
  txRing[entry].link(txRing[(entry + 1) & TX_RING_SIZE-1].bufferAddress);

  /* Construct a full CmdConfig frame. */
  System.arraycopy(i82558ConfigCmd, 0, configData, 0, configData.length);
  //    configData[1] = (byte)((txfifo << 4) | rxfifo);
  //    configData[4] = rxdmacount;
  //    configData[5] = (byte)(txdmacount + 0x80);
  if ((drv_flags & HasChksum)!=0)
   configData[9] |= 1;
  //   configData[15] |= (new_rx_mode & 2)!=0 ? 1 : 0;
  //    configData[19] = (byte)(flow_ctrl ? 0xBD : 0x80);
  //    configData[19] |= full_duplex ? 0x40 : 0;
  //    configData[21] = (byte)((new_rx_mode & 1)!=0 ? 0x0D : 0x05);
  if ((phy[0] & 0x8000)!=0) {   /* Use the AUI port instead. */
   configData[15] |= 0x80;
   configData[8] = 0;
  }
  for(int i=0; i<configData.length; i++){
   sb.append(i).append(':').append(Integer.toHexString(configData[i])).append('
');
   System.out.print(sb.toString());
   sb.setLength(0);
  }
  txRing[entry].params(configData);
  /* Trigger the command unit resume. */
  waitForCmdDone();
  lastCmd0.clearSuspend();
  csr.write8(SCBCmd, CUResume);
  //  CpuControl.umaskCPUInterrupts(mask);
  last_cmd_time = jiffies;
  //  }

  rx_mode = new_rx_mode;

  // set up multicast
//   mask = CpuControl.maskCPUInterrupts();
//   entry = cur_tx & TX_RING_SIZE-1;
//   lastCmd0 = lastCmd;
//   lastCmd = txRing[entry];
//   cur_tx++;
//   txRing[entry].status(CmdSuspend | CmdMulticastList);
//   txRing[entry].descriptorAddress(0);
//   txRing[entry].link(txRing[entry+1 & TX_RING_SIZE-1].bufferAddress);
//   waitForCmdDone();
//   lastCmd0.clearSuspend();
//   csr.write8(SCBCmd, CUResume);
//   CpuControl.umaskCPUInterrupts(mask);
  last_cmd_time = jiffies;
 }

 // static int speedo_pwr_event(void *dev_instance, int event)
 // {
 //  struct net_device *dev = dev_instance;
 //  struct speedo_private *np = (struct speedo_private *)dev->priv;
 //  long ioaddr = dev->base_addr;

 //  if (speedo_debug > 1)
 //   printk("%s: Handling power event %d.\n", dev->name, event);
 //  switch(event) {
 //  case DRV_ATTACH:
 //   MOD_INC_USE_COUNT;
 //   break;
 //  case DRV_SUSPEND:
 //   outl(PortPartialReset, ioaddr + SCBPort);
 //   break;
 //  case DRV_RESUME:
 //   resume();
 //   np->rx_mode = -1;
 //   np->flow_ctrl = np->partner = 0;
 //   setRxMode();
 //   break;
 //  case DRV_DETACH: {
 //   struct net_device **devp, **next;
 //   if (dev->flags & IFF_UP) {
 //    dev_close(dev);
 //    dev->flags &= ~(IFF_UP|IFF_RUNNING);
 //   }
 //   unregister_netdev(dev);
 //   release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
 // #ifndef USE_IO_OPS
 //   iounmap((char *)dev->base_addr);
 // #endif
 //   for (devp = &root_speedo_dev; *devp; devp = next) {
 //    next = &((struct speedo_private *)(*devp)->priv)->next_module;
 //    if (*devp == dev) {
 //     *devp = *next;
 //     break;
 //    }
 //   }
 //   if (np->priv_addr)
 //    kfree(np->priv_addr);
 //   kfree(dev);
 //   MOD_DEC_USE_COUNT;
 //   break;
 //  }
 //  case DRV_PWR_DOWN:
 //  case DRV_PWR_UP:
 //   acpi_set_pwr_state(np->pci_dev, event==DRV_PWR_DOWN ? ACPI_D3:ACPI_D0);
 //   break;
 //  case DRV_PWR_WakeOn:
 //  default:
 //   return -1;
 //  }

 //  return 0;
 // }

 public int getMask() {
  return pciInterruptMask;
 }

 public void setupInterrupt(){
  FIQHandler.addHandler(this, interrupt);
  SystemResource.getIntrController().set(pciInterruptMask);
  System.out.println(name+": interrupt " + interrupt);
 }

 public void setBroadcastMode(boolean mode){
 }

 public boolean bootComplete(){
  return false;
 }

 public boolean addMulticastAddress(long address){
  return false;
 }

 public void setMulticastMode(boolean mode){
 }

 public void setPromiscuousMode(boolean mode){
 }

 public boolean shutDown() {
  return false;
 }

 public boolean txChipBufferFull() {
  return txFull;
 }

 public TxBuffer allocateTxBuffer(int size) {
  return new EEProTxBuffer(size);
 }
}



--
Joe Kulig                                               phone:  216-357-2580
joek@websprocket.com                    fax:    216-357-2584
2253 Professor Street                   Cleveland, OH 44113