[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