Goto sanos source index
//
// eepro100.c
//
// Intel EtherExpress Pro100 NIC network driver
//
// Written 1998-2002 by Donald Becker.
// Ported to sanos 2002-2004 by Michael Ringgaard.
//
// This software may be used and distributed according to the terms of
// the GNU General Public License (GPL), incorporated herein by reference.
// Drivers based on or derived from this code fall under the GPL and must
// retain the authorship, copyright and license notice. This driver is not
// a complete program and may only be used when the entire operating
// system is licensed under the GPL.
//
// This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
// It should work with all i82557/558/559 boards.
//
// The author may be reached as becker@scyld.com, or C/O
// Scyld Computing Corporation
// 410 Severn Ave., Suite 210
// Annapolis MD 21403
//
#include <os/krnl.h>
// User-configurable values
// The first five are undocumented and spelled per Intel recommendations.
static int congenb = 0; // Enable congestion control in the DP83840
static int txfifo = 8; // Tx FIFO threshold in 4 byte units, 0-15
static int rxfifo = 8; // Rx FIFO threshold, default 32 bytes
// Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled
static int txdmacount = 128;
static int rxdmacount = 0;
// Copy breakpoint for the copy-only-tiny-buffer Rx method
// Lower values use more memory, but are faster
static int rx_copybreak = 200;
// Maximum events (Rx packets, etc.) to handle at each interrupt
static int max_interrupt_work = 20;
// Maximum number of multicast addresses to filter (vs. rx-all-multicast)
static int multicast_filter_limit = 64;
// The ring sizes should be a power of two for efficiency
#define TX_RING_SIZE 32 // Effectively 2 entries fewer
#define RX_RING_SIZE 32
// Actual number of TX packets queued, must be <= TX_RING_SIZE - 2
#define TX_QUEUE_LIMIT 12
#define TX_QUEUE_UNFULL 8 // Hysteresis marking queue as no longer full
// Operational parameters that usually are not changed
// Time in ticks before concluding the transmitter is hung
#define TX_TIMEOUT (2*HZ)
// Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack
#define PKT_BUF_SZ 1536
// Bus+endian portability operations
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
// This table drives the PCI probe routines
enum chip_capability_flags {
ResetMII = 1,
HasChksum = 2
};
static struct board board_tbl[] = {
{"Intel", "Intel i82559 rev 8", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1229), 0xffffffff, 0, 0, 8, 0xff, HasChksum},
{"Intel", "Intel PCI EtherExpress Pro100", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1229), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel EtherExpress Pro/100+ i82559ER", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1209), 0xffffffff, 0, 0, 0, 0, ResetMII},
{"Intel", "Intel EtherExpress Pro/100 type 1029", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1029), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel EtherExpress Pro/100 type 1030", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1030), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 V Network", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x2449), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VE (type 1031)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1031), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VM (type 1038)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1038), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VM (type 1039)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1039), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VM (type 103a)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x103a), 0xffffffff, 0, 0, 0, 0, 0},
{"HP", "HP/Compaq D510 Intel Pro/100 VM", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x103b), 0xffffffff, PCI_UNITCODE(0x0e11, 0x0012), 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VM (type 103b)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x103b), 0xffffffff, 0, 0, 0, 0, 0},
{"Intel", "Intel Pro/100 VM (unknown type series 1030)", BUSTYPE_PCI, PCI_UNITCODE(0x8086, 0x1030), 0xfffffff0, 0, 0, 0, 0, 0},
{NULL,},
};
// Offsets to the various registers
// All accesses need not be longword aligned
enum speedo_offsets {
SCBStatus = 0, // Rx/Command Unit status
SCBCmd = 2, // Rx/Command Unit command
SCBPointer = 4, // General purpose pointer
SCBPort = 8, // Misc. commands and operands
SCBflash = 12, // Flash memory control
SCBeeprom = 14, // EEPROM memory control
SCBCtrlMDI = 16, // MDI interface control
SCBEarlyRx = 20, // Early receive byte count
};
// Commands that can be put in a command list entry
enum commands {
CmdNOp = 0x00000000,
CmdIASetup = 0x00010000,
CmdConfigure = 0x00020000,
CmdMulticastList = 0x00030000,
CmdTx = 0x00040000,
CmdTDR = 0x00050000,
CmdDump = 0x00060000,
CmdDiagnose = 0x00070000,
CmdTxFlex = 0x00080000, // Use "Flexible mode" for CmdTx command
CmdIntr = 0x20000000, // Interrupt after completion
CmdSuspend = 0x40000000, // Suspend after completion
};
enum SCBCmdBits {
SCBMaskCmdDone = 0x8000,
SCBMaskRxDone = 0x4000,
SCBMaskCmdIdle = 0x2000,
SCBMaskRxSuspend = 0x1000,
SCBMaskEarlyRx = 0x0800,
SCBMaskFlowCtl = 0x0400,
SCBTriggerIntr = 0x0200,
SCBMaskAll = 0x0100,
// The rest are Rx and Tx commands
CUStart = 0x0010,
CUResume = 0x0020,
CUHiPriStart = 0x0030,
CUStatsAddr = 0x0040,
CUShowStats = 0x0050,
CUCmdBase = 0x0060, // CU Base address (set to zero)
CUDumpStats = 0x0070, // Dump then reset stats counters
CUHiPriResume = 0x00b0, // Resume for the high priority Tx queue
RxStart = 0x0001,
RxResume = 0x0002,
RxAbort = 0x0004,
RxAddrLoad = 0x0006,
RxResumeNoResources = 0x0007,
};
enum intr_status_bits {
IntrCmdDone = 0x8000,
IntrRxDone = 0x4000,
IntrCmdIdle = 0x2000,
IntrRxSuspend = 0x1000,
IntrMIIDone = 0x0800,
IntrDrvrIntr = 0x0400,
IntrAllNormal = 0xfc00,
};
enum SCBPort_cmds {
PortReset = 0,
PortSelfTest = 1,
PortPartialReset = 2,
PortDump = 3,
};
// The Speedo3 Rx and Tx frame/buffer descriptors
// A generic descriptor
struct descriptor {
long cmd_status; // All command and status fields
unsigned long link; // struct descriptor *
unsigned char params[0];
};
// The Speedo3 Rx and Tx buffer descriptors
// Receive frame descriptor
struct RxFD {
long status;
unsigned long link; // struct RxFD *
unsigned long rx_buf_addr; // void *
unsigned long count;
};
// Selected elements of the Tx/RxFD.status word
enum RxFD_bits {
RxComplete = 0x8000,
RxOK = 0x2000,
RxErrCRC = 0x0800,
RxErrAlign = 0x0400,
RxErrTooBig = 0x0200,
RxErrSymbol = 0x0010,
RxEth2Type = 0x0020,
RxNoMatch = 0x0004,
RxNoIAMatch = 0x0002,
TxUnderrun = 0x1000,
StatusComplete = 0x8000,
};
// Transmit frame descriptor set
struct TxFD {
long status;
unsigned long link; // void *
unsigned long tx_desc_addr; // Always points to the tx_buf_addr element.
long count; // # of TBD (=1), Tx start thresh., etc.
// This constitutes two "TBD" entries. Non-zero-copy uses only one
unsigned long tx_buf_addr0; // void *, frame to be transmitted.
long tx_buf_size0; // Length of Tx frame.
unsigned long tx_buf_addr1; // Used only for zero-copy data section.
long tx_buf_size1; // Length of second data buffer (0).
};
// Elements of the dump_statistics block. This block must be dword aligned.
struct speedo_stats {
unsigned long tx_good_frames;
unsigned long tx_coll16_errs;
unsigned long tx_late_colls;
unsigned long tx_underruns;
unsigned long tx_lost_carrier;
unsigned long tx_deferred;
unsigned long tx_one_colls;
unsigned long tx_multi_colls;
unsigned long tx_total_colls;
unsigned long rx_good_frames;
unsigned long rx_crc_errs;
unsigned long rx_align_errs;
unsigned long rx_resource_errs;
unsigned long rx_overrun_errs;
unsigned long rx_colls_errs;
unsigned long rx_runt_errs;
unsigned long done_marker;
};
// Do not change the position (alignment) of the first few elements!
// The later elements are grouped for cache locality.
struct nic {
struct TxFD tx_ring[TX_RING_SIZE]; // Commands (usually CmdTxPacket)
struct RxFD *rx_ringp[RX_RING_SIZE]; // Rx descriptor, used as ring
// The addresses of a Tx/Rx-in-place packets/buffers.
struct pbuf *tx_pbuf[TX_RING_SIZE];
struct pbuf *rx_pbuf[RX_RING_SIZE];
struct descriptor *last_cmd; // Last command sent
unsigned int cur_tx, dirty_tx; // The ring entries to be free()ed
unsigned long tx_threshold; // The value for txdesc.count
unsigned long last_cmd_time;
struct sem tx_sem; // Semaphore for Tx ring not full
// Rx control, one cache line.
struct RxFD *last_rxf; // Most recent Rx frame
unsigned int cur_rx, dirty_rx; // The next free ring entry
long last_rx_time; // Last Rx, in ticks, to handle Rx hang
dev_t devno; // Device number
struct dev *dev; // Device block
unsigned short iobase; // Configured I/O base
unsigned short irq; // Configured IRQ
struct interrupt intr; // Interrupt object for driver
struct dpc dpc; // DPC for driver
struct timer timer; // Media selection timer
struct eth_addr hwaddr; // MAC address for NIC
unsigned int trans_start;
struct stats_nic stats;
struct speedo_stats lstats;
int alloc_failures;
struct board *board;
int flags;
int mc_setup_frm_len; // The length of an allocated...
struct descriptor *mc_setup_frm; // ... multicast setup frame
int mc_setup_busy; // Avoid double-use of setup frame
char rx_mode; // Current PROMISC/ALLMULTI setting
int tx_full; // The Tx queue is full
int full_duplex; // Full-duplex operation requested
int flow_ctrl; // Use 802.3x flow control
int rx_bug; // Work around receiver hang errata
int rx_bug10; // Receiver might hang at 10mbps
int rx_bug100; // Receiver might hang at 100mbps
int polling; // Hardware blocked interrupt line
int medialock; // The media speed/duplex is fixed
unsigned short phy[2]; // PHY media interfaces available
unsigned short advertising; // Current PHY advertised caps
unsigned short partner; // Link partner caps
long last_reset;
};
// 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
const unsigned char i82557_config_cmd[22] = {
22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, // 1=Use MII 0=Use AUI
0, 0x2E, 0, 0x60, 0,
0xf2, 0x48, 0, 0x40, 0xf2, 0x80, // 0x40=Force full-duplex
0x3f, 0x05,
};
const unsigned char i82558_config_cmd[22] = {
22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, // 1=Use MII 0=Use AUI
0, 0x2E, 0, 0x60, 0x08, 0x88,
0x68, 0, 0x40, 0xf2, 0xBD, // 0xBD->0xFD=Force full-duplex
0x31, 0x05,
};
// PHY media interface chips
static const char *phys[] = {
"None", "i82553-A/B", "i82553-C", "i82503",
"DP83840", "80c240", "80c24", "i82555",
"unknown-8", "unknown-9", "DP83840A", "unknown-11",
"unknown-12", "unknown-13", "unknown-14", "unknown-15"
};
enum phy_chips {
NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, S80C24, I82555, DP83840A=10
};
static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
#define clear_suspend(cmd) ((char *)(&(cmd)->cmd_status))[3] &= ~0x40
#define EE_READ_CMD (6)
// How to wait for the command unit to accept a command.
// Typically this takes 0 ticks
__inline static void wait_for_cmd_done(long cmd_ioaddr) {
int wait = 0;
int delayed_cmd;
while (++wait <= 100) {
if (inp(cmd_ioaddr) == 0) return;
}
delayed_cmd = inp(cmd_ioaddr);
while (++wait <= 10000) {
if (inp(cmd_ioaddr) == 0) break;
}
kprintf(KERN_WARNING "eepro100: Command %2.2x was not immediately accepted, %d ticks!\n", delayed_cmd, wait);
}
// Serial EEPROM section.
// A "bit" grungy, but we work our way through bit-by-bit :->.
// EEPROM_Ctrl bits
#define EE_SHIFT_CLK 0x01 // EEPROM shift clock.
#define EE_CS 0x02 // EEPROM chip select.
#define EE_DATA_WRITE 0x04 // EEPROM chip data in.
#define EE_DATA_READ 0x08 // EEPROM chip data out.
#define EE_ENB (0x4800 | EE_CS)
#define EE_WRITE_0 0x4802
#define EE_WRITE_1 0x4806
#define EE_OFFSET SCBeeprom
// Delay between EEPROM clock transitions.
// The code works with no delay on 33Mhz PCI
#define eeprom_delay(ee_addr) inpw(ee_addr)
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) {
unsigned retval = 0;
long ee_addr = ioaddr + SCBeeprom;
outpw(ee_addr, EE_ENB | EE_SHIFT_CLK);
// Shift the command bits out
do {
short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
outpw(ee_addr, dataval);
eeprom_delay(ee_addr);
outpw(ee_addr, dataval | EE_SHIFT_CLK);
eeprom_delay(ee_addr);
retval = (retval << 1) | ((inpw(ee_addr) & EE_DATA_READ) ? 1 : 0);
} while (--cmd_len >= 0);
outpw(ee_addr, EE_ENB);
// Terminate the EEPROM access.
outpw(ee_addr, EE_ENB & ~EE_CS);
return retval;
}
static int mdio_read(long ioaddr, int phy_id, int location) {
int val, boguscnt = 64 * 10; // <64 usec. to complete, typ 27 ticks
outpd(ioaddr + SCBCtrlMDI, 0x08000000 | (location << 16) | (phy_id << 21));
do {
val = inpd(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
kprintf(KERN_WARNING "eepro100: mdio_read() timed out with val = %8.8x\n", val);
break;
}
} while (!(val & 0x10000000));
return val & 0xffff;
}
static int mdio_write(long ioaddr, int phy_id, int location, int value) {
int val, boguscnt = 64 * 10; // <64 usec. to complete, typ 27 ticks
outpd(ioaddr + SCBCtrlMDI, 0x04000000 | (location << 16) | (phy_id << 21) | value);
do {
val = inpd(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
kprintf(KERN_WARNING "eepro100: mdio_write() timed out with val = %8.8x\n", val);
break;
}
} while (!(val & 0x10000000));
return val & 0xffff;
}
// Perform a SCB command known to be slow.
// This function checks the status both before and after command execution.
static void do_slow_command(struct dev *dev, int cmd) {
struct nic *sp = (struct nic *) dev->privdata;
long cmd_ioaddr = sp->iobase + SCBCmd;
int wait = 0;
while (++wait <= 200) {
if (inp(cmd_ioaddr) == 0) break;
}
if (wait > 100) kprintf(KERN_WARNING "%s: Command %4.4x was never accepted (%d polls)!\n", dev->name, inp(cmd_ioaddr), wait);
outp(cmd_ioaddr, cmd);
for (wait = 0; wait <= 100; wait++) {
if (inp(cmd_ioaddr) == 0) return;
}
for (; wait <= 20000; wait++) {
if (inp(cmd_ioaddr) == 0) {
return;
} else {
udelay(1);
}
}
kprintf(KERN_WARNING "%s: Command %4.4x was not accepted after %d polls! Current status %8.8x\n", dev->name, cmd, wait, inpd(sp->iobase + SCBStatus));
}
static int speedo_set_rx_mode(struct dev *dev);
static void speedo_resume(struct dev *dev);
static void speedo_timer(void *arg);
static void speedo_interrupt(struct dev *dev);
static void speedo_tx_timeout(struct dev *dev);
static void speedo_show_state(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
int phy_num = sp->phy[0] & 0x1f;
unsigned int i;
// Print a few items for debugging
kprintf("%s: Tx ring dump, Tx queue %d / %d:\n", dev->name, sp->cur_tx, sp->dirty_tx);
for (i = 0; i < TX_RING_SIZE; i++) {
kprintf("%s: %c%c%d %8.8x\n", dev->name,
i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ',
i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ',
i, sp->tx_ring[i].status);
}
kprintf("%s: Rx ring dump (next to receive into %d)\n", dev->name, sp->cur_rx);
for (i = 0; i < RX_RING_SIZE; i++) {
kprintf(" Rx ring entry %d %8.8x\n", i, sp->rx_ringp[i]->status);
}
for (i = 0; i < 16; i++) {
if (i == 6) i = 21;
kprintf(" PHY index %d register %d is %4.4x.\n", phy_num, i, mdio_read(ioaddr, phy_num, i));
}
}
// Initialize the Rx and Tx rings
static void speedo_init_rx_ring(struct dev *dev)
{
struct nic *sp = (struct nic *) dev->privdata;
struct RxFD *rxf, *last_rxf = NULL;
int i;
init_sem(&sp->tx_sem, TX_QUEUE_LIMIT);
sp->cur_rx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
struct pbuf *p;
p = pbuf_alloc(PBUF_RAW, PKT_BUF_SZ + sizeof(struct RxFD), PBUF_RW);
sp->rx_pbuf[i] = p;
if (p == NULL) break; // OK. Just initially short of Rx bufs
rxf = (struct RxFD *) p->payload;
sp->rx_ringp[i] = rxf;
pbuf_header(p, - (int) sizeof(struct RxFD));
if (last_rxf) last_rxf->link = virt2phys(rxf);
last_rxf = rxf;
rxf->status = 0x00000001; // '1' is flag value only
rxf->link = 0; // None yet
rxf->rx_buf_addr = 0xffffffff;
rxf->count = PKT_BUF_SZ << 16;
}
sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
// Mark the last entry as end-of-list.
last_rxf->status = 0xC0000002; // '2' is flag value only
sp->last_rxf = last_rxf;
}
static int speedo_open(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
//kprintf("%s: speedo_open() irq %d.\n", dev->name, sp->irq);
// Set up the Tx queue early
sp->cur_tx = 0;
sp->dirty_tx = 0;
sp->last_cmd = 0;
sp->tx_full = 0;
sp->polling = 0;
if ((sp->phy[0] & 0x8000) == 0) {
sp->advertising = mdio_read(ioaddr, sp->phy[0] & 0x1f, 4);
}
// With some transceivers we must retrigger negotiation to reset
// power-up errors
if ((sp->flags & ResetMII) && (sp->phy[0] & 0x8000) == 0) {
int phy_addr = sp->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
mdio_write(ioaddr, phy_addr, 0, 0x3300);
}
// We can safely take handler calls during init
// Doing this after speedo_init_rx_ring() results in a memory leak
enable_irq(sp->irq);
// Initialize Rx and Tx rings
speedo_init_rx_ring(dev);
// Fire up the hardware
speedo_resume(dev);
// Setup the chip and configure the multicast list
sp->mc_setup_frm = NULL;
sp->mc_setup_frm_len = 0;
sp->mc_setup_busy = 0;
sp->rx_mode = -1; // Invalid -> always reset the mode.
sp->flow_ctrl = sp->partner = 0;
speedo_set_rx_mode(dev);
//kprintf("%s: Done speedo_open(), status %8.8x\n", dev->name, inpw(ioaddr + SCBStatus));
// 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(&sp->timer, speedo_timer, dev);
mod_timer(&sp->timer, get_ticks() + 3*HZ);
// No need to wait for the command unit to accept here.
if ((sp->phy[0] & 0x8000) == 0) mdio_read(ioaddr, sp->phy[0] & 0x1f, 0);
return 0;
}
static int speedo_close(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
int i;
kprintf("%s: Shutting down ethercard, status was %4.4x. Allocation failures: %d\n", dev->name, inpw(ioaddr + SCBStatus), sp->alloc_failures);
// Shut off the media monitoring timer
del_timer(&sp->timer);
// Shutting down the chip nicely fails to disable flow control. So..
outpd(ioaddr + SCBPort, PortPartialReset);
disable_irq(sp->irq);
// Free all the pbufs in the Rx and Tx queues
for (i = 0; i < RX_RING_SIZE; i++) {
struct pbuf *p = sp->rx_pbuf[i];
sp->rx_pbuf[i] = NULL;
if (p) pbuf_free(p);
}
for (i = 0; i < TX_RING_SIZE; i++) {
struct pbuf *p = sp->tx_pbuf[i];
sp->tx_pbuf[i] = NULL;
if (p) pbuf_free(p);
}
if (sp->mc_setup_frm) {
kfree(sp->mc_setup_frm);
sp->mc_setup_frm_len = 0;
}
// Print a few items for debugging
speedo_show_state(dev);
return 0;
}
// Start the chip hardware after a full reset
static void speedo_resume(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
outpw(ioaddr + SCBCmd, SCBMaskAll);
// Start with a Tx threshold of 256 (0x..20.... 8 byte units)
sp->tx_threshold = 0x01208000;
// Set the segment registers to '0'
wait_for_cmd_done(ioaddr + SCBCmd);
if (inp(ioaddr + SCBCmd)) {
outpd(ioaddr + SCBPort, PortPartialReset);
udelay(10);
}
outpd(ioaddr + SCBPointer, 0);
inpd(ioaddr + SCBPointer); // Flush to PCI
udelay(10); // Bogus, but it avoids the bug
// Note: these next two operations can take a while
do_slow_command(dev, RxAddrLoad);
do_slow_command(dev, CUCmdBase);
// Load the statistics block and rx ring addresses
outpd(ioaddr + SCBPointer, virt2phys(&sp->lstats));
inpd(ioaddr + SCBPointer); // Flush to PCI
outp(ioaddr + SCBCmd, CUStatsAddr);
sp->lstats.done_marker = 0;
wait_for_cmd_done(ioaddr + SCBCmd);
outpd(ioaddr + SCBPointer, virt2phys(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]));
inpd(ioaddr + SCBPointer); // Flush to PCI
// Note: RxStart should complete instantly.
do_slow_command(dev, RxStart);
do_slow_command(dev, CUDumpStats);
// Fill the first command with our physical address
{
int entry = sp->cur_tx++ % TX_RING_SIZE;
struct descriptor *cur_cmd = (struct descriptor *) &sp->tx_ring[entry];
// Avoid a bug(?!) here by marking the command already completed
cur_cmd->cmd_status = (CmdSuspend | CmdIASetup) | 0xa000;
cur_cmd->link = virt2phys(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
memcpy(cur_cmd->params, sp->hwaddr.addr, 6);
if (sp->last_cmd) clear_suspend(sp->last_cmd);
sp->last_cmd = cur_cmd;
}
// Start the chip's Tx process and unmask interrupts
outpd(ioaddr + SCBPointer, virt2phys(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]));
outpw(ioaddr + SCBCmd, CUStart);
}
//
// 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!
//
static struct stats_nic *speedo_get_stats(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
// Update only if the previous dump finished.
if (sp->lstats.done_marker == 0xA007) {
sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
//sp->stats.tx_deferred += sp->lstats.tx_deferred;
sp->stats.collisions += sp->lstats.tx_total_colls;
sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
sp->lstats.done_marker = 0x0000;
wait_for_cmd_done(ioaddr + SCBCmd);
outp(ioaddr + SCBCmd, CUDumpStats);
}
return &sp->stats;
}
// 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.
static int speedo_set_rx_mode(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
struct descriptor *last_cmd;
char new_rx_mode;
int entry, i;
if (!dev->netif) {
// Network interface not attached yet -- accept all multicasts
new_rx_mode = 1;
} else if (dev->netif->flags & NETIF_PROMISC) {
// Set promiscuous.
new_rx_mode = 3;
} else if ((dev->netif->flags & NETIF_ALLMULTI) || dev->netif->mccount > multicast_filter_limit) {
new_rx_mode = 1;
} else {
new_rx_mode = 0;
}
if (sp->cur_tx - sp->dirty_tx >= 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.
sp->rx_mode = -1;
return -EBUSY;
}
if (new_rx_mode != sp->rx_mode) {
unsigned char *config_cmd_data;
cli();
entry = sp->cur_tx % TX_RING_SIZE;
last_cmd = sp->last_cmd;
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
sp->tx_pbuf[entry] = 0; // Redundant
sp->tx_ring[entry].status = CmdSuspend | CmdConfigure;
sp->cur_tx++;
sp->tx_ring[entry].link = virt2phys(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
// We may nominally release the lock here
config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
// Construct a full CmdConfig frame
memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd));
config_cmd_data[1] = (txfifo << 4) | rxfifo;
config_cmd_data[4] = rxdmacount;
config_cmd_data[5] = txdmacount + 0x80;
if (sp->flags & HasChksum) config_cmd_data[9] |= 1;
config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
config_cmd_data[19] = sp->flow_ctrl ? 0xBD : 0x80;
config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0;
config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
if (sp->phy[0] & 0x8000) {
// Use the AUI port instead.
config_cmd_data[15] |= 0x80;
config_cmd_data[8] = 0;
}
// Trigger the command unit resume.
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
outp(ioaddr + SCBCmd, CUResume);
sti();
sp->last_cmd_time = get_ticks();
}
if (new_rx_mode == 0 && dev->netif->mccount < 4) {
// The simple case of 0-3 multicast list entries occurs often, and
// fits within one tx_ring[] entry.
struct mclist *mclist;
unsigned short *setup_params, *eaddrs;
cli();
entry = sp->cur_tx % TX_RING_SIZE;
last_cmd = sp->last_cmd;
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
sp->tx_pbuf[entry] = 0;
sp->tx_ring[entry].status = CmdSuspend | CmdMulticastList;
sp->cur_tx++;
sp->tx_ring[entry].link = virt2phys(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
// We may nominally release the lock here
sp->tx_ring[entry].tx_desc_addr = 0; // Really MC list count
setup_params = (unsigned short *) &sp->tx_ring[entry].tx_desc_addr;
*setup_params++ = dev->netif->mccount * 6;
// Fill in the multicast addresses.
for (i = 0, mclist = dev->netif->mclist; i < dev->netif->mccount; i++, mclist = mclist->next) {
eaddrs = (unsigned short *) &mclist->hwaddr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
}
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
// Immediately trigger the command unit resume
outp(ioaddr + SCBCmd, CUResume);
sti();
sp->last_cmd_time = get_ticks();
} else if (new_rx_mode == 0) {
struct mclist *mclist;
unsigned short *setup_params, *eaddrs;
struct descriptor *mc_setup_frm = sp->mc_setup_frm;
int i;
if (sp->mc_setup_frm_len < 10 + dev->netif->mccount * 6 || sp->mc_setup_frm == NULL) {
// Allocate a full setup frame, 10bytes + <max addrs>.
if (sp->mc_setup_frm) kfree(sp->mc_setup_frm);
sp->mc_setup_busy = 0;
sp->mc_setup_frm_len = 10 + multicast_filter_limit*6;
sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len);
if (sp->mc_setup_frm == NULL) {
kprintf(KERN_WARNING "%s: Failed to allocate a setup frame\n", dev->name);
sp->rx_mode = -1; // We failed, try again.
return -ENOMEM;
}
}
// If we are busy, someone might be quickly adding to the MC list.
// Try again later when the list updates stop.
if (sp->mc_setup_busy) {
sp->rx_mode = -1;
return -EBUSY;
}
mc_setup_frm = sp->mc_setup_frm;
// Fill the setup frame
kprintf("%s: Constructing a setup frame at %p, %d bytes\n", dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
mc_setup_frm->cmd_status = CmdSuspend | CmdIntr | CmdMulticastList;
// Link set below
setup_params = (unsigned short *) &mc_setup_frm->params;
*setup_params++ = dev->netif->mccount * 6;
// Fill in the multicast addresses
for (i = 0, mclist = dev->netif->mclist; i < dev->netif->mccount; i++, mclist = mclist->next) {
eaddrs = (unsigned short *) &mclist->hwaddr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
}
// Disable interrupts while playing with the Tx Cmd list
cli();
entry = sp->cur_tx % TX_RING_SIZE;
last_cmd = sp->last_cmd;
sp->last_cmd = mc_setup_frm;
sp->mc_setup_busy++;
// Change the command to a NoOp, pointing to the CmdMulti command
sp->tx_pbuf[entry] = 0;
sp->tx_ring[entry].status = CmdNOp;
sp->cur_tx++;
sp->tx_ring[entry].link = virt2phys(mc_setup_frm);
// We may nominally release the lock here
// Set the link in the setup frame.
mc_setup_frm->link = virt2phys(&(sp->tx_ring[(entry+1) % TX_RING_SIZE]));
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
// Immediately trigger the command unit resume
outp(ioaddr + SCBCmd, CUResume);
sti();
sp->last_cmd_time = get_ticks();
kprintf("%s: CmdMCSetup frame length %d in entry %d\n", dev->name, dev->netif->mccount, entry);
}
sp->rx_mode = new_rx_mode;
return 0;
}
// Media monitoring and control
static void speedo_timer(void *arg) {
struct dev *dev = (struct dev *) arg;
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
int phy_num = sp->phy[0] & 0x1f;
int status = inpw(ioaddr + SCBStatus);
unsigned int expires;
//kprintf("%s: Interface monitor tick, chip status %4.4x\n", dev->name, status);
// Normally we check every two seconds.
expires = get_ticks() + 2*HZ;
if (sp->polling) {
// Continue to be annoying.
if (status & 0xfc00) {
speedo_interrupt(dev);
if (get_ticks() - sp->last_reset > 10*HZ) {
kprintf(KERN_WARNING "%s: IRQ %d is still blocked!\n", dev->name, sp->irq);
sp->last_reset = get_ticks();
}
} else if (get_ticks() - sp->last_reset > 10*HZ) {
sp->polling = 0;
}
expires = get_ticks() + 2;
}
// We have MII and lost link beat
if ((sp->phy[0] & 0x8000) == 0) {
int partner = mdio_read(ioaddr, phy_num, 5);
if (partner != sp->partner) {
int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0;
sp->partner = partner;
if (flow_ctrl != sp->flow_ctrl) {
sp->flow_ctrl = flow_ctrl;
sp->rx_mode = -1; // Trigger a reload
}
// Clear sticky bit
mdio_read(ioaddr, phy_num, 1);
// If link beat has returned...
//if (mdio_read(ioaddr, phy_num, 1) & 0x0004)
// netif_link_up(dev);
//else
// netif_link_down(dev);
}
}
// This no longer has a false-trigger window
if (sp->cur_tx - sp->dirty_tx > 1 && (get_ticks() - sp->trans_start) > TX_TIMEOUT && (get_ticks() - sp->last_cmd_time) > TX_TIMEOUT) {
if (status == 0xffff) {
if (get_ticks() - sp->last_reset > 10*HZ) {
sp->last_reset = get_ticks();
kprintf(KERN_WARNING "%s: The EEPro100 chip is missing!\n", dev->name);
}
} else if (status & 0xfc00) {
// We have a blocked IRQ line. This should never happen, but we recover as best we can
if (!sp->polling) {
if (get_ticks() - sp->last_reset > 10*HZ) {
kprintf(KERN_WARNING "%s: IRQ %d is physically blocked! (%4.4x) Failing back to low-rate polling\n", dev->name, sp->irq, status);
sp->last_reset = get_ticks();
}
sp->polling = 1;
}
speedo_interrupt(dev);
expires = get_ticks() + 2; // Avoid
} else {
speedo_tx_timeout(dev);
sp->last_reset = get_ticks();
}
}
if (sp->rx_mode < 0 || (sp->rx_bug && get_ticks() - sp->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.
speedo_set_rx_mode(dev);
}
mod_timer(&sp->timer, expires);
}
static void speedo_tx_timeout(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
int status = inpw(ioaddr + SCBStatus);
kprintf(KERN_WARNING "%s: Transmit timed out: status %4.4x %4.4x at %d/%d commands %8.8x %8.8x %8.8x\n",
dev->name, status, inpw(ioaddr + SCBCmd),
sp->dirty_tx, sp->cur_tx,
sp->tx_ring[(sp->dirty_tx+0) % TX_RING_SIZE].status,
sp->tx_ring[(sp->dirty_tx+1) % TX_RING_SIZE].status,
sp->tx_ring[(sp->dirty_tx+2) % TX_RING_SIZE].status);
// Trigger a stats dump to give time before the reset
speedo_get_stats(dev);
speedo_show_state(dev);
kprintf(KERN_INFO "%s: Restarting the chip...\n", dev->name);
// Reset the Tx and Rx units.
outpd(ioaddr + SCBPort, PortReset);
speedo_show_state(dev);
udelay(10);
speedo_resume(dev);
// Reset the MII transceiver, suggested by Fred Young @ scalable.com
if ((sp->phy[0] & 0x8000) == 0) {
int phy_addr = sp->phy[0] & 0x1f;
int advertising = mdio_read(ioaddr, phy_addr, 4);
int mii_bmcr = mdio_read(ioaddr, phy_addr, 0);
mdio_write(ioaddr, phy_addr, 0, 0x0400);
mdio_write(ioaddr, phy_addr, 1, 0x0000);
mdio_write(ioaddr, phy_addr, 4, 0x0000);
mdio_write(ioaddr, phy_addr, 0, 0x8000);
mdio_read(ioaddr, phy_addr, 0);
mdio_write(ioaddr, phy_addr, 0, mii_bmcr);
mdio_write(ioaddr, phy_addr, 4, advertising);
}
sp->stats.tx_errors++;
sp->trans_start = get_ticks();
}
// Handle the interrupt cases when something unexpected happens
static void speedo_intr_error(struct dev *dev, int intr_status) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
if (intr_status & IntrRxSuspend) {
if ((intr_status & 0x003c) == 0x0028)
// No more Rx buffers
outp(ioaddr + SCBCmd, RxResumeNoResources);
else if ((intr_status & 0x003c) == 0x0008) {
// No resources (why?!)
kprintf(KERN_ERR "%s: Unknown receiver error, status=%#4.4x\n", dev->name, intr_status);
// No idea of what went wrong. Restart the receiver
outpd(ioaddr + SCBPointer, virt2phys(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]));
outp(ioaddr + SCBCmd, RxStart);
}
sp->stats.rx_errors++;
}
}
static int speedo_rx(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
int entry = sp->cur_rx % RX_RING_SIZE;
int status;
int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
//kprintf("%s: In speedo_rx()\n", dev->name);
// If we own the next entry, it's a new packet. Send it up.
while (sp->rx_ringp[entry] != NULL && (status = sp->rx_ringp[entry]->status) & RxComplete) {
int desc_count = sp->rx_ringp[entry]->count;
int pkt_len = desc_count & 0x07ff;
if (--rx_work_limit < 0) break;
//kprintf("%s: speedo_rx() status %8.8x len %d\n", dev->name, status, pkt_len);
if ((status & (RxErrTooBig | RxOK | 0x0f90)) != RxOK) {
if (status & RxErrTooBig) {
kprintf("KERN_WARNING %s: Ethernet frame overran the Rx buffer, status %8.8x!\n", dev->name, status);
} else if (!(status & RxOK)) {
// There was a fatal error. This *should* be impossible.
sp->stats.rx_errors++;
kprintf(KERN_WARNING "%s: Anomalous event in speedo_rx(), status %8.8x\n", dev->name, status);
}
} else {
struct pbuf *p;
if (sp->flags & HasChksum) pkt_len -= 2;
// Check if the packet is long enough to just accept without
// copying to a properly sized packet buffer
if (pkt_len < rx_copybreak && (p = pbuf_alloc(PBUF_RAW, pkt_len, PBUF_RW)) != NULL) {
memcpy(p->payload, sp->rx_pbuf[entry]->payload, pkt_len);
} else {
// Pass up the already-filled pbuf
p = sp->rx_pbuf[entry];
if (p == NULL) {
kprintf(KERN_WARNING "%s: Inconsistent Rx descriptor chain\n", dev->name);
break;
}
sp->rx_pbuf[entry] = NULL;
sp->rx_ringp[entry] = NULL;
pbuf_realloc(p, pkt_len);
}
// Send packet to upper layer
if (dev_receive(sp->devno, p) < 0) pbuf_free(p);
sp->stats.rx_packets++;
sp->stats.rx_bytes += pkt_len;
}
entry = (++sp->cur_rx) % RX_RING_SIZE;
}
// Refill the Rx ring buffers
for (; sp->cur_rx - sp->dirty_rx > 0; sp->dirty_rx++) {
struct RxFD *rxf;
entry = sp->dirty_rx % RX_RING_SIZE;
if (sp->rx_pbuf[entry] == NULL) {
struct pbuf *p;
// Get a fresh pbuf to replace the consumed one
p = pbuf_alloc(PBUF_RAW, PKT_BUF_SZ + sizeof(struct RxFD), PBUF_RW);
sp->rx_pbuf[entry] = p;
if (p == NULL) {
sp->rx_ringp[entry] = NULL;
sp->alloc_failures++;
break; // Better luck next time!
}
rxf = sp->rx_ringp[entry] = (struct RxFD *) p->payload;
pbuf_header(p, - (int) sizeof(struct RxFD));
rxf->rx_buf_addr = virt2phys(p->payload);
} else {
rxf = sp->rx_ringp[entry];
}
rxf->status = 0xC0000001; // '1' for driver use only
rxf->link = 0; // None yet
rxf->count = PKT_BUF_SZ << 16;
sp->last_rxf->link = virt2phys(rxf);
sp->last_rxf->status &= ~0xC0000000;
sp->last_rxf = rxf;
}
sp->last_rx_time = get_ticks();
return 0;
}
// The interrupt handler does all of the Rx thread work and cleans up
// after the Tx thread.
static void speedo_interrupt(struct dev *dev) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
long boguscnt = max_interrupt_work;
unsigned short status;
while (1) {
status = inpw(ioaddr + SCBStatus);
if ((status & IntrAllNormal) == 0 || status == 0xffff) break;
// Acknowledge all of the current interrupt sources ASAP
outpw(ioaddr + SCBStatus, status & IntrAllNormal);
//kprintf("%s: interrupt status=%#4.4x\n", dev->name, status);
if (status & (IntrRxDone | IntrRxSuspend)) {
speedo_rx(dev);
}
// The command unit did something, scavenge finished Tx entries
if (status & (IntrCmdDone | IntrCmdIdle | IntrDrvrIntr)) {
unsigned int dirty_tx;
int entries_freed = 0;
dirty_tx = sp->dirty_tx;
while (sp->cur_tx - dirty_tx > 0) {
int entry = dirty_tx % TX_RING_SIZE;
int status = sp->tx_ring[entry].status;
//kprintf("%s: scavenge candidate %d status %4.4x\n", dev->name, entry, status);
if ((status & StatusComplete) == 0) {
// Special case error check: look for descriptor that the chip skipped(?)
if (sp->cur_tx - dirty_tx > 2 && (sp->tx_ring[(dirty_tx + 1) % TX_RING_SIZE].status & StatusComplete)) {
kprintf(KERN_ERR "%s: Command unit failed to mark command %8.8x as complete at %d\n", dev->name, status, dirty_tx);
} else {
// It still hasn't been processed
break;
}
}
if ((status & TxUnderrun) && (sp->tx_threshold < 0x01e08000)) {
sp->tx_threshold += 0x00040000;
kprintf("%s: Tx threshold increased, %#8.8x\n", dev->name, sp->tx_threshold);
}
// Free the original pbuf
if (sp->tx_pbuf[entry]) {
// Count only user packets
sp->stats.tx_packets++;
sp->stats.tx_bytes += sp->tx_pbuf[entry]->len;
pbuf_free(sp->tx_pbuf[entry]);
sp->tx_pbuf[entry] = NULL;
} else if ((status & 0x70000) == CmdNOp) {
sp->mc_setup_busy = 0;
}
dirty_tx++;
entries_freed++;
}
sp->dirty_tx = dirty_tx;
release_sem(&sp->tx_sem, entries_freed);
}
if (status & IntrRxSuspend) {
speedo_intr_error(dev, status);
}
if (--boguscnt < 0) {
kprintf("%s: Too much work at interrupt, status=0x%4.4x\n", dev->name, status);
// Clear all interrupt sources.
outpd(ioaddr + SCBStatus, 0xfc00);
break;
}
}
//kprintf("%s: exiting interrupt, status=%#4.4x\n", dev->name, inpw(ioaddr + SCBStatus));
}
static void speedo_dpc(void *arg) {
struct dev *dev = (struct dev *) arg;
struct nic *sp = (struct nic *) dev->privdata;
speedo_interrupt(dev);
eoi(sp->irq);
}
static int speedo_handler(struct context *ctxt, void *arg) {
struct dev *dev = (struct dev *) arg;
struct nic *sp = (struct nic *) dev->privdata;
// Queue DPC to service interrupt
//kprintf("%s: interrupt\n", dev->name);
queue_irq_dpc(&sp->dpc, speedo_dpc, dev);
return 0;
}
static int speedo_transmit(struct dev *dev, struct pbuf *p) {
struct nic *sp = (struct nic *) dev->privdata;
long ioaddr = sp->iobase;
int entry;
p = pbuf_linearize(PBUF_RAW, p);
if (!p) return -ENOMEM;
// Wait for free entry in transmit ring
if (wait_for_object(&sp->tx_sem, TX_TIMEOUT) < 0) {
kprintf("%s: transmit timeout, drop packet\n", dev->name);
sp->stats.tx_dropped++;
return -ETIMEOUT;
}
// Caution: the write order is important here, set the base address
// with the "ownership" bits last.
// Calculate the Tx descriptor entry
entry = sp->cur_tx % TX_RING_SIZE;
sp->tx_pbuf[entry] = p;
// TODO: be a little more clever about setting the interrupt bit
sp->tx_ring[entry].status = CmdSuspend | CmdTx | CmdTxFlex;
sp->cur_tx++;
sp->tx_ring[entry].link = virt2phys(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
sp->tx_ring[entry].tx_desc_addr = virt2phys(&sp->tx_ring[entry].tx_buf_addr0);
// The data region is always in one buffer descriptor
sp->tx_ring[entry].count = sp->tx_threshold;
sp->tx_ring[entry].tx_buf_addr0 = virt2phys(p->payload);
sp->tx_ring[entry].tx_buf_size0 = p->tot_len;
// 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.
{
struct descriptor *last_cmd = sp->last_cmd;
sp->last_cmd = (struct descriptor *) &sp->tx_ring[entry];
clear_suspend(last_cmd);
}
wait_for_cmd_done(ioaddr + SCBCmd);
outp(ioaddr + SCBCmd, CUResume);
sp->trans_start = get_ticks();
return 0;
}
static int speedo_ioctl(struct dev *dev, int cmd, void *args, size_t size) {
return -ENOSYS;
}
static int speedo_attach(struct dev *dev, struct eth_addr *hwaddr) {
struct nic *np = dev->privdata;
*hwaddr = np->hwaddr;
return 0;
}
static int speedo_detach(struct dev *dev) {
return 0;
}
struct driver speedo_driver = {
"eepro100",
DEV_TYPE_PACKET,
speedo_ioctl,
NULL,
NULL,
speedo_attach,
speedo_detach,
speedo_transmit,
speedo_set_rx_mode,
};
int __declspec(dllexport) install(struct unit *unit, char *opts) {
int i;
struct board *board;
struct nic *sp;
struct dev *dev;
unsigned short ioaddr;
unsigned short irq;
unsigned short eeprom[0x100];
// Check license
if (license() != LICENSE_GPL) kprintf(KERN_WARNING "notice: eepro100 driver is under GPL license\n");
// Determine NIC type
board = lookup_board(board_tbl, unit);
if (!board) return -EIO;
unit->vendorname = board->vendorname;
unit->productname = board->productname;
// Get NIC PCI configuration
ioaddr = (unsigned short) get_unit_iobase(unit);
irq = irq = (unsigned short) get_unit_irq(unit);
// Enable bus mastering
pci_enable_busmastering(unit);
// Allocate private memory
sp = kmalloc(sizeof(struct nic));
if (sp == NULL) return -ENOMEM;
memset(sp, 0, sizeof(struct nic));
// 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.
{
unsigned short sum = 0;
int j;
int read_cmd, ee_size;
if ((do_eeprom_cmd(ioaddr, EE_READ_CMD << 24, 27) & 0xffe0000) == 0xffe0000) {
ee_size = 0x100;
read_cmd = EE_READ_CMD << 24;
} else {
ee_size = 0x40;
read_cmd = EE_READ_CMD << 22;
}
for (j = 0, i = 0; i < ee_size; i++) {
unsigned short value = do_eeprom_cmd(ioaddr, read_cmd | (i << 16), 27);
eeprom[i] = value;
sum += value;
if (i < 3) {
sp->hwaddr.addr[j++] = (unsigned char) (value % 0xFF);
sp->hwaddr.addr[j++] = (unsigned char) (value >> 8);
}
}
if (sum != 0xBABA) kprintf(KERN_WARNING "eepro100: Invalid EEPROM checksum %#4.4x!\n", sum);
}
// Reset the chip: stop Tx and Rx processes and clear counters.
// This takes less than 10usec and will easily finish before the next
// action.
outpd(ioaddr + SCBPort, PortReset);
// Create new device
sp->devno = dev_make("eth#", &speedo_driver, unit, sp);
if (sp->devno == NODEV) return -ENODEV;
dev = device(sp->devno);
init_dpc(&sp->dpc);
register_interrupt(&sp->intr, IRQ2INTR(irq), speedo_handler, dev);
sp->dev = dev;
sp->iobase = ioaddr;
sp->irq = irq;
sp->board = board;
sp->flags = board->flags;
// Set options
if (opts) {
sp->full_duplex = get_num_option(opts, "fullduplex", 0);
}
if (sp->full_duplex) sp->medialock = 1;
sp->phy[0] = eeprom[6];
sp->phy[1] = eeprom[7];
sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
if (sp->rx_bug) kprintf(KERN_INFO "%s: Receiver lock-up workaround activated\n", dev->name);
kprintf(KERN_INFO "%s: %s%s iobase 0x%x irq %d mac %la\n", dev->name, eeprom[3] & 0x0100 ? "OEM " : "", unit->productname, ioaddr, irq, &sp->hwaddr);
return speedo_open(dev);
}
int __stdcall start(hmodule_t hmod, int reason, void *reserved2) {
return 1;
}