Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/ether82557.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/ether82557.c 1996/0418
## diff -e /dev/null /n/fornaxdump/1996/0418/sys/src/brazil/pc/ether82557.c
0a
/*
 * Intel 82557 Fast Ethernet PCI Bus LAN Controller
 * as found on the Intel EtherExpress PRO/100B. This chip is full
 * of smarts, unfortunately none of them are in the right place.
 * To do:
 *	the PCI scanning code could be made common to other adapters;
 *	PCI code needs rewritten to handle byte, word, dword accesses
 *	  and using the devno as a bus+dev+function triplet;
 *	stats.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

enum {
	Nrfd		= 64,		/* receive frame area */

	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */
};

enum {					/* CSR */
	Status		= 0x00,		/* byte or word (word includes Ack) */
	Ack		= 0x01,		/* byte */
	Command		= 0x02,		/* byte or word (word includes Interrupt) */
	Interrupt	= 0x03,		/* byte */
	Pointer		= 0x04,		/* dword */
	Port		= 0x08,		/* dword */
	Fcr		= 0x0C,		/* Flash control register */
	Ecr		= 0x0E,		/* EEPROM control register */
	Mcr		= 0x10,		/* MDI control register */
};

enum {					/* Status */
	RUidle		= 0x0000,
	RUsuspended	= 0x0004,
	RUnoresources	= 0x0008,
	RUready		= 0x0010,
	RUrbd		= 0x0020,	/* bit */
	RUstatus	= 0x003F,	/* mask */

	CUidle		= 0x0000,
	CUsuspended	= 0x0040,
	CUactive	= 0x0080,
	CUstatus	= 0x00C0,	/* mask */

	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */
	StatMDI		= 0x0800,	/* MDI r/w done */
	StatRNR		= 0x1000,	/* Receive unit Not Ready */
	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */
	StatFR		= 0x4000,	/* Finished Receiving */
	StatCX		= 0x8000,	/* Command eXecuted */
	StatTNO		= 0x8000,	/* Transmit NOT OK */
};

enum {					/* Command (byte) */
	CUnop		= 0x00,
	CUstart		= 0x10,
	CUresume	= 0x20,
	LoadDCA		= 0x40,		/* Load Dump Counters Address */
	DumpSC		= 0x50,		/* Dump Statistical Counters */
	LoadCUB		= 0x60,		/* Load CU Base */
	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */

	RUstart		= 0x01,
	RUresume	= 0x02,
	RUabort		= 0x04,
	LoadHDS		= 0x05,		/* Load Header Data Size */
	LoadRUB		= 0x06,		/* Load RU Base */
	RBDresume	= 0x07,		/* Resume frame reception */
};

enum {					/* Interrupt (byte) */
	InterruptM	= 0x01,		/* interrupt Mask */
	InterruptSI	= 0x02,		/* Software generated Interrupt */
};

enum {					/* Ecr */
	EEsk		= 0x01,		/* serial clock */
	EEcs		= 0x02,		/* chip select */
	EEdi		= 0x04,		/* serial data in */
	EEdo		= 0x08,		/* serial data out */

	EEstart		= 0x04,		/* start bit */
	EEread		= 0x02,		/* read opcode */

	EEaddrsz	= 6,		/* bits of address */
};

enum {					/* Mcr */
	MDIread		= 0x08000000,	/* read opcode */
	MDIwrite	= 0x04000000,	/* write opcode */
	MDIready	= 0x10000000,	/* ready bit */
	MDIie		= 0x20000000,	/* interrupt enable */
};

typedef struct Rfd {
	int	field;
	ulong	link;
	ulong	rbd;
	ushort	count;
	ushort	size;

	Etherpkt;

	Block*	bp;
} Rfd;

enum {					/* field */
	RfdCollision	= 0x00000001,
	RfdIA		= 0x00000002,	/* IA match */
	RfdRxerr	= 0x00000010,	/* PHY character error */
	RfdType		= 0x00000020,	/* Type frame */
	RfdRunt		= 0x00000080,
	RfdOverrun	= 0x00000100,
	RfdBuffer	= 0x00000200,
	RfdAlignment	= 0x00000400,
	RfdCRC		= 0x00000800,

	RfdOK		= 0x00002000,	/* frame received OK */
	RfdC		= 0x00008000,	/* reception Complete */
	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */
	RfdH		= 0x00100000,	/* Header RFD */

	RfdI		= 0x20000000,	/* Interrupt after completion */
	RfdS		= 0x40000000,	/* Suspend after completion */
	RfdEL		= 0x80000000,	/* End of List */
};

enum {					/* count */
	RfdF		= 0x00004000,
	RfdEOF		= 0x00008000,
};

typedef struct Cb {
	int	command;
	ulong	link;
	uchar	data[24];	/* CbIAS + CbConfigure */
} Cb;

typedef struct TxCB {
	int	command;
	ulong	link;
	ulong	tbd;
	ushort	count;
	uchar	threshold;
	uchar	number;
} TxCB;

enum {					/* action command */
	CbOK		= 0x00002000,	/* DMA completed OK */
	CbC		= 0x00008000,	/* execution Complete */

	CbNOP		= 0x00000000,
	CbIAS		= 0x00010000,	/* Indvidual Address Setup */
	CbConfigure	= 0x00020000,
	CbMAS		= 0x00030000,	/* Multicast Address Setup */
	CbTransmit	= 0x00040000,
	CbDump		= 0x00060000,
	CbDiagnose	= 0x00070000,
	CbCommand	= 0x00070000,	/* mask */

	CbSF		= 0x00080000,	/* CbTransmit */

	CbI		= 0x20000000,	/* Interrupt after completion */
	CbS		= 0x40000000,	/* Suspend after completion */
	CbEL		= 0x80000000,	/* End of List */
};

enum {					/* CbTransmit count */
	CbEOF		= 0x00008000,
};

typedef struct Ctlr {
	int	port;

	int	ctlrno;
	char*	type;

	uchar	configdata[24];

	Lock	rlock;

	Block*	rfd[Nrfd];
	int	rfdl;
	int	rfdx;

	Lock	cbqlock;
	Block*	cbqhead;
	Block*	cbqtail;
	int	cbqbusy;
} Ctlr;

static uchar configdata[24] = {
	0x16,				/* byte count */
	0x44,				/* Rx/Tx FIFO limit */
	0x00,				/* adaptive IFS */
	0x00,	
	0x04,				/* Rx DMA maximum byte count */
	0x84,				/* Tx DMA maximum byte count */
	0x33,				/* late SCB, CNA interrupts */
	0x01,				/* discard short Rx frames */
	0x00,				/* 503/MII */

	0x00,	
	0x2E,				/* normal operation, NSAI */
	0x00,				/* linear priority */
	0x60,				/* inter-frame spacing */
	0x00,	
	0xF2,	
	0x48,				/* promiscuous mode off */
	0x00,	
	0x40,	
	0xF2,				/* transmit padding enable */
	0x80,				/* full duplex pin enable */
	0x3F,				/* no Multi IA */
	0x05,				/* no Multi Cast ALL */
};

#define csr8r(c, r)	(inb((c)->port+(r)))
#define csr16r(c, r)	(ins((c)->port+(r)))
#define csr32r(c, r)	(inl((c)->port+(r)))
#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))

static void
custart(Ctlr* ctlr)
{
	if(ctlr->cbqhead == 0){
		ctlr->cbqbusy = 0;
		return;
	}
	ctlr->cbqbusy = 1;

	csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp));
	while(csr8r(ctlr, Command))
		;
	csr8w(ctlr, Command, CUstart);
}

static void
action(Ctlr* ctlr, Block* bp)
{
	Cb *cb;

	ilock(&ctlr->cbqlock);
	cb = (Cb*)bp->rp;
	cb->command |= CbEL;

	if(ctlr->cbqhead){
		ctlr->cbqtail->next = bp;
		cb = (Cb*)ctlr->cbqtail->rp;
		cb->link = PADDR(bp->rp);
		cb->command &= ~CbEL;
	}
	else
		ctlr->cbqhead = bp;
	ctlr->cbqtail = bp;

	if(ctlr->cbqbusy == 0)
		custart(ctlr);

	iunlock(&ctlr->cbqlock);
}

static void
attach(Ether* ether)
{
	int status;
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->rlock);
	status = csr16r(ctlr, Status);
	if((status & RUstatus) == RUidle){
		csr32w(ctlr, Pointer, PADDR(ctlr->rfd[ctlr->rfdx]->rp));
		while(csr8r(ctlr, Command))
			;
		csr8w(ctlr, Command, RUstart);
	}
	iunlock(&ctlr->rlock);
}

static void
configure(void* arg, int promiscuous)
{
	Ctlr *ctlr;
	Block *bp;
	Cb *cb;

	ctlr = ((Ether*)arg)->ctlr;

	bp = allocb(sizeof(Cb));
	cb = (Cb*)bp->rp;
	bp->wp += sizeof(Cb);

	cb->command = CbConfigure;
	cb->link = NullPointer;
	memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
	if(promiscuous)
		cb->data[15] |= 0x01;
	action(ctlr, bp);
}

static long
write(Ether* ether, void* buf, long n)
{
	Block *bp;
	TxCB *txcb;

	bp = allocb(n+sizeof(TxCB));
	txcb = (TxCB*)bp->wp;
	bp->wp += sizeof(TxCB);

	txcb->command = CbTransmit;
	txcb->link = NullPointer;
	txcb->tbd = NullPointer;
	txcb->count = CbEOF|n;
	txcb->threshold = 2;
	txcb->number = 0;

	memmove(bp->wp, buf, n);
	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
	bp->wp += n;

	action(ether->ctlr, bp);

	ether->outpackets++;

	return n;
}

static void
interrupt(Ureg*, void* arg)
{
	Rfd *rfd;
	Block *bp;
	Ctlr *ctlr;
	Ether *ether;
	int status;

	ether = arg;
	ctlr = ether->ctlr;

	for(;;){
		status = csr16r(ctlr, Status);
		csr8w(ctlr, Ack, (status>>8) & 0xFF);

		if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0)
			return;

		if(status & StatFR){
			bp = ctlr->rfd[ctlr->rfdx];
			rfd = (Rfd*)bp->rp;
			while(rfd->field & RfdC){
				etherrloop(ether, rfd, rfd->count & 0x3FFF);
				ether->inpackets++;

				/*
				 * Reinitialise the frame for reception and bump
				 * the receive frame processing index;
				 * bump the sentinel index, mark the new sentinel
				 * and clear the old sentinel suspend bit;
				 * set bp and rfd for the next receive frame to
				 * process.
				 */
				rfd->field = 0;
				rfd->count = 0;
				ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd);

				rfd = (Rfd*)ctlr->rfd[ctlr->rfdl]->rp;
				ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd);
				((Rfd*)ctlr->rfd[ctlr->rfdl]->rp)->field |= RfdS;
				rfd->field &= ~RfdS;

				bp = ctlr->rfd[ctlr->rfdx];
				rfd = (Rfd*)bp->rp;
			}
			status &= ~StatFR;
		}

		if(status & StatRNR){
			while(csr8r(ctlr, Command))
				;
			csr8w(ctlr, Command, RUresume);

			status &= ~StatRNR;
		}

		if(status & StatCNA){
			lock(&ctlr->cbqlock);
			while(bp = ctlr->cbqhead){
				if((((Cb*)bp->rp)->command & CbC) == 0)
					break;
				ctlr->cbqhead = bp->next;
				freeb(bp);
			}
			custart(ctlr);
			unlock(&ctlr->cbqlock);

			status &= ~StatCNA;
		}

		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
			panic("%s#%d: status %uX\n", ctlr->type,  ctlr->ctlrno, status);
	}
}

static void
ctlrinit(Ctlr* ctlr)
{
	int i;
	Block *bp;
	Rfd *rfd;
	ulong link;

	link = NullPointer;
	for(i = Nrfd-1; i >= 0; i--){
		if(ctlr->rfd[i] == 0){
			bp = allocb(sizeof(Rfd));
			ctlr->rfd[i] = bp;
		}
		else
			bp = ctlr->rfd[i];
		rfd = (Rfd*)bp->rp;

		rfd->field = 0;
		rfd->link = link;
		link = PADDR(rfd);
		rfd->rbd = NullPointer;
		rfd->count = 0;
		rfd->size = sizeof(Etherpkt);

		rfd->bp = bp;
	}
	((Rfd*)ctlr->rfd[Nrfd-1]->rp)->link = PADDR(ctlr->rfd[0]->rp);

	ctlr->rfdl = 0;
	((Rfd*)ctlr->rfd[0]->rp)->field |= RfdS;
	ctlr->rfdx = 2;

	memmove(ctlr->configdata, configdata, sizeof(configdata));
}

static int
dp83840r(Ctlr* ctlr, int phyadd, int regadd)
{
	int mcr, timo;

	/*
	 * DP83840
	 * 10/100Mb/s Ethernet Physical Layer.
	 */
	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
	mcr = 0;
	for(timo = 10; timo; timo--){
		mcr = csr32r(ctlr, Mcr);
		if(mcr & MDIready)
			break;
		delay(1);
	}

	if(mcr & MDIready)
		return mcr & 0xFFFF;

	return -1;
}

static int
hy93c46r(Ctlr* ctlr, int r)
{
	int i, op, data;

	/*
	 * Hyundai HY93C46 or equivalent serial EEPROM.
	 * This sequence for reading a 16-bit register 'r'
	 * in the EEPROM is taken straight from Section
	 * 2.3.4.2 of the Intel 82557 User's Guide.
	 */
	csr16w(ctlr, Ecr, EEcs);
	op = EEstart|EEread;
	for(i = 2; i >= 0; i--){
		data = (((op>>i) & 0x01)<<2)|EEcs;
		csr16w(ctlr, Ecr, data);
		csr16w(ctlr, Ecr, data|EEsk);
		delay(1);
		csr16w(ctlr, Ecr, data);
		delay(1);
	}

	for(i = EEaddrsz-1; i >= 0; i--){
		data = (((r>>i) & 0x01)<<2)|EEcs;
		csr16w(ctlr, Ecr, data);
		csr16w(ctlr, Ecr, data|EEsk);
		delay(1);
		csr16w(ctlr, Ecr, data);
		delay(1);
		if((csr16r(ctlr, Ecr) & EEdo) == 0)
			break;
	}

	data = 0;
	for(i = 15; i >= 0; i--){
		csr16w(ctlr, Ecr, EEcs|EEsk);
		delay(1);
		if(csr16r(ctlr, Ecr) & EEdo)
			data |= (1<<i);
		csr16w(ctlr, Ecr, EEcs);
		delay(1);
	}

	csr16w(ctlr, Ecr, 0);

	return data;
}

typedef struct Adapter Adapter;
struct Adapter {
	Adapter*	next;
	int		port;
	int		irq;
	int		pcidevno;
};
static Adapter *adapter;

static int
i82557(Ether* ether, int* pcidevno)
{
	PCIcfg pcicfg;
	static int devno = 0;
	int i, irq, port;
	Adapter *ap;

	for(;;){
		pcicfg.vid = 0x8086;
		pcicfg.did = 0x1229;
		if((devno = pcimatch(0, devno, &pcicfg)) == -1)
			break;

		port = 0;
		irq = 0;
		for(i = 0; i < 6; i++){
			if((pcicfg.baseaddr[i] & 0x03) != 0x01)
				continue;
			port = pcicfg.baseaddr[i] & ~0x01;
			irq = pcicfg.irq;
			if(ether->port == 0 || ether->port == port){
				ether->irq = irq;
				*pcidevno = devno-1;
				return port;
			}
		}
		if(port == 0)
			continue;

		ap = malloc(sizeof(Adapter));
		ap->port = port;
		ap->irq = irq;
		ap->pcidevno = devno-1;
		ap->next = adapter;
		adapter = ap;
	}

	return 0;
}

static int
reset(Ether* ether)
{
	int i, pcidevno, port, x;
	Adapter *ap, **app;
	uchar ea[Eaddrlen];
	Ctlr *ctlr;
	Block *bp;
	Cb *cb;

	/*
	 * Any adapter matches if no ether->port is supplied, otherwise the
	 * ports must match. First see if an adapter that fits the bill has
	 * already been found. If not, scan for another.
	 */
	port = 0;
	pcidevno = -1;
	for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
		if(ether->port == 0 || ether->port == ap->port){
			ether->irq = ap->irq;
			pcidevno = ap->pcidevno;
			*app = ap->next;
			free(ap);
			break;
		}
	}
	if(port == 0 && (port = i82557(ether, &pcidevno)) == 0)
		return -1;

	/*
	 * Allocate a controller structure and start to initialise it.
	 * Perform a software reset after which need to ensure busmastering
	 * is still enabled. The EtherExpress PRO/100B appears to leave
	 * the PCI configuration alone (see the 'To do' list above) so punt
	 * for now.
	 * Load the RUB and CUB registers for linear addressing (0).
	 */
	ether->ctlr = malloc(sizeof(Ctlr));
	ctlr = ether->ctlr;
	ctlr->ctlrno = ether->ctlrno;
	ctlr->type = ether->type;
	ctlr->port = port;

	csr32w(ctlr, Port, 0);
	delay(1);

	pcicfgr(0, pcidevno, 0, 0x04, &x, 4);
	if((x & 0x05) != 0x05)
		print("PCI command = %uX\n", x);

	csr32w(ctlr, Pointer, 0);
	csr8w(ctlr, Command, LoadRUB);
	while(csr8r(ctlr, Command))
		;
	csr8w(ctlr, Command, LoadCUB);
	while(csr8r(ctlr, Command))
		;

	/*
	 * Initialise the action and receive frame areas.
	 */
	ctlrinit(ctlr);

	/*
	 * Possibly need to configure the physical-layer chip here, but the
	 * EtherExpress PRO/100B appears to bring it up with a sensible default
	 * configuration. However, should check for the existence of the PHY
	 * and, if found, check whether to use 82503 (serial) or MII (nibble)
	 * mode. Verify the PHY is a National Semiconductor DP83840 by looking
	 * at the Organizationally Unique Identifier (OUI) in registers 2 and 3
	 * which should be 0x80017.
	 */
	for(i = 1; i < 32; i++){
		if((x = dp83840r(ctlr, i, 2)) == 0xFFFF)
			continue;
		x <<= 6;
		x |= dp83840r(ctlr, i, 3)>>10;
		if(x != 0x80017)
			continue;

		x = dp83840r(ctlr, i, 0x1B);
		if((x & 0x0200) == 0){
			ctlr->configdata[8] = 1;
			ctlr->configdata[15] |= 0x80;
		}
		break;
	}

	/*
	 * Load the chip configuration
	 */
	configure(ether, 0);

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to loading
	 * the station address with the Individual Address Setup command.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
		for(i = 0; i < Eaddrlen/2; i++){
			x = hy93c46r(ctlr, i);
			ether->ea[2*i] = x & 0xFF;
			ether->ea[2*i+1] = (x>>8) & 0xFF;
		}
	}

	bp = allocb(sizeof(Cb));
	cb = (Cb*)bp->rp;
	bp->wp += sizeof(Cb);

	cb->command = CbIAS;
	cb->link = NullPointer;
	memmove(cb->data, ether->ea, Eaddrlen);
	action(ctlr, bp);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	ether->port = port;
	ether->attach = attach;
	ether->write = write;
	ether->interrupt = interrupt;

	ether->promiscuous = configure;
	ether->arg = ether;

	return 0;
}

void
ether82557link(void)
{
	addethercard("i82557",  reset);
}
.
## diffname pc/ether82557.c 1996/0419
## diff -e /n/fornaxdump/1996/0418/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0419/sys/src/brazil/pc/ether82557.c
440,441d
111,112d
## diffname pc/ether82557.c 1996/0423
## diff -e /n/fornaxdump/1996/0419/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0423/sys/src/brazil/pc/ether82557.c
629c
	 * Initialise the receive frame and configuration areas.
.
626a
	csr8w(ctlr, Command, LoadCUB);
.
624c
	csr8w(ctlr, Command, LoadRUB);
.
620,621c
	csr32w(ctlr, General, 0);
.
281c
		csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp));
.
240c
	csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp));
.
159c
	CbIAS		= 0x00010000,	/* Individual Address Setup */
.
33c
	General		= 0x04,		/* dword */
.
8a
 *	tidy/fix locking;
 *	optionally use memory-mapped registers;
.
## diffname pc/ether82557.c 1996/0601
## diff -e /n/fornaxdump/1996/0423/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0601/sys/src/brazil/pc/ether82557.c
655c
			ctlr->configdata[15] &= ~0x80;
			ether->mbps = 100;
.
217c
	0xC8,				/* promiscuous mode off */
.
## diffname pc/ether82557.c 1996/0607
## diff -e /n/fornaxdump/1996/0601/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0607/sys/src/brazil/pc/ether82557.c
697c
	ether->promiscuous = promiscuous;
.
695a
	ether->ifstat = ifstat;
.
688a
	iunlock(&ctlr->rlock);

.
664c
	bp = configure(ctlr, 0);
	action(ctlr, bp);
.
641,642c
	 * at the Organizationally Unique Identifier (OUI) in registers 2 and
	 * 3 which should be 0x80017.
.
629a
	while(csr8r(ctlr, Command))
		;
	csr32w(ctlr, General, PADDR(ctlr->dump));
	csr8w(ctlr, Command, LoadDCA);


.
625a

.
624a
	csr32w(ctlr, General, 0);
.
618,622d
614a
	ilock(&ctlr->rlock);

.
413a
	iunlock(&ctlr->rlock);
.
406d
398d
392a
print("%s#%d: status %uX\n", ctlr->type,  ctlr->ctlrno, status);
.
363,364c
				if(rfd->field & RfdOK){
					etherrloop(ether, rfd, rfd->count & 0x3FFF);
					ether->inpackets++;
				}
else print("%s#%d: rfd->field %uX\n", ctlr->type,  ctlr->ctlrno, rfd->field);
.
356,357c
		if((status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) == 0)
			break;
.
351a
	ilock(&ctlr->rlock);
.
333c
	ctlr = ether->ctlr;
	ilock(&ctlr->rlock);
	action(ctlr, bp);
	iunlock(&ctlr->rlock);
.
314a
	Ctlr *ctlr;
.
312a
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr;
	char buf[512];
	int len;

	ctlr = ether->ctlr;
	lock(&ctlr->dlock);
	ctlr->dump[16] = 0;

	ilock(&ctlr->rlock);
	while(csr8r(ctlr, Command))
		;
	csr8w(ctlr, Command, DumpSC);
	iunlock(&ctlr->rlock);

	/*
	 * Wait for completion status, should be 0xA005.
	 */
	while(ctlr->dump[16] == 0)
		;

	ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
	ether->crcs = ctlr->dump[10];
	ether->frames = ctlr->dump[11];
	ether->buffs = ctlr->dump[12]+ctlr->dump[15];
	ether->overflows = ctlr->dump[13];

	if(n == 0){
		unlock(&ctlr->dlock);
		return 0;
	}

	len = sprint(buf, "transmit good frames: %ld\n", ctlr->dump[0]);
	len += sprint(buf+len, "transmit maximum collisions errors: %ld\n", ctlr->dump[1]);
	len += sprint(buf+len, "transmit late collisions errors: %ld\n", ctlr->dump[2]);
	len += sprint(buf+len, "transmit underrun errors: %ld\n", ctlr->dump[3]);
	len += sprint(buf+len, "transmit lost carrier sense: %ld\n", ctlr->dump[4]);
	len += sprint(buf+len, "transmit deferred: %ld\n", ctlr->dump[5]);
	len += sprint(buf+len, "transmit single collisions: %ld\n", ctlr->dump[6]);
	len += sprint(buf+len, "transmit multiple collisions: %ld\n", ctlr->dump[7]);
	len += sprint(buf+len, "transmit total collisions: %ld\n", ctlr->dump[8]);
	len += sprint(buf+len, "receive good frames: %ld\n", ctlr->dump[9]);
	len += sprint(buf+len, "receive CRC errors: %ld\n", ctlr->dump[10]);
	len += sprint(buf+len, "receive alignment errors: %ld\n", ctlr->dump[11]);
	len += sprint(buf+len, "receive resource errors: %ld\n", ctlr->dump[12]);
	len += sprint(buf+len, "receive overrun errors: %ld\n", ctlr->dump[13]);
	len += sprint(buf+len, "receive collision detect errors: %ld\n", ctlr->dump[14]);
	sprint(buf+len, "receive short frame errors: %ld\n", ctlr->dump[15]);

	unlock(&ctlr->dlock);

	return readstr(offset, a, n, buf);
}

static long
.
309a
	iunlock(&ctlr->rlock);
.
308a

	return bp;
}

static void
promiscuous(void* arg, int on)
{
	Ctlr *ctlr;
	Block *bp;

	ctlr = ((Ether*)arg)->ctlr;
	bp = configure(ctlr, on);

	ilock(&ctlr->rlock);
.
298,299d
294d
291,292c
static Block*
configure(Ctlr* ctlr, int promiscuous)
.
285a
		csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp));
.
283d
269,270d
253d
244a
	csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp));
.
242d
197a

	Lock	dlock;		/* dump statistical counters */
	ulong	dump[17];
.
194,195c
	Block*	cbqhead;		/* transmit side */
.
190c
	Block*	rfd[Nrfd];		/* receive side */
.
188c
	Lock	rlock;			/* registers */
.
144c
	uchar	data[24];		/* CbIAS + CbConfigure */
.
9,11c
 *	optionally use memory-mapped registers.
.
## diffname pc/ether82557.c 1996/0608
## diff -e /n/fornaxdump/1996/0607/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0608/sys/src/brazil/pc/ether82557.c
466d
437d
## diffname pc/ether82557.c 1996/0622
## diff -e /n/fornaxdump/1996/0608/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0622/sys/src/brazil/pc/ether82557.c
766c
	iunlock(&ctlr->lock);
.
686c
	ilock(&ctlr->lock);
.
669c
	if(port == 0 && (port = i82557pci(ether, &pcidevno)) == 0)
.
603c
i82557pci(Ether* ether, int* pcidevno)
.
484c
	iunlock(&ctlr->lock);
.
421c
	ilock(&ctlr->lock);
.
402c
	iunlock(&ctlr->lock);
.
400c
	ilock(&ctlr->lock);
.
336c
	iunlock(&ctlr->lock);
.
332c
	ilock(&ctlr->lock);
.
318c
	iunlock(&ctlr->lock);
.
316c
	ilock(&ctlr->lock);
.
285c
	iunlock(&ctlr->lock);
.
277c
	ilock(&ctlr->lock);
.
196c
	Lock	dlock;			/* dump statistical counters */
.
186c
	Lock	lock;
.
## diffname pc/ether82557.c 1996/0625
## diff -e /n/fornaxdump/1996/0622/sys/src/brazil/pc/ether82557.c /n/fornaxdump/1996/0625/sys/src/brazil/pc/ether82557.c
732c
		}
		else{
			x = dp83840r(ctlr, i, 0x1B);
			if((x & 0x0200) == 0){
				ctlr->configdata[8] = 1;
				ctlr->configdata[15] &= ~0x80;
			}
.
728,729c
		x = dp83840r(ctlr, i, 0x19);
		if((x & 0x0040) == 0){
			ether->mbps = 100;
.
8a
 *	auto-negotiation;
.
## diffname pc/ether82557.c 1997/0327
## diff -e /n/fornaxdump/1996/0625/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0327/sys/src/brazil/pc/ether82557.c
781c
	ether->transmit = transmit;
.
774d
772c
	action(ctlr, cb);
.
765,770c
	cb = cballoc(ctlr, CbIAS);
.
760,761c
			ether->ea[2*i] = x;
			ether->ea[2*i+1] = x>>8;
.
757c
	if(!memcmp(ea, ether->ea, Eaddrlen)){
.
748,749c
	configure(ctlr, 0);
.
737c
			if(!(x & 0x0200)){
.
730c
		if(!(x & 0x0040)){
.
706d
704a
	iunlock(&ctlr->rlock);
.
687,688c
	ilock(&ctlr->rlock);
.
683,684d
670c
	if(port == 0 && (port = i82557pci(ether)) == 0)
.
664,666c
			ether->tbdf = ap->tbdf;
			*bpp = bp->next;
			freeb(bp);
.
662a
			port = ap->port;
.
660,661c
	bpp = &adapter;
	for(bp = *bpp; bp; bp = bp->next){
		ap = (Adapter*)bp->rp;
.
651d
647,648c
	int i, port, x;
	Block *bp, **bpp;
	Adapter *ap;
.
633,638c
		i82557adapter(&adapter, port, irq, p->tbdf);
.
630,631d
617,628c
	bp->next = *bpp;
	*bpp = bp;
}

static int
i82557pci(Ether* ether)
{
	static Pcidev *p;
	int irq, port;

	while(p = pcimatch(p, 0x8086, 0x1229)){
		/*
		 * bar[0] is the memory-mapped register address (4KB),
		 * bar[1] is the I/O port register address (32 bytes) and
		 * bar[2] is for the flash ROM (1MB).
		 */
		port = p->bar[1] & ~0x01;
		irq = p->intl;
		if(ether->port == 0 || ether->port == port){
			ether->irq = irq;
			ether->tbdf = p->tbdf;
			return port;
.
611,615c
	bp = allocb(sizeof(Adapter));
	ap = (Adapter*)bp->rp;
	ap->port = port;
	ap->irq = irq;
	ap->tbdf = tbdf;
.
606,608c
	Block *bp;
.
603,604c
static void
i82557adapter(Block** bpp, int port, int irq, int tbdf)
.
594,601c
typedef struct Adapter {
	int	port;
	int	irq;
	int	tbdf;
} Adapter;
static Block* adapter;
.
575c
		if(!(csr16r(ctlr, Ecr) & EEdo))
.
515,518d
513c
	ctlr->rfdtail->next = ctlr->rfdhead;
	rfd = (Rfd*)ctlr->rfdtail->rp;
	rfd->link = PADDR(ctlr->rfdhead->rp);
	rfd->field |= RfdS;
	ctlr->rfdhead = ctlr->rfdhead->next;
.
497,511c
	for(i = 0; i < Nrfd; i++){
		bp = rfdalloc(link);
		if(ctlr->rfdhead == nil)
			ctlr->rfdtail = bp;
		bp->next = ctlr->rfdhead;
		ctlr->rfdhead = bp;
		link = PADDR(bp->rp);
.
495a
	/*
	 * Create the Receive Frame Area (RFA) as a ring of allocated
	 * buffers.
	 * A sentinel buffer is maintained between the last buffer in
	 * the ring (marked with RfdS) and the head buffer to defeat the
	 * hardware prefetch of the next RFD and allow dynamic buffer
	 * allocation.
	 */
.
485d
483c
			panic("#l%d: status %uX\n", ether->ctlrno, status);
.
477a
			unlock(&ctlr->cbqlock);
.
474,475c
				ctlr->cbqhead = cb->next;
				if(cb->bp)
					freeb(cb->bp);
				cbfree(ctlr, cb);
.
471,472c
			lock(&ctlr->cbqlock);
			while(cb = ctlr->cbqhead){
				if(!(cb->command & CbC))
.
465a
			unlock(&ctlr->rlock);
.
462a
			lock(&ctlr->rlock);
.
456c
				/*
				 * Finally done with the current (possibly replaced)
				 * head, move on to the next and maintain the sentinel
				 * between tail and head.
				 */
				ctlr->rfdhead = bp->next;
				bp = ctlr->rfdhead;
.
447,453c
				rfd = (Rfd*)ctlr->rfdtail->rp;
				ctlr->rfdtail = ctlr->rfdtail->next;
				ctlr->rfdtail->next = bp;
				((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
				((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
.
440,445c
				 * The ring tail pointer follows the head with with one
				 * unused buffer in between to defeat hardware prefetch;
				 * once the tail pointer has been bumped on to the next
				 * and the new tail has the Suspend bit set, it can be
				 * removed from the old tail buffer.
				 * As a replacement for the current head buffer may have
				 * been allocated above, ensure that the new tail points
				 * to it (next and link).
.
437a
				else{
					rfd->field = 0;
					rfd->count = 0;
				}
.
434,436c
				/*
				 * If it's an OK receive frame and a replacement buffer
				 * can be allocated then
				 *	adjust the received buffer pointers for the
				 *	  actual data received;
				 *	initialise the replacement buffer to point to
				 *	  the next in the ring;
				 *	pass the received buffer on for disposal;
				 *	initialise bp to point to the replacement.
				 * If not, just adjust the necessary fields for reuse.
				 */
				if((rfd->field & RfdOK) && (xbp = rfdalloc(rfd->link))){
					bp->rp += sizeof(Rfd)-sizeof(Etherpkt);
					bp->wp = bp->rp + (rfd->count & 0x3FFF);

					xbp->next = bp->next;
					bp->next = 0;

					etheriq(ether, bp, 1);
					bp = xbp;
.
431c
			bp = ctlr->rfdhead;
.
427c
		if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
.
425a
		unlock(&ctlr->rlock);
.
423a
		lock(&ctlr->rlock);
.
422d
414c
	Cb* cb;
	Block *bp, *xbp;
.
407c
	action(ctlr, cb);
.
405c
	cb = cballoc(ctlr, CbSF|CbTransmit);
	cb->bp = bp;
	cb->tbd = PADDR(&cb->tba);
	cb->count = 0;
	cb->threshold = 2;
	cb->number = 1;
	cb->tba = PADDR(bp->rp);
	cb->tbasz = BLEN(bp);
.
401,403d
389,399d
385,387c
	bp = qget(ether->oq);
	if(bp == nil)
		return;
.
383c
	Cb *cb;
.
378,379c
static void
transmit(Ether* ether)
.
337c
	iunlock(&ctlr->rlock);
.
333c
	ilock(&ctlr->rlock);
.
311,319c
	configure(((Ether*)arg)->ctlr, on);
.
304,305c
	action(ctlr, cb);
.
295,300c
	cb = cballoc(ctlr, CbConfigure);
.
292d
289c
static void
.
286c
	iunlock(&ctlr->rlock);
.
283c
		csr32w(ctlr, General, PADDR(ctlr->rfdhead->rp));
.
278c
	ilock(&ctlr->rlock);
.
268a
	iunlock(&ctlr->cbqlock);
.
267c
	if(!ctlr->cbqbusy)
.
264,265c
		ctlr->cbqhead = cb;
	ctlr->cbqtail = cb;
.
258,261c
		tail = ctlr->cbqtail;
		tail->next = cb;
		tail->link = PADDR(&cb->command);
		tail->command &= ~CbEL;
.
256a
	ilock(&ctlr->cbqlock);
.
254d
252c
	Cb* tail;
.
250c
action(Ctlr* ctlr, Cb* cb)
.
246a
	iunlock(&ctlr->rlock);
.
245c
	csr32w(ctlr, General, PADDR(&ctlr->cbqhead->command));
.
242a
	ilock(&ctlr->rlock);
.
234a
cbfree(Ctlr* ctlr, Cb* cb)
{
	ilock(&ctlr->cbplock);
	cb->next = ctlr->cbpool;
	ctlr->cbpool = cb;
	iunlock(&ctlr->cbplock);
}

static void
.
233a
static Block*
rfdalloc(ulong link)
{
	Block *bp;
	Rfd *rfd;

	if(bp = iallocb(sizeof(Rfd))){
		rfd = (Rfd*)bp->rp;
		rfd->field = 0;
		rfd->link = link;
		rfd->rbd = NullPointer;
		rfd->count = 0;
		rfd->size = sizeof(Etherpkt);
	}

	return bp;
}

static Cb*
cballoc(Ctlr* ctlr, int command)
{
	Cb *cb;

	ilock(&ctlr->cbplock);
	if(cb = ctlr->cbpool){
		ctlr->cbpool = cb->next;
		iunlock(&ctlr->cbplock);
		cb->next = 0;
		cb->bp = 0;
	}
	else{
		iunlock(&ctlr->cbplock);
		cb = smalloc(sizeof(Cb));
	}

	cb->command = command;
	cb->link = NullPointer;

	return cb;
}

.
221c
	0xF3,				/* transmit padding enable */
.
206,209c
	0x00,				/* Rx DMA maximum byte count */
	0x80,				/* Tx DMA maximum byte count */
	0x32,				/* !late SCB, CNA interrupts */
	0x03,				/* discard short Rx frames */
.
203c
	0x08,				/* Rx/Tx FIFO limit */
.
196a
	Lock	cbplock;		/* pool of free Cb's */
	Cb*	cbpool;


.
193,194c
	Lock	cbqlock;		/* transmit side */
	Cb*	cbqhead;
	Cb*	cbqtail;
.
189,191c
	Lock	rfdlock;		/* receive side */
	Block*	rfdhead;
	Block*	rfdtail;
	int	nrfd;
.
187c
	Lock	rlock;			/* registers */
.
181,184d
168c
	CbSF		= 0x00080000,	/* Flexible-mode CbTransmit */
.
154a
			ulong	tba;
			ushort	tbasz;
			ushort	pad;
		};
	};
} Cb;

.
149,153c
	union {
		uchar	data[24];	/* CbIAS + CbConfigure */
		struct {
			ulong	tbd;
			ushort	count;
			uchar	threshold;
			uchar	number;
.
146d
141,144c
	Cb*	next;
	Block*	bp;
.
139a
typedef struct Cb Cb;
.
111c
	uchar	data[sizeof(Etherpkt)];
.
7,8d
## diffname pc/ether82557.c 1997/0417
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0417/sys/src/brazil/pc/ether82557.c
420c
	p = malloc(READSTR);
	len = snprint(p, READSTR, "transmit good frames: %ld\n", dump[0]);
	len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %ld\n", dump[1]);
	len += snprint(p+len, READSTR-len, "transmit late collisions errors: %ld\n", dump[2]);
	len += snprint(p+len, READSTR-len, "transmit underrun errors: %ld\n", dump[3]);
	len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %ld\n", dump[4]);
	len += snprint(p+len, READSTR-len, "transmit deferred: %ld\n", dump[5]);
	len += snprint(p+len, READSTR-len, "transmit single collisions: %ld\n", dump[6]);
	len += snprint(p+len, READSTR-len, "transmit multiple collisions: %ld\n", dump[7]);
	len += snprint(p+len, READSTR-len, "transmit total collisions: %ld\n", dump[8]);
	len += snprint(p+len, READSTR-len, "receive good frames: %ld\n", dump[9]);
	len += snprint(p+len, READSTR-len, "receive CRC errors: %ld\n", dump[10]);
	len += snprint(p+len, READSTR-len, "receive alignment errors: %ld\n", dump[11]);
	len += snprint(p+len, READSTR-len, "receive resource errors: %ld\n", dump[12]);
	len += snprint(p+len, READSTR-len, "receive overrun errors: %ld\n", dump[13]);
	len += snprint(p+len, READSTR-len, "receive collision detect errors: %ld\n", dump[14]);
	snprint(p+len, READSTR-len, "receive short frame errors: %ld\n", dump[15]);

	n = readstr(offset, a, n, p);
	free(p);

	return n;
.
401,417c
	memmove(dump, ctlr->dump, sizeof(dump));
.
375a

.
372a
	Ctlr *ctlr;
	ulong dump[17];
.
370,371c
	char *p;
.
## diffname pc/ether82557.c 1997/0628
## diff -e /n/emeliedump/1997/0417/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0628/sys/src/brazil/pc/ether82557.c
7a
 *	full-duplex;
.
4c
 * of smarts, unfortunately they're not all in the right place.
.
## diffname pc/ether82557.c 1997/0723
## diff -e /n/emeliedump/1997/0628/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0723/sys/src/brazil/pc/ether82557.c
804,805c
		if(x != 0x80017 && x != 0xAA00)
			print("#l%d: unrecognised PHY - OUI 0x%4.4uX\n", ether->ctlrno, x);
.
795,797c
	 * mode. Verify the PHY is a National Semiconductor DP83840 (OUI 0x80017)
	 * or an Intel 82555 (OUI 0xAA00) by looking at the Organizationally Unique
	 * Identifier (OUI) in registers 2 and 3.
.
751c
	if(port == 0)
.
749a
		bpp = &bp->next;
.
732a
	if(scandone == 0){
		i82557pci();
		scandone = 1;
	}

.
731a
	static int scandone;
.
719,720d
709,717c
		i82557adapter(&adapter, p->bar[1] & ~0x01, p->intl, p->tbdf);
.
702a
	p = nil;
.
700,701c
	Pcidev *p;
.
697,698c
static void
i82557pci(void)
.
8d
4c
 * of smarts, unfortunately none of them are in the right place.
.
## diffname pc/ether82557.c 1997/0806
## diff -e /n/emeliedump/1997/0723/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0806/sys/src/brazil/pc/ether82557.c
830c
	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
.
778c
	csr8w(ctlr, CommandR, LoadDCA);
.
775c
	while(csr8r(ctlr, CommandR))
.
773c
	csr8w(ctlr, CommandR, LoadCUB);
.
771c
	while(csr8r(ctlr, CommandR))
.
769c
	csr8w(ctlr, CommandR, LoadRUB);
.
766c
	while(csr8r(ctlr, CommandR))
.
729,731c
	 * Any adapter matches if no port is supplied,
	 * otherwise the ports must match.
.
540c
			csr8w(ctlr, CommandR, RUresume);
.
538c
			while(csr8r(ctlr, CommandR))
.
383c
	csr8w(ctlr, CommandR, DumpSC);
.
381c
	while(csr8r(ctlr, CommandR))
.
344c
		csr8w(ctlr, CommandR, RUstart);
.
341c
		while(csr8r(ctlr, CommandR))
.
304c
	csr8w(ctlr, CommandR, CUstart);
.
301c
	while(csr8r(ctlr, CommandR))
.
30c
	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */
.
7a
 *	full-duplex;
.
4c
 * of smarts, unfortunately they're not all in the right place.
.
## diffname pc/ether82557.c 1997/0807
## diff -e /n/emeliedump/1997/0806/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0807/sys/src/brazil/pc/ether82557.c
408,423c
	len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);
	len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);
	len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);
	len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);
	len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);
	len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);
	len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);
	len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);
	len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);
	len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);
	len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);
	len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);
	len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);
	len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);
	len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);
	snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
.
## diffname pc/ether82557.c 1997/0823
## diff -e /n/emeliedump/1997/0807/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/0823/sys/src/brazil/pc/ether82557.c
838,841c
	ilock(&ctlr->cblock);
	ctlr->action = CbIAS;
	txstart(ether);
	iunlock(&ctlr->cblock);
.
822c
	if(ether->oq == 0)
		ether->oq = qopen(256*1024, 1, 0, 0);
	configure(ether, 0);
	command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
.
820c
	 * Load the chip configuration and start it off.
.
783a
	ctlr->ncb = Ncb;
.
782c
	 * Initialise the receive frame, transmit ring and configuration areas.
.
780a
	command(ctlr, LoadRUB, 0);
	command(ctlr, LoadCUB, 0);
	command(ctlr, LoadDCA, PADDR(ctlr->dump));

.
765,778d
752c
	 * Perform a software reset after which should ensure busmastering
.
721d
599a
	ctlr->threshold = 8;

	iunlock(&ctlr->cblock);
.
598a
	/*
	 * Create a ring of control blocks for the
	 * transmit side.
	 */
	ilock(&ctlr->cblock);
	ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
	for(i = 0; i < ctlr->ncb; i++){
		ctlr->cbr[i].status = CbC|CbOK;
		ctlr->cbr[i].command = CbS|CbNOP;
		ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
		ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
	}
	ctlr->cbhead = ctlr->cbr;
	ctlr->cbtail = ctlr->cbr;
	ctlr->cbq = 0;

.
559a
			txstart(ether);
			unlock(&ctlr->cblock);

.
557,558c
			ctlr->cbtail = cb;
.
555c
					cb->bp = nil;
				}
				if((cb->status & CbU) && ctlr->threshold < 0xE0)
					ctlr->threshold++;

				ctlr->cbq--;
				cb = cb->next;
.
552,553c
				if(cb->bp){
.
548,550c
			lock(&ctlr->cblock);

			cb = ctlr->cbtail;
			while(ctlr->cbq){
				if(!(cb->status & CbC))
.
538,543c
			command(ctlr, RUresume, 0);
.
453c
static void
transmit(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->cblock);
	txstart(ether);
	iunlock(&ctlr->cblock);
.
444,451c
static void
promiscuous(void* arg, int on)
{
	configure(arg, on);
}
.
442a
	ilock(&ctlr->cblock);
	if(promiscuous){
		ctlr->configdata[6] |= 0x80;		/* Save Bad Frames */
		ctlr->configdata[6] &= ~0x40;		/* !Discard Overrun Rx Frames */
		ctlr->configdata[7] &= ~0x01;		/* !Discard Short Rx Frames */
		ctlr->configdata[15] |= 0x01;		/* Promiscuous mode */
		ctlr->configdata[18] &= ~0x01;		/* (!Padding enable?), !stripping enable */
		ctlr->configdata[21] |= 0x08;		/* Multi Cast ALL */
	}
	else{
		ctlr->configdata[6] &= ~0x80;
		ctlr->configdata[7] |= 0x01;
		ctlr->configdata[15] &= ~0x01;
		ctlr->configdata[18] |= 0x01;		/* 0x03? */
		ctlr->configdata[21] &= ~0x08;
	}
	ctlr->action = CbConfigure;
	txstart(ether);
	iunlock(&ctlr->cblock);
}
.
441a
			cb->command = CbS|CbSF|CbTransmit;
			cb->tbd = PADDR(&cb->tba);
			cb->count = 0;
			cb->threshold = ctlr->threshold;
			cb->number = 1;
			cb->tba = PADDR(bp->rp);
			cb->bp = bp;
			cb->tbasz = BLEN(bp);
		}
		else if(ctlr->action == CbConfigure){
			cb->command = CbS|CbConfigure;
			memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
			ctlr->action = 0;
		}
		else if(ctlr->action == CbIAS){
			cb->command = CbS|CbIAS;
			memmove(cb->data, ether->ea, Eaddrlen);
			ctlr->action = 0;
		}
		else{
			print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action);
			ctlr->action = 0;
			break;
		}
		cb->status = 0;

		ctlr->cbhead->command &= ~CbS;
		ctlr->cbhead = cb;
		ctlr->cbq++;
	}
	command(ctlr, CUresume, 0);

	if(ctlr->cbq > ctlr->cbqmax)
		ctlr->cbqmax = ctlr->cbq;
}

static void
configure(Ether* ether, int promiscuous)
{
	Ctlr *ctlr;

.
438,440c
	ctlr = ether->ctlr;
	while(ctlr->cbq < (ctlr->ncb-1)){
		cb = ctlr->cbhead->next;
		if(ctlr->action == 0){
			bp = qget(ether->oq);
			if(bp == nil)
				break;
.
432c
txstart(Ether* ether)
.
423c
	len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
	snprint(p+len, READSTR-len, "cbqmax: %lud\n", ctlr->cbqmax);
ctlr->cbqmax = 0;
.
389a
	ctlr->dump[16] = 0;
	command(ctlr, DumpSC, 0);
.
388c
	 * Start the command then
	 * wait for completion status,
	 * should be 0xA005.
.
379,386d
350,367d
347c
	unlock(&ctlr->slock);
.
339,345c
	lock(&ctlr->slock);
	if(ctlr->state == 0){
		command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
		ctlr->state = 1;
.
335d
284,332d
260,282d
241a
static void
command(Ctlr* ctlr, int c, int v)
{
	ilock(&ctlr->rlock);

	/*
	 * Only back-to-back CUresume can be done
	 * without waiting for any previous command to complete.
	 * This should be the common case.
	 */
	if(c == CUresume && ctlr->command == CUresume){
		csr8w(ctlr, CommandR, c);
		iunlock(&ctlr->rlock);
		return;
	}

	while(csr8r(ctlr, CommandR))
		;

	switch(c){

	case CUstart:
	case LoadDCA:
	case LoadCUB:
	case RUstart:
	case LoadHDS:
	case LoadRUB:
		csr32w(ctlr, General, v);
		break;

	/*
	case CUnop:
	case CUresume:
	case DumpSC:
	case ResetSA:
	case RUresume:
	case RUabort:
	 */
	default:
		break;
	}
	csr8w(ctlr, CommandR, c);
	ctlr->command = c;

	iunlock(&ctlr->rlock);
}

.
201,204d
196,199c
	Lock	cblock;			/* transmit side */
	int	action;
	uchar	configdata[24];
	int	threshold;
	int	ncb;
	Cb*	cbr;
	Cb*	cbhead;
	Cb*	cbtail;
	int	cbq;
	int	cbqmax;
.
191,192c
	Block*	rfdhead;		/* receive side */
.
189a
	int	command;		/* last command issued */
.
187d
185a
	Lock	slock;			/* attach */
	int	state;

.
182c
	CbEOF		= 0x8000,
.
176,178c
	CbI		= 0x2000,	/* Interrupt after completion */
	CbS		= 0x4000,	/* Suspend after completion */
	CbEL		= 0x8000,	/* End of List */
.
174c
	CbSF		= 0x0008,	/* Flexible-mode CbTransmit */
.
165,172c
	CbNOP		= 0x0000,
	CbIAS		= 0x0001,	/* Individual Address Setup */
	CbConfigure	= 0x0002,
	CbMAS		= 0x0003,	/* Multicast Address Setup */
	CbTransmit	= 0x0004,
	CbDump		= 0x0006,
	CbDiagnose	= 0x0007,
	CbCommand	= 0x0007,	/* mask */
.
162,163c
	CbU		= 0x1000,	/* transmit underrun */
	CbOK		= 0x2000,	/* DMA completed OK */
	CbC		= 0x8000,	/* execution Complete */
.
158a

	Block*	bp;
	Cb*	next;
.
141,144c
	ushort	status;
	ushort	command;
.
135,136c
	RfdF		= 0x4000,
	RfdEOF		= 0x8000,
.
23a
	Ncb		= 64,		/* maximum control blocks queued */
.
7,9c
 *	auto-negotiation, full-duplex;
 *	optionally use memory-mapped registers;
 *	detach for PCI reset problems (also towards loadable drivers).
.
## diffname pc/ether82557.c 1997/1011
## diff -e /n/emeliedump/1997/0823/sys/src/brazil/pc/ether82557.c /n/emeliedump/1997/1011/sys/src/brazil/pc/ether82557.c
760c
		i82557adapter(&adapter, p->mem[1].bar & ~0x01, p->intl, p->tbdf);
.
263a
	 */
.
258c
	 * Unfortunately there's a chip errata where back-to-back
	 * CUresumes can be lost, the fix is to always wait.
.
## diffname pc/ether82557.c 1998/0304
## diff -e /n/emeliedump/1997/1011/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0304/sys/src/brazil/pc/ether82557.c
878c
			x = ctlr->eeprom[i];
.
860a
	else{
		ctlr->configdata[8] = 0;
		ctlr->configdata[15] |= 0x80;
	}
.
859c

		ctlr->configdata[8] = 1;
		ctlr->configdata[15] &= ~0x80;
.
857a
			if(an & 0x0140)
				x |= 0x0400;
			miiw(ctlr, phyaddr, 0x17, x);
			break;
.
849,856c

		switch((ctlr->eeprom[6]>>8) & 0x001F){

		case 0x04:				/* DP83840 */
		case 0x0A:				/* DP83840A */
			/*
			 * The DP83840[A] requires some tweaking for
			 * reliable operation.
			 */
			x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
			x |= 0x0020;
			for(i = 0; i < ether->nopt; i++){
				if(cistrcmp(ether->opt[i], "congestioncontrol"))
					continue;
				x |= 0x0100;
				break;
.
846,847c
	/*
	 * Eeprom[6] indicates whether there is a PHY and whether
	 * it's not 10Mb-only, in which case use the given PHY address
	 * to set any PHY specific options and determine the speed.
	 * If no PHY, assume 82503 (serial) operation.
	 */
	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
		phyaddr = ctlr->eeprom[6] & 0x00FF;

		/*
		 * Resolve the highest common ability of the two
		 * link partners. In descending order:
		 *	0x0100		100BASE-TX Full Duplex
		 *	0x0200		100BASE-T4
		 *	0x0080		100BASE-TX
		 *	0x0040		10BASE-T Full Duplex
		 *	0x0020		10BASE-T
		 */
		an = miir(ctlr, phyaddr, 0x04);
		an &= miir(ctlr, phyaddr, 0x05) & 0x03E0;
		if(an & 0x380)
.
838,844c
	sum = 0;
	for(i = 0; i < 0x40; i++){
		x = hy93c46r(ctlr, i);
		ctlr->eeprom[i] = x;
		sum += x;
	}
	if(sum != 0xBABA)
		print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum);
.
830,836c
	 * Read the EEPROM.
.
769c
	int an, i, phyaddr, port, x;
	unsigned short sum;
.
719c
		microdelay(1);
.
715c
		microdelay(1);
.
707c
		microdelay(1);
.
698c
		microdelay(1);
.
696c
		microdelay(1);
.
688c
	 * 3.3.4.2 of the Intel 82557 User's Guide.
.
679a
miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
{
	int mcr, timo;

	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
	mcr = 0;
	for(timo = 64; timo; timo--){
		mcr = csr32r(ctlr, Mcr);
		if(mcr & MDIready)
			break;
		microdelay(1);
	}

	if(mcr & MDIready)
		return 0;

	return -1;
}

static int
.
670c
		microdelay(1);
.
666c
	for(timo = 64; timo; timo--){
.
660,663d
656c
miir(Ctlr* ctlr, int phyadd, int regadd)
.
650a
	ctlr->tick = 0;
.
507a
		/*
		 * If the watchdog timer for the receiver lockup errata is running,
		 * let it know the receiver is active.
		 */
		if(status & (StatFR|StatRNR))
			ctlr->tick = 0;

.
458a
		ctlr->configdata[6] |= 0x40;
.
424a
		else if(ctlr->action == CbMAS){
			cb->command = CbS|CbMAS;
			memset(cb->data, 0, sizeof(cb->data));
			ctlr->action = 0;
		}
.
382,383c
	if(ctlr->cbqmax > ctlr->cbqmaxhw)
		ctlr->cbqmaxhw = ctlr->cbqmax;
	len += snprint(p+len, READSTR-len, "cbqmax: %lud\n", ctlr->cbqmax);
	ctlr->cbqmax = 0;
	snprint(p+len, READSTR-len, "threshold: %lud\n", ctlr->threshold);
.
325a

		/*
		 * Start the watchdog timer for the receive lockup errata
		 * unless the EEPROM compatibility word indicates it may be
		 * omitted.
		 */
		if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
			snprint(name, NAMELEN, "#l%dwatchdog", ether->ctlrno);
			kproc(name, watchdog, ether);
		}
.
319a
	char name[NAMELEN];
.
316a
watchdog(void* arg)
{
	Ether *ether;
	Ctlr *ctlr;
	static void txstart(Ether*);

	ether = arg;
	for(;;){
		ctlr = ether->ctlr;
		tsleep(&ctlr->timer, return0, 0, 4000);

		/*
		 * Hmmm. This doesn't seem right. Currently
		 * the device can't be disabled but it may be in
		 * the future.
		 */
		ctlr = ether->ctlr;
		if(ctlr == nil || ctlr->state == 0){
			print("%s: exiting\n", up->text);
			pexit("disabled", 0);
		}

		if(ctlr->tick++){
			ilock(&ctlr->cblock);
			ctlr->action = CbMAS;
			txstart(ether);
			iunlock(&ctlr->cblock);
		}
	}
}

static void
.
233c
	0xC8,				/* 503, promiscuous mode off */
.
210a
	int	cbqmaxhw;
.
193a
	Rendez	timer;			/* watchdog timer for receive lockup errata */
	int	tick;

.
192a
	ushort	eeprom[0x40];
.
## diffname pc/ether82557.c 1998/0331
## diff -e /n/emeliedump/1998/0304/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0331/sys/src/brazil/pc/ether82557.c
656c
			iunlock(&ctlr->cblock);
.
637c
			ilock(&ctlr->cblock);
.
560c
		iunlock(&ctlr->rlock);
.
557c
		ilock(&ctlr->rlock);
.
## diffname pc/ether82557.c 1998/0507
## diff -e /n/emeliedump/1998/0331/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0507/sys/src/brazil/pc/ether82557.c
1012a
	ether->multicast = multicast;
.
532a
multicast(void* arg, uchar *addr, int on)
{
	USED(addr, on);
	configure(arg, 1);
}

static void
.
## diffname pc/ether82557.c 1998/0511
## diff -e /n/emeliedump/1998/0507/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0511/sys/src/brazil/pc/ether82557.c
972a

		case 0x07:				/* Intel 82555 */
			/*
			 * Auto-negotiation may fail if the other end is
			 * a DP83840A and the cable is short.
			 */
			miir(ctlr, phyaddr, 0x01);
			bmsr = miir(ctlr, phyaddr, 0x01);
			if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
				miiw(ctlr, phyaddr, 0x1A, 0x2010);
				x = miir(ctlr, phyaddr, 0);
				miiw(ctlr, phyaddr, 0, 0x0200|x);
				for(i = 0; i < 3000; i++){
					delay(1);
					if(miir(ctlr, phyaddr, 0x01) & 0x0020)
						break;
				}
				miiw(ctlr, phyaddr, 0x1A, 0x2000);
					
				anar = miir(ctlr, phyaddr, 0x04);
				anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
				anar &= anlpar;
				bmcr = 0;
				if(anar & 0x380)
					bmcr = 0x2000;
				if(anar & 0x0140)
					bmcr |= 0x0100;
			}
			break;
		}

		/*
		 * Force speed and duplex if no auto-negotiation.
		 */
		if(anlpar == 0){
			force = 0;
			for(i = 0; i < ether->nopt; i++){
				if(cistrcmp(ether->opt[i], "fullduplex") == 0){
					force = 1;
					bmcr |= 0x0100;
					ctlr->configdata[19] |= 0x40;
				}
				else if(cistrcmp(ether->opt[i], "speed") == 0){
					force = 1;
					x = strtol(&ether->opt[i][6], 0, 0);
					if(x == 10)
						bmcr &= ~0x2000;
					else if(x == 100)
						bmcr |= 0x2000;
					else
						force = 0;
				}
			}
			if(force)
				miiw(ctlr, phyaddr, 0x00, bmcr);
.
971a

			/*
			 * If the link partner can't autonegotiate, determine
			 * the speed from elsewhere.
			 */
			if(anlpar == 0){
				miir(ctlr, phyaddr, 0x01);
				bmsr = miir(ctlr, phyaddr, 0x01);
				x = miir(ctlr, phyaddr, 0x19);
				if((bmsr & 0x0004) && !(x & 0x0040))
					bmcr = 0x2000;
			}
.
969,970d
962c
			x |= 0x0420;
.
959a
			 * The manual says bit 10 should be unconditionally
			 * set although it supposedly only affects full-duplex
			 * operation (an & 0x0140).
.
948,951c
		anar = miir(ctlr, phyaddr, 0x04);
		anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
		anar &= anlpar;
		bmcr = 0;
		if(anar & 0x380)
			bmcr = 0x2000;
		if(anar & 0x0140)
			bmcr |= 0x0100;
.
857c
	int anar, anlpar, bmcr, bmsr, force, i, phyaddr, port, x;
.
## diffname pc/ether82557.c 1998/0522
## diff -e /n/emeliedump/1998/0511/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0522/sys/src/brazil/pc/ether82557.c
623a
				coherence();
.
595c
					bp->rp += sizeof(Rfd)-sizeof(rfd->data);
.
515c
		//ctlr->configdata[6] |= 0x40;
.
507c
		//ctlr->configdata[6] &= ~0x40;		/* !Discard Overrun Rx Frames */
.
487a
		coherence();
.
111c
	uchar	data[1700];
.
## diffname pc/ether82557.c 1998/0825
## diff -e /n/emeliedump/1998/0522/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0825/sys/src/brazil/pc/ether82557.c
434c
	snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
.
432c
	len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);
.
## diffname pc/ether82557.c 1998/0929
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0929/sys/src/brazil/pc/ether82557.c
1045c
			if(medium != -1)
.
1034,1043d
1032a
					break;

				case 0x05:			/* 100BASE-TXFD */
				case 0x08:			/* 100BASE-FXFD */
					bmcr |= 0x2000|0x0100;
					ctlr->configdata[19] |= 0x40;
					break;
.
1029,1031c
				for(k = 0; k < nelem(mediatable); k++){
					if(cistrcmp(mediatable[k], ether->opt[i]))
						continue;
					medium = k;
					break;
				}
		
				switch(medium){
				default:
					break;

				case 0x00:			/* 10BASE-T */
				case 0x01:			/* 10BASE-2 */
				case 0x02:			/* 10BASE-5 */
					bmcr &= ~(0x2000|0x0100);
					ctlr->configdata[19] &= ~0x40;
					break;

				case 0x03:			/* 100BASE-TX */
				case 0x06:			/* 100BASE-T4 */
				case 0x07:			/* 100BASE-FX */
					ctlr->configdata[19] &= ~0x40;
					bmcr |= 0x2000;
					break;

				case 0x04:			/* 10BASE-TFD */
					bmcr = (bmcr & ~0x2000)|0x0100;
.
1027c
			medium = -1;
.
859c
	int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, port, x;
.
855a
static char* mediatable[9] = {
	"10BASE-T",				/* TP */
	"10BASE-2",				/* BNC */
	"10BASE-5",				/* AUI */
	"100BASE-TX",
	"10BASE-TFD",
	"100BASE-TXFD",
	"100BASE-T4",
	"100BASE-FX",
	"100BASE-FXFD",
};

.
761a
	unlock(&ctlr->miilock);
.
753a
	lock(&ctlr->miilock);
.
741a
	unlock(&ctlr->miilock);
.
733a
	lock(&ctlr->miilock);
.
435a
	len += snprint(p+len, READSTR-len, "eeprom:");
	for(i = 0; i < 0x40; i++){
		if(i && ((i & 0x07) == 0))
			len += snprint(p+len, READSTR-len, "\n       ");
		len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]);
	}

	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
		phyaddr = ctlr->eeprom[6] & 0x00FF;
		len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);
		for(i = 0; i < 6; i++){
			static int miir(Ctlr*, int, int);

			len += snprint(p+len, READSTR-len, " %4.4uX",
				miir(ctlr, phyaddr, i));
		}
	}

	snprint(p+len, READSTR-len, "\n");
.
434c
	len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
.
382c
	int i, len, phyaddr;
.
194a
	Lock	miilock;

.
## diffname pc/ether82557.c 1998/0930
## diff -e /n/emeliedump/1998/0929/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/0930/sys/src/brazil/pc/ether82557.c
602,657c
			receive(ether);
.
577d
575a
	Ctlr *ctlr;
	int count;
	Block *bp, *pbp, *xbp;

	ctlr = ether->ctlr;
	bp = ctlr->rfdhead;
	for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
		/*
		 * If it's an OK receive frame
		 * 1) save the count 
		 * 2) if it's small, try to allocate a block and copy
		 *    the data, then adjust the necessary fields for reuse;
		 * 3) if it's big, try to allocate a new Rfd and if
		 *    successful
		 *	adjust the received buffer pointers for the
		 *	  actual data received;
		 *	initialise the replacement buffer to point to
		 *	  the next in the ring;
		 *	initialise bp to point to the replacement;
		 * 4) if there's a good packet, pass it on for disposal.
		 */
		if(rfd->field & RfdOK){
			pbp = nil;
			count = rfd->count & 0x3FFF;
			if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
				memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count);
				pbp->wp = pbp->rp + count;

				rfd->count = 0;
				rfd->field = 0;
			}
			else if(xbp = rfdalloc(rfd->link)){
				bp->rp += sizeof(Rfd)-sizeof(rfd->data);
				bp->wp = bp->rp + count;

				xbp->next = bp->next;
				bp->next = 0;

				pbp = bp;
				bp = xbp;
			}
			if(pbp != nil)
				etheriq(ether, pbp, 1);
		}
		else{
			rfd->count = 0;
			rfd->field = 0;
		}

		/*
		 * The ring tail pointer follows the head with with one
		 * unused buffer in between to defeat hardware prefetch;
		 * once the tail pointer has been bumped on to the next
		 * and the new tail has the Suspend bit set, it can be
		 * removed from the old tail buffer.
		 * As a replacement for the current head buffer may have
		 * been allocated above, ensure that the new tail points
		 * to it (next and link).
		 */
		rfd = (Rfd*)ctlr->rfdtail->rp;
		ctlr->rfdtail = ctlr->rfdtail->next;
		ctlr->rfdtail->next = bp;
		((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
		((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
		coherence();
		rfd->field &= ~RfdS;

		/*
		 * Finally done with the current (possibly replaced)
		 * head, move on to the next and maintain the sentinel
		 * between tail and head.
		 */
		ctlr->rfdhead = bp->next;
		bp = ctlr->rfdhead;
	}
}

static void
interrupt(Ureg*, void* arg)
{
.
573c
receive(Ether* ether)
.
## diffname pc/ether82557.c 1998/1024
## diff -e /n/emeliedump/1998/0930/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/1024/sys/src/brazil/pc/ether82557.c
1129a

		if(bmcr & 0x2000)
			ether->mbps = 100;
.
768c
	ctlr->threshold = 80;
.
678a
			iunlock(&ctlr->cblock);
		}
.
677c
		if(status & (StatFR|StatRNR)){
			ilock(&ctlr->cblock);
.
351a
		iunlock(&ctlr->cblock);
.
350d
347d
345a
		ilock(&ctlr->cblock);
.
229c
//	0x80,				/* Tx DMA maximum byte count */
	0x00,				/* Tx DMA maximum byte count */
.
## diffname pc/ether82557.c 1998/1126
## diff -e /n/emeliedump/1998/1024/sys/src/brazil/pc/ether82557.c /n/emeliedump/1998/1126/sys/src/brazil/pc/ether82557.c
972a
	csr8w(ctlr, Interrupt, InterruptM);
.
364a
		ilock(&ctlr->rlock);
		csr8w(ctlr, Interrupt, 0);
		iunlock(&ctlr->rlock);
.
## diffname pc/ether82557.c 1999/0314
## diff -e /n/emeliedump/1998/1126/sys/src/brazil/pc/ether82557.c /n/emeliedump/1999/0314/sys/src/brazil/pc/ether82557.c
908a
		pcisetbme(p);
.
## diffname pc/ether82557.c 1999/0714
## diff -e /n/emeliedump/1999/0314/sys/src/brazil/pc/ether82557.c /n/emeliedump/1999/0714/sys/src/brazil/pc/ether82557.c
908c
		port = p->mem[1].bar & ~0x01;
		if(ioalloc(port, p->mem[1].size, 0, "i82557pci") < 0){
			print("i82557pci: port %d in use\n", port);
			continue;
		}
		i82557adapter(&adapter, port, p->intl, p->tbdf);
.
899a
	int port;
.
## diffname pc/ether82557.c 2000/0317
## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/ether82557.c /n/emeliedump/2000/0317/sys/src/9/pc/ether82557.c
1016c
	else if(!(ctlr->eeprom[5] & 0xFF00))
		phyaddr = scanphy(ctlr);
	else
		phyaddr = -1;
	if(phyaddr >= 0){
.
1014c
	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
.
1011a
	 * Unfortunately, sometimes the EEPROM is blank except for
	 * the ether address and checksum; in this case look at the
	 * controller type and if 0 scan for the first PHY and try to
	 * use that.
.
931a
scanphy(Ctlr* ctlr)
{
	int i, oui, x;

	for(i = 0; i < 32; i++){
		if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
			continue;
		oui <<= 6;
		x = miir(ctlr, i, 3);
		oui |= x>>10;
		//print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1));

		ctlr->eeprom[6] = i;
		if(oui == 0xAA00)
			ctlr->eeprom[6] |= 0x07<<8;
		else if(oui == 0x80017){
			if(x & 0x01)
				ctlr->eeprom[6] |= 0x0A<<8;
			else
				ctlr->eeprom[6] |= 0x04<<8;
		}
		return i;
	}
	return -1;
}

static int
.
## diffname pc/ether82557.c 2000/0619
## diff -e /n/emeliedump/2000/0317/sys/src/9/pc/ether82557.c /n/emeliedump/2000/0619/sys/src/9/pc/ether82557.c
911c
			print("i82557pci: port 0x%uX in use\n", port);
.
## diffname pc/ether82557.c 2001/0301
## diff -e /n/emeliedump/2000/0619/sys/src/9/pc/ether82557.c /n/emeliedump/2001/0301/sys/src/9/pc/ether82557.c
1027c
	for(i = 0; i < (1<<ctlr->eepromsz); i++){
.
1025a
	hy93c46r(ctlr, 0);
.
1024a
	 * Do a dummy read first to get the size
	 * and allocate ctlr->eeprom.
.
869a
	if(ctlr->eepromsz == 0){
		ctlr->eepromsz = 8-size;
		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
		goto reread;
	}

.
847,848c
	/*
	 * First time through must work out the EEPROM size.
	 */
	if((size = ctlr->eepromsz) == 0)
		size = 8;

	for(size = size-1; size >= 0; size--){
		data = (((r>>size) & 0x01)<<2)|EEcs;
.
835a
reread:
.
828c
	int data, i, op, size;
.
443c
	for(i = 0; i < (1<<ctlr->eepromsz); i++){
.
193c
	int	eepromsz;		/* address size in bits */
	ushort*	eeprom;
.
93,94d
## diffname pc/ether82557.c 2001/0419
## diff -e /n/emeliedump/2001/0301/sys/src/9/pc/ether82557.c /n/emeliedump/2001/0419/sys/src/9/pc/ether82557.c
1237d
1065a
		break;
	case 0x04:			/* 82558 A-step */
	case 0x05:			/* 82558 B-step */
	case 0x06:			/* 82559 A-step */
	case 0x07:			/* 82559 B-step */
	case 0x08:			/* 82559 C-step */
	case 0x09:			/* 82559ER A-step */
		phyaddr = scanphy(ctlr);
		break;
	}
.
1064a
	switch(ctlr->pcidev->rid){
	case 0x01:			/* 82557 A-step */
	case 0x02:			/* 82557 B-step */
	case 0x03:			/* 82557 C-step */
	default:
.
1062,1063d
1056,1057c
	 * controller type and if it's am 82558 or 82559 it has an
	 * embedded PHY so scan for that.
.
1015,1017c
	ether->ctlr = ctlr;
	ether->port = ctlr->port;
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;
.
1008c
	 * Initialise the Ctlr structure.
.
1004c
	if(ctlr == nil)
.
1002d
990,999c
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(ether->port == 0 || ether->port == ctlr->port){
			ctlr->active = 1;
.
987c
	 * Any adapter matches if no ether->port is supplied,
.
983,984d
981c
	if(ctlrhead == nil)
.
979d
975,976d
973c
	int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
.
926c

		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = port;
		ctlr->pcidev = p;

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;

.
922,923c
		if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
			print("i82557: port 0x%uX in use\n", port);
.
912a
	Pcidev *p;
	Ctlr *ctlr;
.
911d
893,908d
885,891d
221a
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;

.
190a
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;

.
185a
typedef struct Ctlr Ctlr;
.
## diffname pc/ether82557.c 2001/0424
## diff -e /n/emeliedump/2001/0419/sys/src/9/pc/ether82557.c /n/emeliedump/2001/0424/sys/src/9/pc/ether82557.c
917,921c
		/*
		 *  this is backwards from what we really wanted to do
		 *  but this keeps the numbering of ethernet cards
		 *  consistent with 9load.
		 */
		ctlr->next = ctlrhead;
		ctlrhead = ctlr;
.
228d
## diffname pc/ether82557.c 2001/0527
## diff -e /n/emeliedump/2001/0424/sys/src/9/pc/ether82557.c /n/emeliedump/2001/0527/sys/src/9/pc/ether82557.c
383c
			snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
.
366c
	char name[KNAMELEN];
.
## diffname pc/ether82557.c 2001/0626
## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/ether82557.c /n/emeliedump/2001/0626/sys/src/9/pc/ether82557.c
916,922c
		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
.
227a
static Ctlr* ctlrtail;
.
## diffname pc/ether82557.c 2001/1014
## diff -e /n/emeliedump/2001/0626/sys/src/9/pc/ether82557.c /n/emeliedump/2001/1014/sys/src/9/pc/ether82557.c
901c
	while(p = pcimatch(p, 0x8086, 0)){
		switch(p->did){
		default:
			continue;
		case 0x1229:		/* Intel 8255[789] */
		case 0x1031:		/* Intel 82562EM */
			break;
		}

.
## diffname pc/ether82557.c 2001/1016
## diff -e /n/emeliedump/2001/1014/sys/src/9/pc/ether82557.c /n/emeliedump/2001/1016/sys/src/9/pc/ether82557.c
906a
		case 0x2449:		/* Intel ????? */
.
## diffname pc/ether82557.c 2001/1017
## diff -e /n/emeliedump/2001/1016/sys/src/9/pc/ether82557.c /n/emeliedump/2001/1017/sys/src/9/pc/ether82557.c
907c
		case 0x2449:		/* Intel 82562ET */
.
904a
		case 0x1209:		/* INtel 82559ER */
.
## diffname pc/ether82557.c 2002/0109
## diff -e /n/emeliedump/2001/1017/sys/src/9/pc/ether82557.c /n/emeliedump/2002/0109/sys/src/9/pc/ether82557.c
1249a
	ether->shutdown = shutdown;
.
975a
static void
shutdown(Ether* ether)
{
	Ctlr *ctlr = ether->ctlr;

print("ether82557 shutting down\n");
	csr32w(ctlr, Port, 0);
	delay(1);
	csr8w(ctlr, Interrupt, InterruptM);
}


.
## diffname pc/ether82557.c 2002/0131
## diff -e /n/emeliedump/2002/0109/sys/src/9/pc/ether82557.c /n/emeliedump/2002/0131/sys/src/9/pc/ether82557.c
905c
		case 0x1209:		/* Intel 82559ER */
.
## diffname pc/ether82557.c 2002/0711
## diff -e /n/emeliedump/2002/0131/sys/src/9/pc/ether82557.c /n/emeliedump/2002/0711/sys/src/9/pc/ether82557.c
1232c
		ether->oq = qopen(256*1024, Qmsg, 0, 0);
.
## diffname pc/ether82557.c 2002/0920
## diff -e /n/emeliedump/2002/0711/sys/src/9/pc/ether82557.c /n/emeliedump/2002/0920/sys/src/9/pc/ether82557.c
282,283c
	if(waitfordone(ctlr) < 0)
		print("82557: lastcmd %x, cmd %x(%x) \n", ctlr->command, c, v);
.
269a
	 *  on a 10 Mbs half-duplex connection, we seem to
	 *  hang unless we do a nop before each command.
	 *  This comes from a fix for a chip errata that jmk
	 *  saw in a Linux driver.
	 */
	if(waitfordone(ctlr) < 0)
		print("82557: lastcmd %x, cmd nop \n", ctlr->command);
	csr8w(ctlr, CommandR, CUnop);

	/*
.
263a
int
waitfordone(Ctlr *ctlr)
{
	int loops;

	/* fast wait, we usually get lucky */
	for(loops = 0; loops < 100; loops++){
		if(csr8r(ctlr, CommandR) == 0)
			return 0;
	}

	/* slow wait */
	for(loops = 0; loops < 1000; loops++){
		if(csr8r(ctlr, CommandR) == 0)
			return 0;
		microdelay(1);
	}
	return -1;
}

.
## diffname pc/ether82557.c 2002/0921
## diff -e /n/emeliedump/2002/0920/sys/src/9/pc/ether82557.c /n/emeliedump/2002/0921/sys/src/9/pc/ether82557.c
1256a

	/*
	 * Workaround for some broken HUB chips when connected at 10Mb/s
	 * half-duplex.
	 * This is a band-aid, but as there's no dynamic auto-negotiation
	 * code at the moment, only deactivate the workaround code in txstart
	 * if the link is 100Mb/s.
	 */
	if(ether->mbps != 10)
		ctlr->nop = 0;
.
955a
		ctlr->nop = nop;
.
938a
			nop = 1;
			/*FALLTHROUGH*/
		case 0x1209:		/* Intel 82559ER */
		case 0x1229:		/* Intel 8255[789] */
.
935,936d
930a
	nop = 0;
.
928a
	int nop, port;
.
926d
554a

	/*
	 * Workaround for some broken HUB chips
	 * when connected at 10Mb/s half-duplex.
	 */
	if(ctlr->nop){
		command(ctlr, CUnop, 0);
		microdelay(1);
	}
.
472a
	len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);
.
312,313c
	for(timeo = 0; timeo < 100; timeo++){
		if(!csr8r(ctlr, CommandR))
			break;
		microdelay(1);
	}
	if(timeo >= 100){
		ctlr->command = -1;
		iunlock(&ctlr->rlock);
		iprint("i82557: command 0x%uX %uX timeout\n", c, v);
		return;
	}
.
290,299d
286a
	int timeo;

.
264,283d
212a
	int	nop;
.
## diffname pc/ether82557.c 2003/0407
## diff -e /n/emeliedump/2002/0921/sys/src/9/pc/ether82557.c /n/emeliedump/2003/0407/sys/src/9/pc/ether82557.c
352,353c
		tsleep(&up->sleep, return0, 0, 4000);
.
201d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].