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

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


## diffname pc/etherelnk3.c 1996/0607
## diff -e /dev/null /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c
0a
/*
 * Etherlink III and Fast EtherLink adapters.
 * To do:
 *	check robustness in the face of errors;
 *	autoSelect;
 *	PCI latency timer and master enable;
 *	errata list.
 *
 * Product ID:
 *	9150 ISA	3C509[B]
 *	9050 ISA	3C509[B]-TP
 *	9450 ISA	3C509[B]-COMBO
 *	9550 ISA	3C509[B]-TPO
 *
 *	9350 EISA	3C579
 *	9250 EISA	3C579-TP
 *
 *	5920 EISA	3C592-[TP|COMBO|TPO]
 *	5970 EISA	3C597-TX	Fast Etherlink 10BASE-T/100BASE-TX
 *	5971 EISA	3C597-T4	Fast Etherlink 10BASE-T/100BASE-T4
 *	5972 EISA	3C597-MII	Fast Etherlink 10BASE-T/MII
 *
 *	5900 PCI	3C590-[TP|COMBO|TPO]
 *	5950 PCI	3C595-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
 *	5951 PCI	3C595-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
 *	5952 PCI	3C595-MII	Fast Etherlink 10BASE-T/MII
 *
 *	9058 PCMCIA	3C589[B]-[TP|COMBO]
 *
 *	627C MCA	3C529
 *	627D MCA	3C529-TP
 */
#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 {
	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
};

enum {						/* all windows */
	Command			= 0x000E,
	IntStatus		= 0x000E,
};

enum {						/* Commands */
	GlobalReset		= 0x0000,
	SelectRegisterWindow	= 0x0001,	
	EnableDcConverter	= 0x0002,
	RxDisable		= 0x0003,
	RxEnable		= 0x0004,
	RxReset			= 0x0005,
	TxDone			= 0x0007,	
	RxDiscard		= 0x0008,
	TxEnable		= 0x0009,
	TxDisable		= 0x000A,
	TxReset			= 0x000B,
	RequestInterrupt	= 0x000C,
	AcknowledgeInterrupt	= 0x000D,
	SetInterruptEnable	= 0x000E,
	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
	SetRxFilter		= 0x0010,
	SetRxEarlyThresh	= 0x0011,
	SetTxAvailableThresh	= 0x0012,
	SetTxStartThresh	= 0x0013,
	StartDma		= 0x0014,	/* initiate busmaster operation */
	StatisticsEnable	= 0x0015,
	StatisticsDisable	= 0x0016,
	DisableDcConverter	= 0x0017,
	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
	PowerUp			= 0x001B,	/* not all adapters */
	PowerDownFull		= 0x001C,	/* not all adapters */
	PowerAuto		= 0x001D,	/* not all adapters */
};

enum {						/* (Global|Rx|Tx)Reset command bits */
	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
	networkReset		= 0x0004,	/* network interface logic */
	fifoReset		= 0x0008,	/* FIFO control logic */
	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
	hostReset		= 0x0020,	/* bus interface logic */
	dmaReset		= 0x0040,	/* bus master logic */
	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */

	resetMask		= 0x00FF,
};

enum {						/* SetRxFilter command bits */
	receiveIndividual	= 0x0001,	/* match station address */
	receiveMulticast	= 0x0002,
	receiveBroadcast	= 0x0004,
	receiveAllFrames	= 0x0008,	/* promiscuous */
};

enum {						/* StartDma command bits */
	Upload			= 0x0000,	/* transfer data from adapter to memory */
	Download		= 0x0001,	/* transfer data from memory to adapter */
};

enum {						/* IntStatus bits */
	interruptLatch		= 0x0001,
	hostError		= 0x0002,	/* Adapter Failure */
	txComplete		= 0x0004,
	txAvailable		= 0x0008,
	rxComplete		= 0x0010,
	rxEarly			= 0x0020,
	intRequested		= 0x0040,
	updateStats		= 0x0080,
	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
	busMasterInProgress	= 0x0800,
	commandInProgress	= 0x1000,

	interruptMask		= 0x01FE,
};

#define COMMAND(port, cmd, a)	outs((port)+Command, ((cmd)<<11)|(a))
#define STATUS(port)		ins((port)+IntStatus)

enum {						/* Window 0 - setup */
	Wsetup			= 0x0000,
						/* registers */
	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
	EepromCommand		= 0x000A,
	EepromData		= 0x000C,
						/* AddressConfig Bits */
	xcvrMask9		= 0xC000,
						/* ConfigControl bits */
	Ena			= 0x0001,
						/* EepromCommand bits */
	EepromReadRegister	= 0x0080,
	EepromBusy		= 0x8000,
};

#define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
#define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
#define EEPROMDATA(port)	ins((port)+EepromData)

enum {						/* Window 1 - operating set */
	Wop			= 0x0001,
						/* registers */
	Fifo			= 0x0000,
	RxError			= 0x0004,	/* 3C59[0257] only */
	RxStatus		= 0x0008,
	Timer			= 0x000A,
	TxStatus		= 0x000B,
	TxFree			= 0x000C,
						/* RxError bits */
	rxOverrun		= 0x0001,
	runtFrame		= 0x0002,
	alignmentError		= 0x0004,	/* Framing */
	crcError		= 0x0008,
	oversizedFrame		= 0x0010,
	dribbleBits		= 0x0080,
						/* RxStatus bits */
	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
	rxOverrun9		= 0x0000,
	oversizedFrame9		= 0x0800,
	dribbleBits9		= 0x1000,
	runtFrame9		= 0x1800,
	alignmentError9		= 0x2000,	/* Framing */
	crcError9		= 0x2800,
	rxError			= 0x4000,
	rxIncomplete		= 0x8000,
						/* TxStatus Bits */
	txStatusOverflow	= 0x0004,
	maxCollisions		= 0x0008,
	txUnderrun		= 0x0010,
	txJabber		= 0x0020,
	interruptRequested	= 0x0040,
	txStatusComplete	= 0x0080,
};

enum {						/* Window 2 - station address */
	Wstation		= 0x0002,
};

enum {						/* Window 3 - FIFO management */
	Wfifo			= 0x0003,
						/* registers */
	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
	OtherInt		= 0x0004,	/* 3C59[0257] */
	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
	MacControl		= 0x0006,	/* 3C59[0257] */
	ResetOptions		= 0x0008,	/* 3C59[0257] */
	RxFree			= 0x000A,
						/* InternalConfig bits */
	xcvr10BaseT		= 0x00000000,
	xcvrAui			= 0x00100000,	/* 10BASE5 */
	xcvr10Base2		= 0x00300000,
	xcvr100BaseTX		= 0x00400000,
	xcvr100BaseFX		= 0x00500000,
	xcvrMii			= 0x00600000,
	xcvrMask		= 0x00700000,
	autoSelect		= 0x01000000,
						/* MacControl bits */
	deferExtendEnable	= 0x0001,
	deferTimerSelect	= 0x001E,	/* mask */
	fullDuplexEnable	= 0x0020,
	allowLargePackets	= 0x0040,
						/* ResetOptions bits */
	baseT4Available		= 0x0001,
	baseTXAvailable		= 0x0002,
	baseFXAvailable		= 0x0004,
	base10TAvailable	= 0x0008,
	coaxAvailable		= 0x0010,
	auiAvailable		= 0x0020,
	miiAvailable		= 0x0040,
};

enum {						/* Window 4 - diagnostic */
	Wdiagnostic		= 0x0004,
						/* registers */
	VcoDiagnostic		= 0x0002,
	FifoDiagnostic		= 0x0004,
	NetworkDiagnostic	= 0x0006,
	PhysicalMgmt		= 0x0008,
	MediaStatus		= 0x000A,
	BadSSD			= 0x000C,
						/* FifoDiagnostic bits */
	txOverrun		= 0x0400,
	rxUnderrun		= 0x2000,
	receiving		= 0x8000,
						/* MediaStatus bits */
	dataRate100		= 0x0002,
	crcStripDisable		= 0x0004,
	enableSqeStats		= 0x0008,
	collisionDetect		= 0x0010,
	carrierSense		= 0x0020,
	jabberGuardEnable	= 0x0040,
	linkBeatEnable		= 0x0080,
	jabberDetect		= 0x0200,
	polarityReversed	= 0x0400,
	linkBeatDetect		= 0x0800,
	txInProg		= 0x1000,
	dcConverterEnabled	= 0x4000,
	auiDisable		= 0x8000,
};

enum {						/* Window 5 - internal state */
	Wstate			= 0x0005,
						/* registers */
	TxStartThresh		= 0x0000,
	TxAvalableThresh	= 0x0002,
	RxEarlyThresh		= 0x0006,
	RxFilter		= 0x0008,
	InterruptEnable		= 0x000A,
	IndicationEnable	= 0x000C,
};

enum {						/* Window 6 - statistics */
	Wstatistics		= 0x0006,
						/* registers */
	CarrierLost		= 0x0000,
	SqeErrors		= 0x0001,
	MultipleColls		= 0x0002,
	SingleCollFrames	= 0x0003,
	LateCollisions		= 0x0004,
	RxOverruns		= 0x0005,
	FramesXmittedOk		= 0x0006,
	FramesRcvdOk		= 0x0007,
	FramesDeferred		= 0x0008,
	UpperFramesOk		= 0x0009,
	BytesRcvdOk		= 0x000A,
	BytesXmittedOk		= 0x000C,
};

enum {						/* Window 7 - bus master operations */
	Wmaster			= 0x0007,
						/* registers */
	MasterAddress		= 0x0000,
	MasterLen		= 0x0006,
	MasterStatus		= 0x000C,
						/* MasterStatus bits */
	masterAbort		= 0x0001,
	targetAbort		= 0x0002,
	targetRetry		= 0x0004,
	targetDisc		= 0x0008,
	masterDownload		= 0x1000,
	masterUpload		= 0x4000,
	masterInProgress	= 0x8000,

	masterMask		= 0xD00F,
};	

typedef struct {
	Lock	wlock;				/* window access */

	int	attached;
	int	busmaster;
	Block*	rbp[2];				/* receive buffers */
	int	rbpix;

	Block*	txqhead;			/* transmit queue */
	Block*	txqtail;
	int	txthreshold;
	int	txbusy;

	long	interrupts;			/* statistics */
	long	timer;
	
	long	carrierlost;
	long	sqeerrors;
	long	multiplecolls;
	long	singlecollframes;
	long	latecollisions;
	long	rxoverruns;
	long	framesxmittedok;
	long	framesrcvdok;
	long	framesdeferred;
	long	bytesrcvdok;
	long	bytesxmittedok;
	long	badssd;

	int	xcvr;				/* transceiver type */
	int	rxstatus9;			/* old-style RxStatus register */
} Ctlr;

static Block*
allocrbp(void)
{
	Block *bp;
	ulong addr;

	/*
	 * The receive buffers must be on a 32-byte
	 * boundary for EISA busmastering.
	 */
	bp = allocb(ROUNDUP(sizeof(Etherpkt), 4) + 31);
	addr = (ulong)bp->base;
	addr = ROUNDUP(addr, 32);
	bp->rp = (uchar*)addr;

	return bp;
}

static uchar*
startdma(Ether* ether, ulong address)
{
	int port, status, w;
	uchar *wp;

	port = ether->port;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wmaster);

	wp = KADDR(inl(port+MasterAddress));
	status = ins(port+MasterStatus);
	if(status & (masterInProgress|targetAbort|masterAbort))
		print("elnk3#%d: BM status 0x%uX\n", ether->ctlrno, status);
	outs(port+MasterStatus, masterMask);
	outl(port+MasterAddress, address);
	outs(port+MasterLen, sizeof(Etherpkt));
	COMMAND(port, StartDma, Upload);

	COMMAND(port, SelectRegisterWindow, w);
	return wp;
}

static void
promiscuous(void* arg, int on)
{
	int filter, port;

	port = ((Ether*)arg)->port;
	
	filter = receiveBroadcast|receiveIndividual;
	if(on)
		filter |= receiveAllFrames;
	COMMAND(port, SetRxFilter, filter);
}

static void
attach(Ether* ether)
{
	int port, x;
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->wlock);
	if(ctlr->attached){
		iunlock(&ctlr->wlock);
		return;
	}

	port = ether->port;

	/*
	 * Set the receiver packet filter for this and broadcast addresses,
	 * set the interrupt masks for all interrupts, enable the receiver
	 * and transmitter.
	 */
	promiscuous(ether, ether->prom);

	x = interruptMask|interruptLatch;
	if(ctlr->busmaster)
		x &= ~(rxEarly|rxComplete);
	COMMAND(port, SetIndicationEnable, x);
	COMMAND(port, SetInterruptEnable, x);

	COMMAND(port, RxEnable, 0);
	COMMAND(port, TxEnable, 0);

	/*
	 * Prime the busmaster channel for receiving directly into a
	 * receive packet buffer if necessary.
	 */
	ctlr->rbpix = 0;
	if(ctlr->busmaster)
		startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));

	ctlr->attached = 1;
	iunlock(&ctlr->wlock);
}

static void
statistics(Ether* ether)
{
	int port, u, w;
	Ctlr *ctlr;

	port = ether->port;
	ctlr = ether->ctlr;

	/*
	 * 3C59[27] require a read between a PIO write and
	 * reading a statistics register.
	 */
	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wstatistics);
	STATUS(port);

	ctlr->carrierlost += inb(port+CarrierLost) & 0xFF;
	ctlr->sqeerrors += inb(port+SqeErrors) & 0xFF;
	ctlr->multiplecolls += inb(port+MultipleColls) & 0xFF;
	ctlr->singlecollframes += inb(port+SingleCollFrames) & 0xFF;
	ctlr->latecollisions += inb(port+LateCollisions) & 0xFF;
	ctlr->rxoverruns += inb(port+RxOverruns) & 0xFF;
	ctlr->framesxmittedok += inb(port+FramesXmittedOk) & 0xFF;
	ctlr->framesrcvdok += inb(port+FramesRcvdOk) & 0xFF;
	u = inb(port+UpperFramesOk) & 0xFF;
	ctlr->framesxmittedok += (u & 0x30)<<4;
	ctlr->framesrcvdok += (u & 0x03)<<8;
	ctlr->framesdeferred += inb(port+FramesDeferred) & 0xFF;
	ctlr->bytesrcvdok += ins(port+BytesRcvdOk) & 0xFFFF;
	ctlr->bytesxmittedok += ins(port+BytesXmittedOk) & 0xFFFF;

	if(ctlr->xcvr == xcvr100BaseTX || ctlr->xcvr == xcvr100BaseFX){
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		STATUS(port);
		ctlr->badssd += inb(port+BadSSD);
	}

	COMMAND(port, SelectRegisterWindow, w);
}

static void
transmit(Ether* ether)
{
	int port, len;
	Ctlr *ctlr;
	Block *bp;

	port = ether->port;
	ctlr = ether->ctlr;

	/*
	 * Attempt to top-up the transmit FIFO. If there's room simply
	 * stuff in the packet length (unpadded to a dword boundary), the
	 * packet data (padded) and remove the packet from the queue.
	 * If there's no room post an interrupt for when there is.
	 * This routine is called both from the top level and from interrupt
	 * level and expects to be called with ctlr->wlock already locked
	 * and the correct register window (Wop) in place.
	 */
	while(bp = ctlr->txqhead){
		len = ROUNDUP(BLEN(bp), 4);
		if(len+4 <= ins(port+TxFree)){
			outl(port+Fifo, BLEN(bp));
			outsl(port+Fifo, bp->rp, len/4);

			ctlr->txqhead = bp->next;
			freeb(bp);

			ether->outpackets++;
		}
		else if(ctlr->txbusy == 0){
			ctlr->txbusy = 1;
			COMMAND(port, SetTxAvailableThresh, len);
			return;
		}
	}
}

static long
write(Ether* ether, void* buf, long n)
{
	Ctlr *ctlr;
	Block *bp;
	int port, w;

	port = ether->port;
	ctlr = ether->ctlr;

	/*
	 * Pack the write request up in a buffer, give it a source address
	 * and place it on the end of the transmit queue. The data written to the
	 * FIFO must be padded to a dword boundary, hence the ROUNDUP allocation.
	 * Call transmit() to stuff it into the TxFIFO if possible. 
	 */
	bp = allocb(ROUNDUP(n, 4));
	memmove(bp->wp, buf, n);
	memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
	bp->wp += n;

	ilock(&ctlr->wlock);
	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wop);
	if(ctlr->txqhead == 0)
		ctlr->txqhead = bp;
	else
		ctlr->txqtail->next = bp;
	ctlr->txqtail = bp;
	if(ctlr->txbusy == 0)
		transmit(ether);
	COMMAND(port, SelectRegisterWindow, w);
	iunlock(&ctlr->wlock);

	return n;
}

static void
receive(Ether* ether)
{
	int len, port, rxerror, rxstatus;
	Ctlr *ctlr;
	Block *bp;

	port = ether->port;
	ctlr = ether->ctlr;

	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
		if(ctlr->busmaster && (STATUS(port) & busMasterInProgress))
			break;

		/*
		 * If there was an error, log it and continue.
		 * Unfortunately the 3C5[078]9 has the error info in the status register
		 * and the 3C59[0257] implement a separate RxError register.
		 */
		if(rxstatus & rxError){
			if(ctlr->rxstatus9){
iprint("R%uX|", rxstatus);
				switch(rxstatus & rxError9){

				case rxOverrun9:
					ether->overflows++;
					break;

				case oversizedFrame9:
				case runtFrame9:
					ether->buffs++;
					break;

				case alignmentError9:
					ether->frames++;
					break;

				case crcError9:
					ether->crcs++;
					break;

				}
			}
			else{
				rxerror = inb(port+RxError);
iprint("R%uX|", rxerror);
				if(rxerror & rxOverrun)
					ether->overflows++;
				if(rxerror & (oversizedFrame|runtFrame))
					ether->buffs++;
				if(rxerror & alignmentError)
					ether->frames++;
				if(rxerror & crcError)
					ether->crcs++;
			}

			COMMAND(port, RxDiscard, 0);
			while(STATUS(port) & commandInProgress)
				;

			if(ctlr->busmaster)
				startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
		}
		else{
			ether->inpackets++;
			bp = ctlr->rbp[ctlr->rbpix];

			if(ctlr->busmaster == 0){
				len = (rxstatus & rxBytes9);
				bp->wp = bp->rp + len;
				insl(port+Fifo, bp->rp, HOWMANY(len, 4));
			}

			COMMAND(port, RxDiscard, 0);
			while(STATUS(port) & commandInProgress)
				;

			if(ctlr->busmaster){
				ctlr->rbpix ^= 1;
				bp->wp = startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
			}

			etherrloop(ether, (Etherpkt*)bp->rp, BLEN(bp));
		}
	}
}

static void
interrupt(Ureg*, void* arg)
{
	Ether *ether;
	int port, status, txstatus, w, x;
	Ctlr *ctlr;

	ether = arg;
	port = ether->port;
	ctlr = ether->ctlr;

	lock(&ctlr->wlock);
	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wop);

	ctlr->interrupts++;
	ctlr->timer += inb(port+Timer) & 0xFF;
	for(;;){
		/*
		 * Clear the interrupt latch.
		 * It's possible to receive a packet and for another
		 * to become complete before exiting the interrupt
		 * handler so this must be done first to ensure another
		 * interrupt will occur.
		 */
		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
		status = STATUS(port);
		if((status & interruptMask) == 0)
			break;

		if(status & hostError){
			/*
			 * Adapter failure, try to find out why, reset if
			 * necessary. What happens if Tx is active and a reset
			 * occurs, need to retransmit? This probably isn't right.
			 */
			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
			x = ins(port+FifoDiagnostic);
			COMMAND(port, SelectRegisterWindow, Wop);
			print("elnk3#%d: status 0x%uX, diag 0x%uX\n",
			    ether->ctlrno, status, x);

			if(x & txOverrun){
				COMMAND(port, TxReset, 0);
				COMMAND(port, TxEnable, 0);
				wakeup(&ether->tr);
			}

			if(x & rxUnderrun){
				/*
				 * This shouldn't happen...
				 * Need to restart any busmastering?
				 */
				COMMAND(port, RxReset, 0);
				while(STATUS(port) & commandInProgress)
					;
				COMMAND(port, RxEnable, 0);
			}

			status &= ~hostError;
		}

		if(status & (transferInt|rxComplete)){
			receive(ether);
			status &= ~(transferInt|rxComplete);
		}

		if(status & txComplete){
			/*
			 * Pop the TxStatus stack, accumulating errors.
			 * Adjust the TX start threshold if there was an underrun.
			 * If there was a Jabber or Underrun error, reset
			 * the transmitter.
			 * For all conditions enable the transmitter.
			 */
			txstatus = 0;
			do{
				if(x = inb(port+TxStatus))
					outb(port+TxStatus, 0);
				txstatus |= x;
			}while(STATUS(port) & txComplete);

			if(txstatus & txUnderrun){
				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
				while(ins(port+MediaStatus) & txInProg)
					;
				COMMAND(port, SelectRegisterWindow, Wop);
				if(ctlr->txthreshold < ETHERMAXTU)
					ctlr->txthreshold += ETHERMINTU;
			}

			if(txstatus & (txJabber|txUnderrun)){
				COMMAND(port, TxReset, 0);
				while(STATUS(port) & commandInProgress)
					;
				COMMAND(port, SetTxStartThresh, ctlr->txthreshold);
			}
			COMMAND(port, TxEnable, 0);
			ether->oerrs++;
			status &= ~txComplete;
			status |= txAvailable;
		}

		if(status & txAvailable){
			COMMAND(port, AcknowledgeInterrupt, txAvailable);
			ctlr->txbusy = 0;
			transmit(ether);
			status &= ~txAvailable;
		}

		if(status & updateStats){
			statistics(ether);
			status &= ~updateStats;
		}

		/*
		 * Panic if there are any interrupts not dealt with.
		 */
		if(status & interruptMask)
			panic("elnk3#%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
	}

	COMMAND(port, SelectRegisterWindow, w);
	unlock(&ctlr->wlock);
}

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr;
	char buf[512];
	int len;

	if(n == 0)
		return 0;

	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	statistics(ether);
	iunlock(&ctlr->wlock);

	len = sprint(buf, "interrupts: %ld\n", ctlr->interrupts);
	len += sprint(buf+len, "timer: %ld\n", ctlr->timer);
	len += sprint(buf+len, "carrierlost: %ld\n", ctlr->carrierlost);
	len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->sqeerrors);
	len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->multiplecolls);
	len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->singlecollframes);
	len += sprint(buf+len, "latecollisions: %ld\n", ctlr->latecollisions);
	len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->rxoverruns);
	len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->framesxmittedok);
	len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->framesrcvdok);
	len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->framesdeferred);
	len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->bytesrcvdok);
	len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->bytesxmittedok);
	sprint(buf+len, "badssd: %ld\n", ctlr->badssd);

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

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

/*
 * Write two 0 bytes to identify the IDport and then reset the
 * ID sequence. Then send the ID sequence to the card to get
 * the card into command state.
 */
static void
idseq(void)
{
	int i;
	uchar al;
	static int reset, untag;

	/*
	 * One time only:
	 *	reset any adapters listening
	 */
	if(reset == 0){
		outb(IDport, 0);
		outb(IDport, 0);
		outb(IDport, 0xC0);
		delay(20);
		reset = 1;
	}

	outb(IDport, 0);
	outb(IDport, 0);
	for(al = 0xFF, i = 0; i < 255; i++){
		outb(IDport, al);
		if(al & 0x80){
			al <<= 1;
			al ^= 0xCF;
		}
		else
			al <<= 1;
	}

	/*
	 * One time only:
	 *	write ID sequence to get the attention of all adapters;
	 *	untag all adapters.
	 * If we do a global reset here on all adapters we'll confuse any
	 * ISA cards configured for EISA mode.
	 */
	if(untag == 0){
		outb(IDport, 0xD0);
		untag = 1;
	}
}

static ulong
activate(void)
{
	int i;
	ushort x, acr;

	/*
	 * Do the little configuration dance:
	 *
	 * 2. write the ID sequence to get to command state.
	 */
	idseq();

	/*
	 * 3. Read the Manufacturer ID from the EEPROM.
	 *    This is done by writing the IDPort with 0x87 (0x80
	 *    is the 'read EEPROM' command, 0x07 is the offset of
	 *    the Manufacturer ID field in the EEPROM).
	 *    The data comes back 1 bit at a time.
	 *    We seem to need a delay here between reading the bits.
	 *
	 * If the ID doesn't match, there are no more adapters.
	 */
	outb(IDport, 0x87);
	delay(20);
	for(x = 0, i = 0; i < 16; i++){
		delay(20);
		x <<= 1;
		x |= inb(IDport) & 0x01;
	}
	if(x != 0x6D50)
		return 0;

	/*
	 * 3. Read the Address Configuration from the EEPROM.
	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
	 */
	outb(IDport, 0x88);
	for(acr = 0, i = 0; i < 16; i++){
		delay(20);
		acr <<= 1;
		acr |= inb(IDport) & 0x01;
	}

	return (acr & 0x1F)*0x10 + 0x200;
}

static ulong
tcm509isa(Ether* ether)
{
	int irq, port;
	Adapter *ap;

	/*
	 * Attempt to activate adapters until one matches the
	 * address criteria. If adapter is set for EISA mode (0x3F0),
	 * tag it and ignore. Otherwise, activate it fully.
	 */
	while(port = activate()){
		/*
		 * 6. Tag the adapter so it won't respond in future.
		 */
		outb(IDport, 0xD1);
		if(port == 0x3F0)
			continue;

		/*
		 * 6. Activate the adapter by writing the Activate command
		 *    (0xFF).
		 */
		outb(IDport, 0xFF);
		delay(20);

		/*
		 * 8. Can now talk to the adapter's I/O base addresses.
		 *    Use the I/O base address from the acr just read.
		 *
		 *    Enable the adapter and clear out any lingering status
		 *    and interrupts.
		 */
		while(STATUS(port) & commandInProgress)
			;
		COMMAND(port, SelectRegisterWindow, Wsetup);
		outs(port+ConfigControl, Ena);

		COMMAND(port, TxReset, 0);
		COMMAND(port, RxReset, 0);
		COMMAND(port, AcknowledgeInterrupt, 0xFF);

		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
		if(ether->port == 0 || ether->port == port){
			ether->irq = irq;
			return port;
		}

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

	return 0;
}

static int
tcm5XXeisa(Ether* ether)
{
	static int slot = 1;
	ushort x;
	int irq, port;
	Adapter *ap;

	/*
	 * First time through, check if this is an EISA machine.
	 * If not, nothing to do.
	 */
	if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
		return 0;

	/*
	 * Continue through the EISA slots looking for a match on both
	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
	 * If we find an adapter, select window 0, enable it and clear
	 * out any lingering status and interrupts.
	 */
	while(slot < MaxEISA){
		port = slot++*0x1000;
		if(ins(port+0xC80+ManufacturerID) != 0x6D50)
			continue;
		x = ins(port+0xC80+ProductID);
		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
			continue;

		COMMAND(port, SelectRegisterWindow, Wsetup);
		outs(port+ConfigControl, Ena);

		COMMAND(port, TxReset, 0);
		COMMAND(port, RxReset, 0);
		COMMAND(port, AcknowledgeInterrupt, 0xFF);

		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
		if(ether->port == 0 || ether->port == port){
			ether->irq = irq;
			return port;
		}

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

	return 0;
}

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

	for(;;){
		pcicfg.vid = 0x10B7;
		pcicfg.did = 0;
		if((devno = pcimatch(0, devno, &pcicfg)) == -1)
			break;
		port = pcicfg.baseaddr[0] & ~0x01;
		COMMAND(port, GlobalReset, 0);
		while(STATUS(port) & commandInProgress)
			;
		irq = pcicfg.irq;
		if(ether->port == 0 || ether->port == port){
			ether->irq = irq;
			return port;
		}

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

	return 0;
}

static int
tcm5XXpcmcia(Ether* ether)
{
	if(cistrcmp(ether->type, "3C589") == 0)
		return ether->port;

	return 0;
}

int
etherelnk3reset(Ether* ether)
{
	int busmaster, i, port, rxstatus9, x, xcvr;
	Adapter *ap, **app;
	uchar ea[Eaddrlen];
	Ctlr *ctlr;

	/*
	 * 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 adapter on PCI, EISA and finally
	 * using the little ISA configuration dance. The EISA and ISA scan
	 * routines leave Wsetup mapped.
	 * If an adapter is found save the IRQ and transceiver type.
	 */
	port = 0;
	rxstatus9 = 0;
	xcvr = 0;
	for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
		if(ether->port == 0 || ether->port == ap->port){
			port = ap->port;
			ether->irq = ap->irq;
			*app = ap->next;
			free(ap);
			break;
		}
	}
	if(port == 0 && (port = tcm5XXpcmcia(ether))){
		xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
		rxstatus9 = 1;
	}
	else if(port == 0 && (port = tcm59Xpci(ether))){
		COMMAND(port, SelectRegisterWindow, Wfifo);
		xcvr = inl(port+InternalConfig) & xcvrMask;
	}
	else if(port == 0 && (port = tcm5XXeisa(ether))){
		x = ins(port+ProductID);
		if((x & 0xFF00) == 0x5900){
			COMMAND(port, SelectRegisterWindow, Wfifo);
			xcvr = inl(port+InternalConfig) & xcvrMask;
		}
		else{
			xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
			rxstatus9 = 1;
		}
	}
	else if(port == 0 && (port = tcm509isa(ether))){
		xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
		rxstatus9 = 1;
	}

	if(port == 0)
		return -1;

	/*
	 * 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 in Wstation. The EEPROM returns 16-bits at a time.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
		COMMAND(port, SelectRegisterWindow, Wsetup);
		while(EEPROMBUSY(port))
			;
		for(i = 0; i < Eaddrlen/2; i++){
			EEPROMCMD(port, EepromReadRegister, i);
			while(EEPROMBUSY(port))
				;
			x = EEPROMDATA(port);
			ether->ea[2*i] = (x>>8) & 0xFF;
			ether->ea[2*i+1] = x & 0xFF;
		}
	}

	COMMAND(port, SelectRegisterWindow, Wstation);
	for(i = 0; i < Eaddrlen; i++)
		outb(port+i, ether->ea[i]);

	/*
	 * Enable the transceiver if necessary and determine whether
	 * busmastering can be used. Due to bugs in the first revision
	 * of the 3C59[05], don't use busmastering at 10Mbps.
	 */
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
	outs(port+MediaStatus, x);
	if(x & dataRate100)
		busmaster = 1;
	else
		busmaster = 0;
	switch(xcvr){

	case xcvr100BaseTX:
	case xcvr100BaseFX:
		ether->mbps = 100;
		/*FALLTHROUGH*/
	case xcvr10BaseT:
		/*
		 * Enable Link Beat and Jabber to start the
		 * transceiver.
		 */
		x |= linkBeatEnable|jabberGuardEnable;
		outs(port+MediaStatus, x);
		break;

	case xcvr10Base2:
		/*
		 * Start the DC-DC converter.
		 * Wait > 800 microseconds.
		 */
		COMMAND(port, EnableDcConverter, 0);
		delay(1);
		break;
	}

	/*
	 * Wop is the normal operating register set.
	 * The 3C59[0257] adapters allow access to more than one register window
	 * at a time, but there are situations where switching still needs to be
	 * done, so just do it.
	 * Clear out any lingering Tx status.
	 */
	COMMAND(port, SelectRegisterWindow, Wop);
	while(inb(port+TxStatus))
		outb(port+TxStatus, 0);

	/*
	 * Allocate a controller structure, clear out the
	 * adapter statistics, clear the statistics logged into ctlr
	 * and enable statistics collection. Xcvr is needed in order
	 * to collect the BadSSD statistics.
	 */
	ether->ctlr = malloc(sizeof(Ctlr));
	ctlr = ether->ctlr;

	ilock(&ctlr->wlock);
	ctlr->xcvr = xcvr;
	statistics(ether);
	memset(ctlr, 0, sizeof(Ctlr));

	ctlr->busmaster = busmaster;
	ctlr->xcvr = xcvr;
	ctlr->rxstatus9 = rxstatus9;

	COMMAND(port, StatisticsEnable, 0);

	/*
	 * Allocate the receive buffers.
	 */
	ctlr->rbpix = 0;
	ctlr->rbp[0] = allocrbp();
	if(ctlr->busmaster)
		ctlr->rbp[1] = allocrbp();

	/*
	 * Set a base TxStartThresh which will be incremented
	 * if any txUnderrun errors occur and ensure no RxEarly
	 * interrupts happen.
	 */
	ctlr->txthreshold = ETHERMINTU;
	COMMAND(port, SetTxStartThresh, ETHERMINTU);
	COMMAND(port, SetRxEarlyThresh, ETHERMAXTU);

	iunlock(&ctlr->wlock);

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

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

	return 0;
}

void
etherelnk3link(void)
{
	addethercard("elnk3",  etherelnk3reset);
	addethercard("3C509",  etherelnk3reset);
}
.
## diffname pc/etherelnk3.c 1996/0608
## diff -e /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c
591d
567d
## diffname pc/etherelnk3.c 1996/0612
## diff -e /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c
411a
x &= ~rxEarly;
.
## diffname pc/etherelnk3.c 1996/0613
## diff -e /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c
1210c
	COMMAND(port, SetRxEarlyThresh, rxearly);
.
1087a
			rxearly = 8188;
.
1081a
		rxearly = 8188;
.
1064a
	rxearly = 2044;
.
1051c
	int busmaster, i, port, rxearly, rxstatus9, x, xcvr;
.
1042c
	if(cistrcmp(ether->type, "3C589") == 0 || cistrcmp(ether->type, "3C562") == 0)
.
747a
		 * Currently, this shouldn't happen.
		 */
		if(status & rxEarly){
			COMMAND(port, AcknowledgeInterrupt, rxEarly);
			status &= ~rxEarly;
		}

		/*
.
412d
4a
 *	RxEarly and busmaster;
.
## diffname pc/etherelnk3.c 1996/0925
## diff -e /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c
1220,1221c
	COMMAND(port, SetTxStartThresh, ETHERMINTU>>ctlr->ts);
	COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts);
.
1202a
	if(rxearly >= 2048)
		ctlr->ts = 2;
.
727c
				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
.
504c
			COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
.
330a
	int	ts;				/* threshold shift */
.
## diffname pc/etherelnk3.c 1996/0928
## diff -e /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c
1222,1223c
	ctlr->txthreshold = ETHERMINTU*2;
	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
.
725c
				COMMAND(port, TxReset, dmaReset);
.
## diffname pc/etherelnk3.c 1996/0929
## diff -e /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c
1143a
	if(xcvr & autoSelect)
		xcvr = autoselect(port, rxstatus9);
.
1130,1131c
			ether->ea[2*i] = x>>8;
			ether->ea[2*i+1] = x;
.
1108c
		x = ins(port+AddressConfig);
		xcvr = ((x & xcvrMask9)>>14)<<20;
		if(x & autoSelect9)
			xcvr |= autoSelect;
.
1103c
			x = ins(port+AddressConfig);
			xcvr = ((x & xcvrMask9)>>14)<<20;
			if(x & autoSelect9)
				xcvr |= autoSelect;
.
1100c
			xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
.
1093c
		xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
.
1056a
static int
autoselect(int port, int rxstatus9)
{
	int media, x;

	/*
	 * Pathetic attempt at automatic media selection.
	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
	 * cards operational.
	 */
	media = auiAvailable|coaxAvailable|base10TAvailable;
	if(rxstatus9 == 0){
		COMMAND(port, SelectRegisterWindow, Wfifo);
		media = ins(port+ResetOptions);
	}

	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	x = ins(port+MediaStatus) & ~(dcConverterEnabled|linkBeatEnable|jabberGuardEnable);
	outs(port+MediaStatus, x);

	if(media & baseTXAvailable){
		/*
		 * Must have InternalConfig register.
		 */
		COMMAND(port, SelectRegisterWindow, Wfifo);
		x = inl(port+InternalConfig) & ~xcvrMask;
		x |= xcvr100BaseTX;
		outl(port+InternalConfig, x);
		COMMAND(port, TxReset, 0);
		while(STATUS(port) & commandInProgress)
			;
		COMMAND(port, RxReset, 0);
		while(STATUS(port) & commandInProgress)
			;

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus);
		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
		delay(1);

		if(ins(port+MediaStatus) & linkBeatDetect)
			return xcvr100BaseTX;
		outs(port+MediaStatus, x);
	}

	if(media & base10TAvailable){
		if(rxstatus9 == 0){
			COMMAND(port, SelectRegisterWindow, Wfifo);
			x = inl(port+InternalConfig) & ~xcvrMask;
			x |= xcvr10BaseT;
			outl(port+InternalConfig, x);
		}
		else{
			COMMAND(port, SelectRegisterWindow, Wsetup);
			x = ins(port+AddressConfig) & ~xcvrMask9;
			x |= (xcvr10BaseT>>20)<<14;
			outs(port+AddressConfig, x);
		}
		COMMAND(port, TxReset, 0);
		while(STATUS(port) & commandInProgress)
			;
		COMMAND(port, RxReset, 0);
		while(STATUS(port) & commandInProgress)
			;

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus);
		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
		delay(1);

		if(ins(port+MediaStatus) & linkBeatDetect)
			return xcvr10BaseT;
		outs(port+MediaStatus, x);
	}

	/*
	 * Botch.
	 */
	return autoSelect;
}

.
981c
	 * If an adapter is found, select window 0, enable it and clear
.
877c
	 *    A delay seems necessary between reading the bits.
.
849,850c
	 * If a global reset is done here on all adapters it will confuse
	 * any ISA cards configured for EISA mode.
.
725c
				if(ctlr->busmaster == 0)
					COMMAND(port, TxReset, 0);
				else
					COMMAND(port, TxReset, dmaReset);
.
705c
			 * the transmitter, taking care not to reset the dma logic
			 * as a busmaster receive may be in progress.
.
676c
				if(ctlr->busmaster == 0)
					COMMAND(port, TxReset, 0);
				else
					COMMAND(port, TxReset, dmaReset);
.
138a
	autoSelect9		= 0x0080,
.
## diffname pc/etherelnk3.c 1996/1025
## diff -e /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c
1059c
	if(!cistrcmp(ether->type, "3C589") || !cistrcmp(ether->type, "3C562"))
.
## diffname pc/etherelnk3.c 1997/0327
## diff -e /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c
1330c
	ether->transmit = transmit;
.
1319c
	ctlr->txthreshold = ETHERMAXTU/2;
.
1309,1312c
	ctlr->rbp = allocrbp(allocb);
.
1307c
	 * Allocate the receive buffer.
.
1300a
	ctlr->rxearly = rxearly;
.
1245a
		x = inl(port+InternalConfig) & ~ramPartitionMask;
		outl(port+InternalConfig, x|ramPartition1to1);
	}
.
1244c
	if(x & dataRate100){
.
1179a
		COMMAND(port, GlobalReset, 0);
		while(STATUS(port) & commandInProgress)
			;
.
1173a
		bpp = &bp->next;
.
1170,1171c
			ether->tbdf = ap->tbdf;
			*bpp = bp->next;
			freeb(bp);
.
1166c
	bpp = &adapter;
	for(bp = *bpp; bp; bp = bp->next){
		ap = (Adapter*)bp->rp;
.
1150c
	Block *bp, **bpp;
	Adapter *ap;
.
1046,1050c
		tcmadapter(port, irq, p->tbdf);
.
1042a
			ether->tbdf = p->tbdf;
.
1031,1040c
	while(p = pcimatch(p, 0x10B7, 0)){
		port = p->bar[0] & ~0x01;
		irq = p->intl;
.
1029d
1026,1027c
	static Pcidev *p;
.
1013,1017c
		tcmadapter(port, irq, BUSUNKNOWN);
.
977d
961,965c
		tcmadapter(port, irq, BUSUNKNOWN);
.
917d
816a
static void
tcmadapter(int port, int irq, int tbdf)
{
	Block *bp;
	Adapter *ap;

	bp = allocb(sizeof(Adapter));
	ap = (Adapter*)bp->rp;
	ap->port = port;
	ap->irq = irq;
	ap->tbdf = tbdf;

	bp->next = adapter;
	adapter = bp;
}

.
809,815c
typedef struct Adapter {
	int	port;
	int	irq;
	int	tbdf;
} Adapter;
static Block* adapter;
.
769a
	COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
768c
			panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
.
747c
			txstart(ether);
.
729c
			if(s & (txJabber|txUnderrun)){
.
720c
			if(s & txUnderrun){
.
717c
				s |= x;
.
713c
			s = 0;
.
692a
				COMMAND(port, SetRxFilter, s);
				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
.
689a
				COMMAND(port, SelectRegisterWindow, Wstate);
				s = (port+RxFilter) & 0x000F;
				COMMAND(port, SelectRegisterWindow, Wop);
.
687a
				 * Reset the receiver and restore the filter and RxEarly
				 * threshold before re-enabling.
.
682d
673c
			print("#l%d: status 0x%uX, diag 0x%uX\n",
.
651,663c
	for(status = STATUS(port); status & interruptMask; status = STATUS(port)){
.
638c
	int port, status, s, w, x;
.
629,630c
		etheriq(ether, ctlr->rbp, 1);
		ctlr->rbp = bp;
.
624,627c
		if(ctlr->busmaster)
			ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
.
620,622c
		COMMAND(port, RxDiscard, 0);
		while(STATUS(port) & commandInProgress)
			;
.
614,618c
		/*
		 * A valid receive packet awaits:
		 *	if using PIO, read it into the buffer;
		 *	discard the packet from the FIFO;
		 *	if using busmastering, start a new transfer for
		 *	  the next packet and as a side-effect get the
		 *	  end-pointer of the one just received;
		 *	pass the packet on to whoever wants it.
		 */
		if(ctlr->busmaster == 0){
			len = (rxstatus & rxBytes9);
			ctlr->rbp->wp = ctlr->rbp->rp + len;
			insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
		}
.
610,612d
608c
				startdma(ether, PADDR(ctlr->rbp->rp));

			continue;
.
602a
		/*
		 * If there was an error or a new receive buffer can't be
		 * allocated, discard the packet and go on to the next.
		 */
		if((rxstatus & rxError) || (bp = allocrbp(iallocb)) == 0){
.
601a
		}
.
545,546d
536,542c
	txstart(ether);
.
522,532d
516d
512,513c
static void
transmit(Ether* ether)
.
504,507c
		else{
			ctlr->txbp = bp;
			if(ctlr->txbusy == 0){
				ctlr->txbusy = 1;
				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
			}
			break;
.
499d
493c
	for(;;){
		if(ctlr->txbp){
			bp = ctlr->txbp;
			ctlr->txbp = 0;
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
		}

.
475c
txstart(Ether* ether)
.
427c
		startdma(ether, PADDR(ctlr->rbp->rp));
.
425d
412c
	x = interruptMask;
.
367c
		print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
.
345,348c
	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
		addr = (ulong)bp->base;
		addr = ROUNDUP(addr, 32);
		bp->rp = (uchar*)addr;
	}
.
336c
allocrbp(Block* (*f)(int))
.
331a
	int	rxearly;			/* RxEarlyThreshold */
.
309,310c
	Block*	txbp;				/* */
.
306,307c
	Block*	rbp;				/* receive buffer */
.
259c
	TxAvailableThresh	= 0x0002,
.
202a
	ramPartition5to3	= 0x00000000,
	ramPartition3to1	= 0x00010000,
	ramPartition1to1	= 0x00020000,
	ramPartitionMask	= 0x00030000,
.
125,126c
#define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port)		ins((port)+IntStatusR)
.
50,51c
	CommandR		= 0x000E,
	IntStatusR		= 0x000E,
.
8c
 *	errata list;
 *	3C90x.
.
4c
 *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
.
## diffname pc/etherelnk3.c 1997/0328
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c
1259,1260d
1256a
	case xcvrMii:
		break;

.
1249c
		ether->mbps = 100;
.
1225,1228c
			x = eepromdata(port, i);
.
1221,1223d
1211,1213d
1208a
		break;
.
1207a
		rxearly = 2044;
.
1187,1203c
		rxearly = 8188;
		rxstatus9 = 0;
		break;

	buggery:
	default:
		busmaster = 0;
		COMMAND(port, SelectRegisterWindow, Wsetup);
.
1185d
1176,1183c
	if(port == 0)
		port = tcm5XXpcmcia(ether);
	if(port == 0)
		port = tcm59Xpci(ether);
	if(port == 0)
		port = tcm5XXeisa(ether);
	if(port == 0)
		port = tcm509isa(ether);
	if(port == 0)
		return -1;
	/*
	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
	 * and do something depending on capabilities.
	 */
	switch(did = eepromdata(port, 0x03)){

	case 0x9000:
	case 0x9001:
	case 0x9050:
	case 0x9051:
		if(BUSTYPE(ether->tbdf) != BusPCI)
			goto buggery;
		busmaster = 2;
		goto vortex;

	case 0x5900:
	case 0x5920:
	case 0x5950:
	case 0x5951:
	case 0x5952:
	case 0x5970:
	case 0x5971:
	case 0x5972:
		busmaster = 1;
	vortex:
.
1160,1162d
1145c
	int busmaster, did, i, port, rxearly, rxstatus9, x, xcvr;
.
1141a
static int
eepromdata(int port, int offset)
{
	COMMAND(port, SelectRegisterWindow, Wsetup);
	while(EEPROMBUSY(port))
		;
	EEPROMCMD(port, EepromReadRegister, offset);
	while(EEPROMBUSY(port))
		;
	return EEPROMDATA(port);
}

.
1076a
	if(media & miiConnector)
		return xcvrMii;

.
1039a
		COMMAND(port, GlobalReset, 0);
		while(STATUS(port) & commandInProgress)
			;
.
796,807c
	len += sprint(buf+len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]);
	len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]);
	len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]);
	len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]);
	len += sprint(buf+len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]);
	len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]);
	len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]);
	len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]);
	len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]);
	len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]);
	len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]);
	sprint(buf+len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]);
.
772d
770a

		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
735c
					COMMAND(port, TxReset, (updnReset|dmaReset));
.
659c
	while((status = STATUS(port)) & (interruptMask|interruptLatch)){
.
472c
		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
.
463,467c
	ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
	ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
	ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
	ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
.
454,461c
	for(i = 0; i < UpperFramesOk; i++)
		ctlr->stats[i] += inb(port+i) & 0xFF;
.
440c
	int port, i, u, w;
.
388c

.
319,331c
	long	stats[BytesRcvdOk+2];
.
305a
enum {						/* 3C90x extended register set */
	PktStatus		= 0x0020,	/* 32-bits */
	DnListPtr		= 0x0024,	/* 32-bits, 8-byte aligned */
	FragAddr		= 0x0028,	/* 32-bits */
	FragLen			= 0x002C,	/* 16-bits */
	ListOffset		= 0x002E,	/* 8-bits */
	TxFreeThresh		= 0x002F,	/* 8-bits */
	UpPktStatus		= 0x0030,	/* 32-bits */
	FreeTimer		= 0x0034,	/* 16-bits */
	UpListPtr		= 0x0038,	/* 32-bits, 8-byte aligned */

						/* PktStatus bits */
	fragLast		= 0x00000001,
	dnCmplReq		= 0x00000002,
	dnStalled		= 0x00000004,
	upCompleteX		= 0x00000008,
	dnCompleteX		= 0x00000010,
	upRxEarlyEnable		= 0x00000020,
	armCountdown		= 0x00000040,
	dnInProg		= 0x00000080,
	counterSpeed		= 0x00000010,	/* 0 3.2uS, 1 320nS */
	countdownMode		= 0x00000020,
						/* UpPktStatus bits */
	upPktLenMask		= 0x00001FFF,
	upStalled		= 0x00002000,
	upError			= 0x00004000,
	upPktComplete		= 0x00008000,
	upOverrrun		= 0x00010000,	/* RxError<<16 */
	upRuntFrame		= 0x00020000,
	upAlignmentError	= 0x00040000,
	upCRCError		= 0x00080000,
	upOversizedFrame	= 0x00100000,
	upDribbleBits		= 0x00800000,
	upOverflow		= 0x01000000,
};

/*
 * Up/Dn Packet Descriptor.
 * The hardware info (np, control, addr, len) must be 8-byte aligned.
 */
typedef struct Pd Pd;
typedef struct Pd {
	Pd*	next;
	Block*	bp;

	ulong	np;				/* next pointer */
	ulong	control;			/* FSH or UpPktStatus */
	ulong	addr;
	ulong	len;
};

.
304c
};
.
257c
	auiDisable		= 0x8000,	/* 10BaseT transceiver selected */
.
239a
	UpperBytesOk		= 0x000D,
.
228c
	miiConnector		= 0x0040,
.
206a
	ramPartition3to5	= 0x00030000,
.
203a
	disableBadSsdDetect	= 0x00000100,
	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
.
123c
	interruptMask		= 0x07FE,
.
119a
	dnComplete		= 0x0200,
	upComplete		= 0x0400,
.
97a
enum {						/* Stall command bits */
	UpStall			= 0x0000,
	UpUnStall		= 0x0001,
	DnStall			= 0x0002,
	DnUnStall		= 0x0003,
};

.
95c
	resetMask		= 0x01FF,
.
93a
	updnReset		= 0x0100,	/* upload/download (Rx/TX) logic */
.
62c
	Stall			= 0x0006,	/* 3C90x */
	TxDone			= 0x0007,
.
57c
	SelectRegisterWindow	= 0x0001,
.
29a
 *	9000 PCI	3C900-TPO	Etherlink III XL PCI 10BASE-T
 *	9001 PCI	3C900-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
 *	9050 PCI	3C905-TX	Fast Etherlink XL Shared 10BASE-T/100BASE-TX
 *	9051 PCI	3C905-T4	Fast Etherlink Shared 10BASE-T/100BASE-T4
 *
.
9c
 *	3C90x full busmastering;
 *	rewrite all initialisation.
.
## diffname pc/etherelnk3.c 1997/0403
## diff -e /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c
451a
	COMMAND(port, SetRxFilter, filter);
}

static void
multicast(void* arg, char *addr, int on)
{
	int filter, port;
	Ether *ether;

	ether = (Ether*)arg;
	port = ether->port;

	filter = receiveBroadcast|receiveIndividual;
	if(ether->nmaddr)
		filter |= receiveMulticast;
.
449a
	if(ether->nmaddr)
		filter |= receiveMulticast;
.
447c
	ether = (Ether*)arg;
	port = ether->port;
.
445a
	Ether *ether;
.
## diffname pc/etherelnk3.c 1997/0404
## diff -e /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c
1438a
	ether->multicast = multicast;
.
1316a
	USED(did);
.
467a
print("mutlicast nmaddr == %d\n", ether->nmaddr);
.
464a
	USED(addr, on);

.
460c
multicast(void* arg, uchar *addr, int on)
.
## diffname pc/etherelnk3.c 1997/0415
## diff -e /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c
473a
	if(ether->prom)
		filter |= receiveAllFrames;
.
470c
print("multicast nmaddr == %d\n", ether->nmaddr);
.
## diffname pc/etherelnk3.c 1997/0417
## diff -e /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c
887c
	n = readstr(offset, a, n, p);
	free(p);

	return n;
.
872,885c
	p = malloc(READSTR);
	len = snprint(p, READSTR, "interrupts: %ld\n", ctlr->interrupts);
	len += snprint(p+len, READSTR-len, "timer: %ld\n", ctlr->timer);
	len += snprint(p+len, READSTR-len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]);
	len += snprint(p+len, READSTR-len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]);
	len += snprint(p+len, READSTR-len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]);
	len += snprint(p+len, READSTR-len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]);
	len += snprint(p+len, READSTR-len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]);
	len += snprint(p+len, READSTR-len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]);
	len += snprint(p+len, READSTR-len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]);
	len += snprint(p+len, READSTR-len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]);
	len += snprint(p+len, READSTR-len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]);
	len += snprint(p+len, READSTR-len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]);
	len += snprint(p+len, READSTR-len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]);
	snprint(p+len, READSTR-len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]);
.
861a
	Ctlr *ctlr;
.
859,860c
	char *p;
.
## diffname pc/etherelnk3.c 1997/0418
## diff -e /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c
470d
## diffname pc/etherelnk3.c 1997/0614
## diff -e /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c
1411c
	memset(ctlr->stats, 0, sizeof(ctlr->stats));
.
## diffname pc/etherelnk3.c 1997/0712
## diff -e /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c
1425c
	switch(ctlr->busmaster){

	case 2:
		ctlr->dnenabled = 1;

		/*
		 * Too severe, can use receive busmastering at 100Mbps OK,
		 * but how to tell which rate is actually being used - the
		 * 3c905 always seems to have dataRate100 set?
		 */
		x = eepromdata(port, 0x0F);
		print("software info 2: %uX\n", x);
		if(x & 0x01)
			ctlr->upenabled = 1;

		if(ctlr->upenabled)
			init905(ctlr);
		else
			ctlr->rbp = rbpalloc(allocb);
		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
		break;

	default:
		ctlr->rbp = rbpalloc(allocb);
		break;
	}
.
1423c
	 * Allocate any receive buffers.
.
1378a
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
		outs(port+MediaStatus, x);

.
1375a

		if((did & 0xFF00) == 0x5900)
			busmaster = 0;
.
1373a
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~dcConverterEnabled;
.
1368a
		COMMAND(port, SelectRegisterWindow, Wfifo);
		x = inl(port+InternalConfig) & ~ramPartitionMask;
		outl(port+InternalConfig, x|ramPartition1to1);

		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
		x |= linkBeatEnable;
		outs(port+MediaStatus, x);

		if(x & dataRate100)
			ether->mbps = 100;
		break;

.
1364a
		/*
		 * Bug? the 3c905 always seems to have dataRate100 set.
		 */
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		if(ins(port+MediaStatus) & dataRate100)
			ether->mbps = 100;
.
1351,1361c
		xcvr = autoselect(port, xcvr, rxstatus9);
.
1349a
//print("reset: xcvr %uX\n", xcvr);
.
1325d
1281a

.
1272,1280c
	if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
.
1257a
	if(scandone == 0){
		tcm59Xpci();
		tcm5XXeisa();
		tcm509isa();
		scandone = 1;
	}

	/*
	 * Any adapter matches if no ether->port is supplied,
	 * otherwise the ports must match.
	 */
.
1251,1256c
	 * Scan for adapter on PCI, EISA and finally
	 * using the little ISA configuration dance.
.
1248a
	static int scandone;
.
1216c
		delay(100);
.
1214c
		x = ins(port+MediaStatus) & ~dcConverterEnabled;
.
1194,1211c
		setxcvr(port, xcvr10BaseT, is9);
.
1187a
{ int i, v;
  for(i = 0; i < 2000; i++){
	v = ins(port+MediaStatus);
	if(v & linkBeatDetect){
		print("count %d v %uX\n", i, v);
		return xcvr100BaseTX;
	}
	delay(1);
  }
//print("count %d v %uX\n", i, ins(port+MediaStatus));
}

.
1184,1186c
		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
		outs(port+MediaStatus, linkBeatEnable|x);
		delay(10);
.
1172,1181c
		setxcvr(port, xcvr100BaseTX, is9);
.
1164,1166c
//COMMAND(port, SelectRegisterWindow, Wdiagnostic);
//print("autoselect: media status %uX\n", ins(port+MediaStatus));
.
1159a
//print("autoselect: media %uX\n", media);
.
1155,1156c
	if(is9){
		COMMAND(port, SelectRegisterWindow, Wsetup);
		x = ins(port+ConfigControl);
		media = 0;
		if(x & base10TAvailable9)
			media |= base10TAvailable;
		if(x & coaxAvailable9)
			media |= coaxAvailable;
		if(x & auiAvailable9)
			media |= auiAvailable;
	}
	else{
.
1153a
	 * It's a bonus if it works for anything else.
.
1146c
autoselect(int port, int xcvr, int is9)
.
1144a
static void
setxcvr(int port, int xcvr, int is9)
{
	int x;

	if(is9){
		COMMAND(port, SelectRegisterWindow, Wsetup);
		x = ins(port+AddressConfig) & ~xcvrMask9;
		x |= (xcvr>>20)<<14;
		outs(port+AddressConfig, x);
	}
	else{
		COMMAND(port, SelectRegisterWindow, Wfifo);
		x = inl(port+InternalConfig) & ~xcvrMask;
		x |= xcvr;
		outl(port+InternalConfig, x);
	}

	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
}

#ifdef notdef
static struct xxx {
	int	available;
	int	next;
} xxx[8] = {
	{ base10TAvailable,	1, },		/* xcvr10BaseT	-> xcvrAui */
	{ auiAvailable,		3, },		/* xcvrAui	-> xcvr10Base2 */
	{ 0, -1, },
	{ coaxAvailable,	-1, },		/* xcvr10Base2	-> nowhere */
	{ baseTXAvailable,	5, },		/* xcvr100BaseTX-> xcvr100BaseFX */
	{ baseFXAvailable,	-1, },		/* xcvr100BaseFX-> nowhere */
	{ miiConnector,		-1, },		/* xcvrMii	-> nowhere */
	{ 0, -1, },
};
#endif /* notdef */

.
1132,1133d
1124,1128d
1117a
	p = nil;
.
1115c
	Pcidev *p;
.
1112,1113c
static void
tcm59Xpci(void)
.
1108,1109d
1101,1105d
1085,1086c
	for(slot = 1; slot < MaxEISA; slot++){
		port = slot*0x1000;
.
1076,1077c
	if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
		return;
.
1073c
	 * Check if this is an EISA machine.
.
1070c
	int irq, port, slot;
.
1068d
1065,1066c
static void
tcm5XXeisa(void)
.
1061,1062d
1054,1058d
1018,1020c
	 * Attempt to activate all adapters. If adapter is set for
	 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
	 * it fully.
.
1012,1013c
static void
tcm509isa(void)
.
872,885c
	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
	len += snprint(p+len, READSTR-len, "timer: %lud\n", ctlr->timer);
	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]);
	len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]);
	len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]);
	len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]);
	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]);
	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]);
	len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]);
	len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]);
	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]);
	len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]);
	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]);
	snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
.
828a
		if(status & dnComplete){
			COMMAND(port, AcknowledgeInterrupt, dnComplete);
			start905(ether, 0);
			status &= ~dnComplete;
		}

.
814a
				if(ctlr->busmaster == 2)
					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
.
798a
				if(ctlr->dnenabled){
					while(inl(port+PktStatus) & dnInProg)
						;
				}
.
781a
		if(status & (upComplete)){
			COMMAND(port, AcknowledgeInterrupt, upComplete);
			receive905(ether);
			status &= ~upComplete;
		}

.
710c
		if(ctlr->busmaster == 1)
.
700c
		if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
.
685c
			if(ctlr->busmaster == 1)
.
680c
		if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
.
632c
		if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
.
621a
receive905(Ether* ether)
{
	Ctlr *ctlr;
	int port;
	Block *bp, *xbp;
	Upd *upd;

	ctlr = ether->ctlr;
	port = ether->port;

	//ilock(&ctlr->upqlock);
	COMMAND(port, Stall, upStall);
	while(STATUS(port) & commandInProgress)
		;

	bp = ctlr->upqhead;
	upd = (Upd*)bp->rp;
	while(upd->control & upPktComplete){
		if(upd->control & upError){
			if(upd->control & upOverrun)
				ether->overflows++;
			if(upd->control & (upOversizedFrame|upRuntFrame))
				ether->buffs++;
			if(upd->control & upAlignmentError)
				ether->frames++;
			if(upd->control & upCRCError)
				ether->crcs++;

			upd->control = 0;
		}
		else if(xbp = updalloc(upd->np)){
			bp->rp += sizeof(Upd)-sizeof(Etherpkt);
			bp->wp = bp->rp + (upd->control & rxBytes);

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

			etheriq(ether, bp, 1);
			bp = xbp;
		}

		upd = (Upd*)ctlr->upqtail->rp;
		upd->np = PADDR(bp->rp);
		ctlr->upqtail->next = bp;
		ctlr->upqtail = bp;
		ctlr->upqhead = bp->next;

		bp = ctlr->upqhead;
		upd = (Upd*)bp->rp;
	}

	COMMAND(port, Stall, upUnStall);
	//iunlock(&ctlr->upqlock);
}

static void
.
612a
	if(ctlr->dnenabled){
		transmit905(ether);
		return;
	}

.
604a
start905(Ether* ether, Dpd* add)
{
	Ctlr *ctlr;
	int dnlistptr, port;
	Dpd *dpd;

	ctlr = ether->ctlr;
	port = ether->port;

	ilock(&ctlr->dnqlock);
	COMMAND(port, Stall, dnStall);
	while(STATUS(port) & commandInProgress)
		;

	/*
	 * Free any completed packets.
	 */
	dnlistptr = inl(port+DnListPtr);
	while(dpd = ctlr->dnqhead){
		if(PADDR(dpd) == dnlistptr)
			break;
		ctlr->dnqhead = dpd->next;
		if(dpd->bp)
			freeb(dpd->bp);
		dpdfree(ctlr, dpd);
	}

	/*
	 * Add any new packets to the queue.
	 */
	if(add){
		if(ctlr->dnqhead){
			dpd = ctlr->dnqtail;
			dpd->next = add;
			dpd->np = PADDR(&add->np);
			dpd->control &= ~dnIndicate;
		}
		else
			ctlr->dnqhead = add;
		ctlr->dnqtail = add;
	}

	/*
	 * If the adapter is not currently processing anything
	 * and there is something on the queue, start it processing.
	 */
	if(dnlistptr == 0 && ctlr->dnqhead)
		outl(port+DnListPtr, PADDR(ctlr->dnqhead));

	COMMAND(port, Stall, dnUnStall);
	iunlock(&ctlr->dnqlock);
}

static void
transmit905(Ether* ether)
{
	Ctlr *ctlr;
	Block *bp;
	Dpd* dpd;

	bp = qget(ether->oq);
	if(bp == nil)
		return;

	ctlr = ether->ctlr;

	dpd = dpdalloc(ctlr);
	dpd->next = 0;
	dpd->bp = bp;

	dpd->np = 0;
	dpd->control = dnIndicate|BLEN(bp);
	dpd->addr = PADDR(bp->rp);
	dpd->len = updnLastFrag|BLEN(bp);

	start905(ether, dpd);
}

static void
.
548a
		break;
.
545c
	switch(ctlr->xcvr){

	case xcvrMii:
	case xcvr100BaseTX:
	case xcvr100BaseFX:
.
514a
	else{
		if(ctlr->upenabled)
			outl(port+UpListPtr, PADDR(ctlr->upqhead->rp));
	}
.
513c
	if(ctlr->busmaster == 1)
.
502a
	else{
		if(ctlr->dnenabled)
			x &= ~transferInt;
		if(ctlr->upenabled)
			x &= ~(rxEarly|rxComplete);
	}
.
501c
	if(ctlr->busmaster == 1)
.
402a
	Upd *upd;

	/*
	 * The hardware info (np, control, addr, len)
	 * must be 8-byte aligned.
	 */
	if(bp = iallocb(sizeof(Upd)+8)){
		bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 8);
		bp->wp = bp->rp;
		upd = (Upd*)bp->rp;
		upd->np = np;
		upd->control = 0;
		upd->addr = PADDR(upd->data);
		upd->len = updnLastFrag|sizeof(Etherpkt);
	}

	return bp;
}

static void
init905(Ctlr* ctlr)
{
	int i;
	ulong np;
	Block *bp;
	Upd *upd;

	np = 0;
	for(i = 0; i < 16; i++){
		bp = updalloc(np);
		if(ctlr->upqhead == 0)
			ctlr->upqtail = bp;
		bp->next = ctlr->upqhead;
		ctlr->upqhead = bp;
		np = PADDR(bp->rp);
	}
	ctlr->upqtail->next = ctlr->upqhead;
	upd = (Upd*)ctlr->upqtail->rp;
	upd->np = PADDR(ctlr->upqhead->rp);
}

static Dpd*
dpdalloc(Ctlr* ctlr)
{
	Dpd *dpd;
	void *base;

	ilock(&ctlr->dpdlock);
	if(dpd = ctlr->dpdpool){
		ctlr->dpdpool = dpd->next;
		iunlock(&ctlr->dpdlock);
		dpd->next = 0;
		dpd->bp = 0;
	}
	else{
		iunlock(&ctlr->dpdlock);
		base = smalloc(sizeof(Dpd)+8);
		dpd = (Dpd*)ROUNDUP((ulong)base, 8);
		dpd->base = base;
	}

	return dpd;
}

static void
dpdfree(Ctlr* ctlr, Dpd* dpd)
{
	ilock(&ctlr->dpdlock);
	dpd->next = ctlr->dpdpool;
	ctlr->dpdpool = dpd;
	iunlock(&ctlr->dpdlock);
}

static Block*
rbpalloc(Block* (*f)(int))
{
	Block *bp;
.
400c
updalloc(ulong np)
.
396a
	int	upenabled;
	int	dnenabled;
.
391c
	long	stats[BytesRcvdOk+3];
.
388a
	//Lock	upqlock;			/* full-busmaster -based reception */
	Block*	upqhead;
	Block*	upqtail;
	int	upalloc;
	int	nupd;

	Lock	dpdlock;			/* pool of free Dpd's */
	Dpd*	dpdpool;

	Lock	dnqlock;			/* full-busmaster -based transmission */
	Dpd*	dnqhead;
	Dpd*	dnqtail;

.
385c
	Block*	txbp;				/* FIFO -based transmission */
.
377a
	uchar	data[sizeof(Etherpkt)];
} Upd;

.
376d
371a
typedef struct Upd {
.
370a
	void*	base;				/* base of this allocation */
} Dpd;
.
367,369c
typedef struct Dpd Dpd;
typedef struct Dpd {
	ulong	np;				/* next pointer */
	ulong	control;			/* FSH or UpPktStatus */
	ulong	addr;
	ulong	len;

	Dpd*	next;
.
360a

	dnIndicate		= 0x80000000,	/* FrameStartHeader (dpd->control) */

	updnLastFrag		= 0x80000000,	/* (dpd->len) */
.
354c
	upOverrun		= 0x00010000,	/* RxError<<16 */
.
349c
						/* UpPktStatus bits (dpd->control) */
.
160a
	base10TAvailable9	= 0x0200,
	coaxAvailable9		= 0x1000,
	auiAvailable9		= 0x2000,
.
107,110c
	upStall			= 0x0000,
	upUnStall		= 0x0001,
	dnStall			= 0x0002,
	dnUnStall		= 0x0003,
.
## diffname pc/etherelnk3.c 1997/0715
## diff -e /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c
1755,1757c
		//print("software info 2: %uX\n", x);
		if(!(x & 0x01))
			outl(port+PktStatus, upRxEarlyEnable);
.
1753a
		ctlr->upenabled = 1;
.
1750,1752c
		 * 10MUpldBug.
		 * Disabling is too severe, can use receive busmastering at
		 * 100Mbps OK, but how to tell which rate is actually being used -
		 * the 3c905 always seems to have dataRate100 set?
		 * Believe the bug doesn't apply if upRxEarlyEnable is set
		 * and the threshold is set such that uploads won't start
		 * until the whole packet has been received.
.
463c
	for(i = 0; i < 64; i++){
.
## diffname pc/etherelnk3.c 1997/0806
## diff -e /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c
9c
 *	limit 3C90x transmit queue;
.
## diffname pc/etherelnk3.c 1997/0919
## diff -e /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c
1765a
		}
.
1764c
		if(ctlr->upenabled || ctlr->dnenabled){
			ctlr->nup = Nup;
			ctlr->ndn = Ndn;
.
1166a
len += snprint(p+len, READSTR-len, "up: q %lud s %lud\n",
    ctlr->upqmax, ctlr->upstalls);
ctlr->upqmax = 0;
len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n",
    ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax);
ctlr->dnqmax = 0;
.
1108a
			ctlr->dninterrupts++;
.
1107d
1105a
			txstart905(ether);
.
1048a
			COMMAND(port, AcknowledgeInterrupt, upComplete);
.
1047d
881,882c
	if(q > ctlr->upqmax)
		ctlr->upqmax = q;
.
879a
	ctlr->uphead = pd;
.
877,878c
		q++;
.
871,875c
		pd->control = 0;
		COMMAND(port, Stall, upUnStall);
.
860,868c
		else if(bp = iallocb(sizeof(Etherpkt)+4)){
			len = pd->control & rxBytes;
			pd->bp->wp = pd->bp->rp+len;
			etheriq(ether, pd->bp, 1);
			pd->bp = bp;
			pd->addr = PADDR(bp->rp);
.
857,858d
855c
			if(pd->control & upCRCError)
.
853c
			if(pd->control & upAlignmentError)
.
851c
			if(pd->control & (upOversizedFrame|upRuntFrame))
.
840,849c
	if(inl(port+UpPktStatus) & upStalled)
		ctlr->upstalls++;
	q = 0;
	for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
		if(pd->control & upError){
			if(pd->control & upOverrun)
.
833,835c
	int len, port, q;
	Pd *pd;
	Block *bp;
.
817c
		txstart905(ether);
.
783,807d
779c
	iunlock(&ctlr->dnlock);
.
775,776c
	if(dnlistptr == 0 && ctlr->dnq)
		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
.
770a
	if(ctlr->dnq > ctlr->dnqmax)
		ctlr->dnqmax = ctlr->dnq;

.
756,768c
	while(ctlr->dnq < (ctlr->ndn-1)){
		bp = qget(ether->oq);
		if(bp == nil)
			break;

		pd = ctlr->dnhead->next;
		pd->np = 0;
		pd->control = dnIndicate|BLEN(bp);
		pd->addr = PADDR(bp->rp);
		pd->len = updnLastFrag|BLEN(bp);
		pd->bp = bp;

		if(ctlr->dnq == 0)
			ctlr->dntail = pd;
		ctlr->dnhead->np = PADDR(&pd->np);
		ctlr->dnhead->control &= ~dnIndicate;
		ctlr->dnhead = pd;
		ctlr->dnq++;

		ctlr->dnqueued++;
.
754a
	ctlr->dntail = pd;
.
750,753c
		if(pd->bp){
			freeb(pd->bp);
			pd->bp = nil;
		}
		ctlr->dnq--;
		pd = pd->next;
.
747,748c
	pd = ctlr->dntail;
	while(ctlr->dnq){
		if(PADDR(&pd->np) == dnlistptr)
.
738c
	ilock(&ctlr->dnlock);
.
733c
	Block *bp;
	Pd *pd;
.
729c
txstart905(Ether* ether)
.
632c
			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
.
499,507d
489,496c
	ctlr->dnhead = ctlr->dnr;
	ctlr->dntail = ctlr->dnr;
	ctlr->dnq = 0;
	iunlock(&ctlr->dnlock);
.
482,487c
	prev = ctlr->dnr;
	for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
		pd->next = prev;
		prev = pd;
.
476,480c
	ilock(&ctlr->dnlock);
	ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
	ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
.
471,474c
	ctlr->uphead = ctlr->upr;
.
454,469c
		pd->next = prev;
		prev = pd;
		pd->bp = bp;
.
451,452c
	prev = ctlr->upr;
	for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
		pd->np = PADDR(&prev->np);
		pd->control = 0;
		bp = allocb(sizeof(Etherpkt));
		pd->addr = PADDR(bp->rp);
		pd->len = updnLastFrag|sizeof(Etherpkt);
.
441,449c
	ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
	ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
.
438,439c
	 * Create rings for the receive and transmit sides.
	 * Take care with alignment:
	 *	make sure ring base is 8-byte aligned;
	 *	make sure each entry is 8-byte aligned.
.
435c
	Pd *pd, *prev;
.
431,432c
static void
init905(Ctlr* ctlr)
.
422a
	int	upqmax;
	int	upstalls;
	int	dnqmax;
	long	dninterrupts;
	long	dnqueued;

.
415,418d
412,413c
	Lock	dnlock;				/* full-busmaster -based transmission */
	int	ndn;
	void*	dnbase;
	Pd*	dnr;
	Pd*	dnhead;
	Pd*	dntail;
	int	dnq;
.
406,410c
	int	nup;				/* full-busmaster -based reception */
	void*	upbase;
	Pd*	upr;
	Pd*	uphead;
.
386,394d
383,384c
} Pd;
.
381c
	Pd*	next;
.
374,375c
typedef struct Pd Pd;
typedef struct Pd {
.
371,372c
 * Up/Dn Packet Descriptors.
 * The hardware info (np, control, addr, len) must be 8-byte aligned
 * and this structure size must be a multiple of 8.
.
367a

	Nup			= 32,
	Ndn			= 16,
.
9d
## diffname pc/etherelnk3.c 1997/1011
## diff -e /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c
1338c
		port = p->mem[0].bar & ~0x01;
.
## diffname pc/etherelnk3.c 1997/1025
## diff -e /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c
1301c
	if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
.
776,781d
774c
	else{
		w = (STATUS(port)>>13) & 0x07;
		COMMAND(port, SelectRegisterWindow, Wop);
		txstart(ether);
		COMMAND(port, SelectRegisterWindow, w);
.
772c
	ilock(&ctlr->wlock);
	if(ctlr->dnenabled)
.
760d
705d
471d
459d
404,405c
	int	ndn;				/* full-busmaster -based transmission */
.
## diffname pc/etherelnk3.c 1997/1203
## diff -e /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c
1731a

COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
1106,1111c

	if(ctlr->upenabled){
		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d s %lud\n",
			ctlr->upqueued, ctlr->upinterrupts, ctlr->dnqmax, ctlr->upstalls);
		ctlr->upqmax = 0;
	}
	if(ctlr->dnenabled){
		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n",
			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax);
		ctlr->dnqmax = 0;
	}

.
1045a
			txstart905(ether);
.
1044d
987a
			ctlr->upinterrupts++;
.
986a
			receive905(ether);
.
985d
818a
	ctlr->upqueued += q;
.
415a
	long	upinterrupts;
	long	upqueued;
.
## diffname pc/etherelnk3.c 1997/1207
## diff -e /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c
1113c
			ctlr->upqueued, ctlr->upinterrupts, ctlr->upqmax, ctlr->upstalls);
.
369c
	Ndn			= 32,
.
## diffname pc/etherelnk3.c 1997/1212
## diff -e /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c
1742,1743d
1033a

			print("#l%d: txstatus 0x%uX, threshold %d\n",
			    	ether->ctlrno, s, ctlr->txthreshold);
.
1032a
				if(ctlr->dnenabled)
					status |= dnComplete;
.
756,757c
	if(stalled)
		COMMAND(port, Stall, dnUnStall);
.
754c
	if(inl(port+DnListPtr) == 0 && ctlr->dnq)
.
741a
		if(ctlr->dnq == 0)
			ctlr->dntail = pd;
.
737,738c
		if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
			COMMAND(port, Stall, dnStall);
			for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
				;
			if(timeo == 0)
				print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
			stalled = 1;
		}

.
724a
	stalled = 0;
.
714c
		if(PADDR(&pd->np) == inl(port+DnListPtr))
.
711d
704,707d
697c
	int port, stalled, timeo;
.
369c
	Ndn			= 64,
.
## diffname pc/etherelnk3.c 1998/0319
## diff -e /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c
1414c
autoselect(int port, int , int is9)
.
## diffname pc/etherelnk3.c 1998/0320
## diff -e /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c
962c
					COMMAND(port, TxReset, (updnReset|dmaReset));
.
## diffname pc/etherelnk3.c 1998/0321
## diff -e /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c
1028c
			/*
			 * According to the manual, maxCollisions does not require
			 * a TxReset, merely a TxEnable. However, evidence points to
			 * it being necessary on the 3C905. The jury is still out.
			 */
			if(s & (txJabber|txUnderrun|maxCollisions)){
.
## diffname pc/etherelnk3.c 1998/0325
## diff -e /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c
1631a
		if(an & 0x0140)
			setfullduplex(port);
.
1629,1630c
		phyaddr = 24;
		an = miir(ether, phyaddr, 0x04);
		an &= miir(ether, phyaddr, 0x05) & 0x03E0;
		XCVRDEBUG("mii an: %uX\n", an);
		if(an & 0x380)
.
1627c
		 * Quick hack.
.
1622a
	XCVRDEBUG("autoselect returns: xcvr %uX\n", xcvr);
.
1620c
	XCVRDEBUG("reset: xcvr %uX\n", xcvr);
.
1514c
	int an, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
1487a
		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
.
1472c
  XCVRDEBUG("count %d v %uX\n", i, ins(port+MediaStatus));
.
1449,1450c
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
.
1444c
	XCVRDEBUG("autoselect: media %uX\n", media);
.
1422a
	USED(xcvr);

.
1419c
autoselect(int port, int xcvr, int is9)
.
1401a
static void
setfullduplex(int port)
{
	int x;

	COMMAND(port, SelectRegisterWindow, Wfifo);
	x = ins(port+MacControl);
	outs(port+MacControl, fullDuplexEnable|x);

	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
}

static int
miir(Ether* ether, int phyad, int regad)
{
	int data, i, port, w, x;

	port = ether->port+PhysicalMgmt;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(ether->port, SelectRegisterWindow, Wdiagnostic);

	/*
	 * Taken from the Cyclone manual appendix describing
	 * how to programme the MII Management Interface.
	 */
	/*
	 * Preamble
	 */
	for(i = 0; i < 32; i++){
		outs(port, mgmtDir|mgmtData);
		microdelay(1);
		outs(port, mgmtDir|mgmtData|mgmtClk);
		microdelay(1);
	}

	/*
	 * ST+OP+PHYAD+REGAD
	 */
	x = 0x1800|(phyad<<5)|regad;
	for(i = 14-1; i >= 0; i--){
		if(x & (1<<i))
			data = mgmtDir|mgmtData;
		else
			data = mgmtDir;
		outs(port, data);
		microdelay(1);
		outs(port, data|mgmtClk);
		microdelay(1);
	}

	/*
	 * "Z" cycle (turnaround) + one read (0 means there's a PHY responding).
	 */
	data = 0;
	outs(port, 0);
	microdelay(1);
	outs(port, mgmtClk);
	x = ins(port);
	if(x & mgmtData)
		data |= (1<<16);

	/*
	 * 16 data read cycles.
	 */
	for(i = 16-1; i >= 0; i--){
		outs(port, 0);
		microdelay(1);
		outs(port, mgmtClk);
		microdelay(1);
		x = ins(port);
		if(x & mgmtData)
			data |= (1<<i);
		microdelay(1);
	}

	/*
	 * "Z" cycle (turnaround).
	 */
	outs(port, 0);
	microdelay(1);
	outs(port, mgmtClk);

	COMMAND(ether->port, SelectRegisterWindow, w);

	if(data & 0x10000)
		return -1;
print("%d/%d: data=%uX\n", phyad, regad, data);

	return data & 0xFFFF;
}

static void
scanphy(Ether* ether)
{
	int i, x;

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

.
1132,1133c
		if(ctlr->dnqmax > ctlr->dnqmaxhw)
			ctlr->dnqmaxhw = ctlr->dnqmax;
		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
.
1127,1128c
		if(ctlr->upqmax > ctlr->upqmaxhw)
			ctlr->upqmaxhw = ctlr->upqmax;
		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
			ctlr->upqueued, ctlr->upinterrupts,
			ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
.
419a
	int	dnqmaxhw;
.
415a
	int	upqmaxhw;
.
266a
						/* PhysicalMgmt bits */
	mgmtClk			= 0x0001,
	mgmtData		= 0x0002,
	mgmtDir			= 0x0004,
	cat5LinkTestDefeat	= 0x8000,
.
242a
	extendAfterCollision	= 0x0080,	/* 3C90xB */
	flowControlEnable	= 0x0100,	/* 3C90xB */
	vltEnable		= 0x0200,	/* 3C90xB */
.
50a
#define XCVRDEBUG		if(1)print

.
9c
 *	rewrite all initialisation;
 *	handle the cyclone adapter.
.
2c
 * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
.
## diffname pc/etherelnk3.c 1998/0326
## diff -e /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c
52c
#define XCVRDEBUG		if(0)print
.
## diffname pc/etherelnk3.c 1998/0327
## diff -e /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c
1512c
	XCVRDEBUG("phy%d/%d: data=%uX\n", phyad, regad, data);
.
## diffname pc/etherelnk3.c 1998/0331
## diff -e /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c
1766c
		//if(an & 0x380)
.
1527c
		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1));
.
1512c
print("%d/%d: data=%uX\n", phyad, regad, data);
.
1105c
	iunlock(&ctlr->wlock);
.
952c
	ilock(&ctlr->wlock);
.
## diffname pc/etherelnk3.c 1998/0401
## diff -e /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c
1527a
		USED(x);
.
## diffname pc/etherelnk3.c 1998/0505
## diff -e /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c
1767c
		for(i = 0; i < ether->nopt; i++){
			if(cistrcmp(ether->opt[i], "fullduplex") == 0)
				an |= 0x0100;
			else if(cistrcmp(ether->opt[i], "force100") == 0)
				an |= 0x0080;
		}
		XCVRDEBUG("mii an: %uX\n", an);
		if(an & 0x380)
.
1761a
		scanphy(ether);
.
1753a
/*
 * forgive me, but i am weak
 */
if(did == 0x9055)
   xcvr = xcvrMii;
else
.
1695a
	case 0x9055:
.
1595,1606d
1429,1434c
	txrxreset(port);
.
1412,1417c
	txrxreset(port);
.
1358,1359c
		txrxreset(port);
.
1319,1320c
		txrxreset(port);
.
1162a
static void
txrxreset(int port)
{
	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
}

.
34a
 *	9055 PCI	3C905B-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
.
## diffname pc/etherelnk3.c 1998/0605
## diff -e /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c
1872d
1760,1761c
		an = miir(port, phyaddr, 0x04);
		an &= miir(port, phyaddr, 0x05) & 0x03E0;
.
1757c
		scanphy(port);
.
1751c
	XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did);
.
1526,1527c
		x |= miir(port, i, 3)>>10;
		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
.
1523c
		if((x = miir(port, i, 2)) == -1 || x == 0)
.
1518c
scanphy(int port)
.
1512d
1508c
	port -= PhysicalMgmt;
	COMMAND(port, SelectRegisterWindow, w);
.
1446a
	port += PhysicalMgmt;

.
1445c
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
.
1442,1443d
1440c
	int data, i, w, x;
.
1438c
miir(int port, int phyad, int regad)
.
## diffname pc/etherelnk3.c 1998/0606
## diff -e /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c
1765a
			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
				an |= 0x0100;
.
## diffname pc/etherelnk3.c 1998/0725
## diff -e /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c
1775a
		}
		else if(anar & 0x0200)		/* 100BASE-T4 */
			;
		else if(anar & 0x0080)		/* 100BASE-TX */
			ether->mbps = 100;
		else if(anar & 0x0040)		/* 10BASE-TFD */
			setfullduplex(port);
		else				/* 10BASE-T */
			;
.
1774d
1771,1772c
		XCVRDEBUG("mii anar: %uX\n", anar);
		if(anar & 0x0100){		/* 100BASE-TXFD */
.
1769c
				anar |= 0x0080;
.
1767c
				anar |= 0x0100;
.
1765c
				anar |= 0x0100;
.
1760,1762c
		anar = miir(port, phyaddr, 0x04);
		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
		anar &= anlpar;
		miir(port, phyaddr, 0x00);
		XCVRDEBUG("mii an: %uX r0:%uX r1:%uX\n",
			anar, anlpar, miir(port, phyaddr, 0x00), miir(port, phyaddr, 0x01));
.
1635c
	int anar, anlpar, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
## diffname pc/etherelnk3.c 1998/0811
## diff -e /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c
1764c
		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
.
## diffname pc/etherelnk3.c 1998/0825
## diff -e /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c
434,435c
	ulong	dninterrupts;
	ulong	dnqueued;
.
429,431c
	ulong	upinterrupts;
	ulong	upqueued;
	ulong	upstalls;
.
## diffname pc/etherelnk3.c 1998/1024
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c
830a
			coherence();
.
755a
		coherence();
.
## diffname pc/etherelnk3.c 1998/1027
## diff -e /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c
1063,1064c
			if(s & ~(txStatusComplete|maxCollisions))
				print("#l%d: txstatus 0x%uX, threshold %d\n",
			    		ether->ctlrno, s, ctlr->txthreshold);
.
1047a
			 * On busy or badly configured networks maxCollisions can
			 * happen frequently enough for messages to be annoying so
			 * keep quiet about them by popular request.
.
## diffname pc/etherelnk3.c 1999/0130
## diff -e /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c
1687a
	case 0x9005:
.
32a
 *	9005 PCI	3C900B-COMBO	Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
.
## diffname pc/etherelnk3.c 1999/0219
## diff -e /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c
1405,1406c
	int i;

	for(i = 0; tcmpcmcia[i] != nil; i++){
		if(!cistrcmp(ether->type, tcmpcmcia[i]))
			return ether->port;
	}
.
1401a
static char* tcmpcmcia[] = {
	"3C589",			/* 3COM 589[ABCD] */
	"3C562",			/* 3COM 562 */
	"589E",				/* 3COM Megahertz 589E */
	nil,
};

.
## diffname pc/etherelnk3.c 1999/0314
## diff -e /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c
1398a
		pcisetbme(p);
.
## diffname pc/etherelnk3.c 1999/0318
## diff -e /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0318/sys/src/brazil/pc/etherelnk3.c
1785c
			anar, anlpar, miir(port, phyaddr, 0x00),
			miir(port, phyaddr, 0x01));
.
1779a
for(i = 0; i < 7; i++)
    XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
XCVRDEBUG("\n");

{	int phystat, timeo;
	for(timeo = 0; timeo < 30; timeo++){
		phystat = miir(port, phyaddr, 0x01);
		if(phystat & 0x20)
			break;
		XCVRDEBUG(" %2.2uX", phystat);
		delay(100);
	}
	XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
	XCVRDEBUG("\n");
}

.
1767a
   XCVRDEBUG("9055 reset ops 0x%uX\n",
	ins(port+ResetOp905B));
}
.
1766c
if(did == 0x9055){
.
1523,1525c
	miimdo(port, 0xFFFFFFFF, 32);
	miimdo(port, 0x1800|(phyad<<5)|regad, 14);
	data = miimdi(port, 18);
.
1521c
	 * Preamble;
	 * ST+OP+PHYAD+REGAD;
	 * TA + 16 data bits.
.
1519a
static int
miir(int port, int phyad, int regad)
{
	int data, w;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	port += PhysicalMgmt;

.
1518a
}
.
1514,1516c
		outs(port, mdo);
.
1512c
		outs(port, mdo|mgmtClk);
.
1509,1510c
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			mdo = mgmtDir|mgmtData;
		else
			mdo = mgmtDir;
		outs(port, mdo);
.
1507c
	 * Write n bits to the MII Management Register.
.
1505a
static void
miimdo(int port, int bits, int n)
{
	int i, mdo;

.
1495,1504c
	return data;
}
.
1491,1492d
1478,1489c
		outs(port, 0);
.
1476c
		outs(port, mgmtClk);
.
1470,1474c
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(ins(port) & mgmtData)
			data |= (1<<i);
.
1467,1468c
	 * Read n bits from the MII Management Register.
.
1461,1465d
1459c
	int data, i;
.
1457c
miimdi(int port, int n)
.
225a
	MediaOptions		= 0x0008,	/* 3C905B */
.
215a

	ResetOp905B		= 0x000C,
.
## diffname pc/etherelnk3.c 1999/0714
## diff -e /n/emeliedump/1999/0318/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0714/sys/src/brazil/pc/etherelnk3.c
1420a
		}
.
1419c
		if(!cistrcmp(ether->type, tcmpcmcia[i])){
			if(ioalloc(ether->port, 0x20, 0, "tcm5XXpcmcia") < 0)
				return 0;
.
1395a
		if(ioalloc(port, p->mem[0].size, 0, "tcm59Xpci") < 0){
			print("tcm59Xpci: port %d in use\n", port);
			continue;
		}
.
1374a
		}
.
1373c
		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
			iofree(port);
.
1371a
		}
		if(ins(port+0xC80+ManufacturerID) != 0x6D50){
			iofree(port);
			continue;
		}
.
1370c
		if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
			print("tcm5XXeisa: port %d in use\n", port);
.
1320a
		}
.
1319c
		if(port == 0x3F0){
			iofree(port);
.
1314a
		if(ioalloc(port, 0x20, 0, "tcm509isa") < 0){
			print("tcm509isa:port %d in use\n", port);
			continue;
		}

.
## diffname pc/etherelnk3.c 1999/0716
## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c
1440a
			 */
.
1439c
			/*
			 * No need for an ioalloc here, the 589 reset
			 * code deals with it.
			if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0)
.
1315c
		if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
.
## diffname pc/etherelnk3.c 1999/0916
## diff -e /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c
1782a

	/*
	 * Allow user to specify desired media in plan9.ini
	 */
	for(i = 0; i < ether->nopt; i++){
		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
			continue;
		p = ether->opt[i]+6;
		for(j = 0; j < nelem(media); j++)
			if(cistrcmp(p, media[j].name) == 0)
				xcvr = media[j].xcvr;
	}
	
.
1679a
	char *p;
.
1674c
	int anar, anlpar, busmaster, did, i, j, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
1591d
1586a
static struct {
	char *name;
	int avail;
	int xcvr;
} media[] = {
	"10BaseT",	base10TAvailable,	xcvr10BaseT,
	"10Base2",	coaxAvailable,		xcvr10Base2,
	"100BaseTX",	baseTXAvailable,	xcvr100BaseTX,
	"100BaseFX",	baseFXAvailable,	xcvr100BaseFX,
	"aui",		auiAvailable,		xcvrAui,
	"mii",		miiConnector,		xcvrMii
};

.
## diffname pc/etherelnk3.c 2000/0612
## diff -e /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c
1812c
if(did == 0x9055 || did ==0x9200){
.
1737a
	case 0x9200:
.
36a
 *	9200 PCI	3C905C-TX	Fast Etherlink Shared 10BASE-T/100BASE-TX
.
## diffname pc/etherelnk3.c 2000/0619
## diff -e /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c
1413c
			print("tcm59Xpci: port 0x%uX in use\n", port);
.
1379c
			print("tcm5XXeisa: port 0x%uX in use\n", port);
.
1317c
			print("tcm509isa: port 0x%uX in use\n", port);
.
## diffname pc/etherelnk3.c 2000/0907
## diff -e /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c
1816c
   XCVRDEBUG("905[BC] reset ops 0x%uX\n",
.
1814c
if(did == 0x9055 || did == 0x9200){
.
## diffname pc/etherelnk3.c 2000/0921
## diff -e /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c
1928,1929c
	if(busmaster == 2)
		x = port+TxStatus905;
	else
		x = port+TxStatus;
	while(inb(x))
		outb(x, 0);
.
1138,1149c
	len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
		ctlr->timer[0], ctlr->timer[1]);
	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
		ctlr->stats[CarrierLost]);
	len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
		ctlr->stats[SqeErrors]);
	len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
		ctlr->stats[MultipleColls]);
	len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
		ctlr->stats[SingleCollFrames]);
	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
		ctlr->stats[LateCollisions]);
	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
		ctlr->stats[RxOverruns]);
	len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
		ctlr->stats[FramesXmittedOk]);
	len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
		ctlr->stats[FramesRcvdOk]);
	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
		ctlr->stats[FramesDeferred]);
	len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
		ctlr->stats[BytesRcvdOk]);
	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
		ctlr->stats[BytesRcvdOk+1]);
.
1115a
	if(ctlr->busmaster == 2)
		ctlr->timer[1] += inb(port+Timer905) & 0xFF;
	else
		ctlr->timer[1] += inb(port+Timer) & 0xFF;

.
1114c
	}while((status = STATUS(port)) & (interruptMask|interruptLatch));
.
1031,1032c
				if(x = inb(txstatus))
					outb(txstatus, 0);
.
1028a
			if(ctlr->busmaster == 2)
				txstatus = port+TxStatus905;
			else
				txstatus = port+TxStatus;
.
965,966c
	if(ctlr->busmaster == 2)
		ctlr->timer[0] += inb(port+Timer905) & 0xFF;
	else
		ctlr->timer[0] += inb(port+Timer) & 0xFF;

	do{
.
961c
	status = STATUS(port);
	if(!(status & (interruptMask|interruptLatch))){
		iunlock(&ctlr->wlock);
		return;
	}
	w = (status>>13) & 0x07;
.
953c
	int port, status, s, txstatus, w, x;
.
429c
	long	timer[2];
.
346a
	Timer905		= 0x001A,	/* 8-bits */
	TxStatus905		= 0x001B,	/* 8-bits */
.
## diffname pc/etherelnk3.c 2001/0330
## diff -e /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c
1867,1877c
		for(timeo = 0; timeo < 30; timeo++){
			phystat = miir(port, phyaddr, 0x01);
			if(phystat & 0x20)
				break;
			XCVRDEBUG(" %2.2uX", phystat);
			delay(100);
		}
		XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
		XCVRDEBUG("\n");
.
1863,1865c
		for(i = 0; i < 7; i++)
			XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
			XCVRDEBUG("\n");
.
1854a

.
1843,1852c
	/*
	 * forgive me, but i am weak
	 */
	if(did == 0x9055 || did == 0x9200){
		xcvr = xcvrMii;
		txrxreset(port);
		XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B));
	}
	else if(xcvr & autoSelect)
.
1808,1809c
	 * If not, read it from the EEPROM and set in ether->ea prior to
	 * loading the station address in Wstation.
	 * The EEPROM returns 16-bits at a time.
.
1719c
	int anar, anlpar, phyaddr, phystat, timeo, xcvr;
	int busmaster, did, i, j, port, rxearly, rxstatus9, x;
.
9,10c
 *	rewrite all initialisation.
.
## diffname pc/etherelnk3.c 2001/0504
## diff -e /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c
2022c
		ctlr->rbp = rbpalloc(iallocb);
		if(ctlr->rbp == nil)
			panic("can't reset ethernet: out of memory");
.
2016,2017c
		else {
			ctlr->rbp = rbpalloc(iallocb);
			if(ctlr->rbp == nil)
				panic("can't reset ethernet: out of memory");
		}
.
1230c
	bp = iallocb(sizeof(Adapter));
	if(bp == nil)
		return;
.
470c
		bp = iallocb(sizeof(Etherpkt));
		if(bp == nil)
			panic("can't allocate ethernet receive ring");
.
## diffname pc/etherelnk3.c 2001/0510
## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c
1445a
		/*
		 * Not prepared to deal with memory-mapped
		 * devices yet.
		 */
		if(!(p->mem[0].bar & 0x01))
			continue;
.
## diffname pc/etherelnk3.c 2001/0518
## diff -e /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c
1914,1915c
		else{				/* 10BASE-T */
			/* nothing to do */
		}
.
1908,1909c
		else if(anar & 0x0200){		/* 100BASE-T4 */
			/* nothing to do */
		}
.
## diffname pc/etherelnk3.c 2001/0622
## diff -e /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c
2074a
	addethercard("3C575", etherelnk3reset);
.
2019c
		x = eepromdata(did, port, 0x0F);
.
1873c
		phyaddr = (did == 0x5157)? 0: 24;
.
1860a
		if (did == 0x5157) {
			ushort reset_opts;

			COMMAND(port, SelectRegisterWindow, Wstation);
			reset_opts = ins(port + ResetOp905B);
			reset_opts |= 0x0010;		/* Invert LED */
			outs(port + ResetOp905B, reset_opts);
		}
.
1857c
	if(did == 0x9055 || did == 0x9200 || did == 0x5157){
.
1825c
			x = eepromdata(did, port, i);
.
1781a
	case 0x5157:		/* 3C575 Cyclone */
.
1773c
	switch(did = eepromdata(did, port, 0x03)){
.
1759a
			ether->mem = ap->cbfns;
.
1757a
			did = ap->did;
.
1752a
	did = 0;
.
1719c
	EEPROMCMD(port, (did == 0x5157)? EepromRead8bRegister: EepromReadRegister, offset);
.
1714c
eepromdata(int did, int port, int offset)
.
1462c
		bar = 0;
		if (p->did == 0x5157) {
			/* Map the CardBus functions */
			bar = pcicfgr32(p, PciBAR2);
			print("tcmp59Xpci: CardBus functions at %.8uX\n", bar & ~KZERO);
		}

		tcmadapter(port, irq, p->tbdf, p->did, bar);
.
1445a
		ulong bar;

.
1434c
		tcmadapter(port, irq, BUSUNKNOWN, 0, 0);
.
1388c
		tcmadapter(port, irq, BUSUNKNOWN, 0, 0);
.
1238a
	ap->did = did;
	ap->cbfns = cbfns;
.
1227c
tcmadapter(int port, int irq, int tbdf, int did, ulong cbfns)
.
1222a
	int	did;
	ulong cbfns;
.
1129a
		if (ether->mem) intrack3c575((ulong *)KADDR(ether->mem));

.
610a
	if (ether->mem)
		/* This must be a cardbus card.  Acknowledge the interrupt */
		intrack3c575(KADDR(ether->mem));
		
.
607d
574a
intrack3c575(ulong *cbfns)
{
	cbfns[1] = 0x8000;
}

static void
.
573a
/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
.
169a
	EepromRead8bRegister	= 0x0230,
.
## diffname pc/etherelnk3.c 2001/0623
## diff -e /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c
2035a

	ctlr->did = did;
	ctlr->cbfns = cbfns;
.
1788c
			cbfns = ap->cbfns;
.
1779a
	cbfns = 0;
.
1761a
	ulong cbfns;
.
1485c
			print("tcmp59Xpci: CardBus functions at %.8ulX\n", bar & ~KZERO);
.
1141c
		if (ctlr->cbfns) intrack3c575((ulong *)KADDR(ctlr->cbfns));
.
998a
	
			if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
				print("#l%d: Card ejected?\n", ether->ctlrno);
				iunlock(&ctlr->wlock);
				return;
			}

.
962a
static int
ejectable(int did)
{
	switch (did) {
	case 0x5157:
		return 1;

	default:
		return 0;
	}
}

.
620c
		intrack3c575(KADDR(ctlr->cbfns));
.
618c
	if (ctlr->cbfns)
.
443a
	int	did;				/* Controller's device ID */
	ulong	cbfns;		/* CardBus functions */

.
## diffname pc/etherelnk3.c 2001/0720
## diff -e /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c
1813,1814c
			// *bpp = bp->next;		// HIRO.
			// freeb(bp);
.
## diffname pc/etherelnk3.c 2001/0920
## diff -e /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c
1493a
		print("tcm59Xpci: Allocating port %X\n", port);
.
## diffname pc/etherelnk3.c 2001/0925
## diff -e /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c
1494,1495c
		if((port = ioalloc((port == 0)? -1: port,  p->mem[0].size, 
					  0, "tcm59Xpci")) < 0){
.
## diffname pc/etherelnk3.c 2002/0315
## diff -e /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c
1912c
	if(did == 0x9055 || did == 0x7646 || did == 0x9200 || did == 0x5157){
.
1835a
	case 0x7646:		/* 3CSOHO100-TX */
.
## diffname pc/etherelnk3.c 2002/0403
## diff -e /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c
1815a
			ap->active = 1;
.
1807a
		if(ap->active)
			continue;
.
1529c
		if(ether->type==nil || !cistrcmp(ether->type, tcmpcmcia[i])){
.
1259a
	int	active;
.
## diffname pc/etherelnk3.c 2002/0405
## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c
1170c
		ctlr->timer[1] += inb(port+Timerx) & 0xFF;
.
1002c
		ctlr->timer[0] += inb(port+Timerx) & 0xFF;
.
184c
	Timerx			= 0x000A,
.
## diffname pc/etherelnk3.c 2002/0411
## diff -e /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c
1499c

.
1496,1497c
					  0, "tcm59Xpci")) < 0)
.
1456c

.
1453,1454c
		if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0)
.
1394d
1391,1392c
		if(ioalloc(port, 0x10, 0, "tcm509isa") < 0)
.
## diffname pc/etherelnk3.c 2002/0414
## diff -e /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c
852a
			coherence();
.
779a
		coherence();
.
## diffname pc/etherelnk3.c 2002/0502
## diff -e /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c
28a

 *	7646 PCI   3CSOHO100-TX Fast Etherlink Small Office Connect (9050 downsized clone)
.
## diffname pc/etherelnk3.c 2002/0510
## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c
1917c
	if(ismii){
.
1843a
		}
.
1838,1842c
		if(BUSTYPE(ether->tbdf) != BusPCI){
			ismii = 0;
.
1832a
	case 0x9055:
	case 0x9200:
	case 0x7646:		/* 3CSOHO100-TX */
	case 0x5157:		/* 3C575 Cyclone */
	case 0x6056:
		ismii = 1;
		/*FALLTHROUGH*/
.
1830a
	ismii = 0;
.
1779c
	int busmaster, did, i, ismii, j, port, rxearly, rxstatus9, x;
.
## diffname pc/etherelnk3.c 2002/0511
## diff -e /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c
2150,2151c
	addethercard("elnk3", etherelnk3reset);
	addethercard("3C509", etherelnk3reset);
.
2134d
2127c
	COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
.
2096c
		x = eepromdata(ctlr, 0x0F);
.
2066,2075d
2062d
2058,2060d
2055,2056c
	 * and enable statistics collection.
.
2053c
	 * Clear out the
.
2045c
	if(ctlr->busmaster == 2)
.
2022d
2019,2020c
		if((ctlr->did & 0xFF00) == 0x5900)
			ctlr->busmaster = 0;
.
2008d
1993d
1947c
		if(ctlr->did == 0x5157)
			phyaddr = 0;
		else
			phyaddr = 24;
.
1940,1941c
	switch(ctlr->xcvr){
.
1936,1938c
	XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
.
1934a
		break;
.
1927c
		if(ctlr->did == 0x5157) {
.
1923,1924c
	switch(ctlr->did){
	default:
		if(ctlr->xcvr & autoSelect)
			ctlr->xcvr = autoselect(ctlr);
		break;
	case 0x6056:
	case 0x5157:
	case 0x7646:
	case 0x9055:
	case 0x9200:
		ctlr->xcvr = xcvrMii;
.
1917c
				ctlr->xcvr = media[j].xcvr;
.
1906c
	XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
.
1891c
			x = eepromdata(ctlr, i);
.
1880a
	if(ctlr->rxearly >= 2048)
		ctlr->ts = 2;
.
1876,1878c
			ctlr->xcvr |= autoSelect;
		ctlr->rxearly = 2044;
		ctlr->rxstatus9 = 1;
.
1874c
		ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
.
1871c
		ctlr->busmaster = 0;
.
1868d
1864,1866c
		ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
		ctlr->rxearly = 8188;
		ctlr->rxstatus9 = 0;
.
1852,1861c
	case 0x5900:		/* 3C590-[TP|COMBO|TPO] */
	case 0x5920:		/* 3C592-[TP|COMBO|TPO] */
	case 0x5950:		/* 3C595-TX */
	case 0x5951:		/* 3C595-T4 */
	case 0x5952:		/* 3C595-MII */
	case 0x5970:		/* 3C597-TX */
	case 0x5971:		/* 3C597-T4 */
	case 0x5972:		/* 3C597-MII */
		ctlr->busmaster = 1;
.
1849,1850c
		ctlr->busmaster = 2;
.
1841,1847c
	case 0x9000:		/* 3C900-TPO */
	case 0x9001:		/* 3C900-COMBO */
	case 0x9005:		/* 3C900B-COMBO */
	case 0x9050:		/* 3C905-TX */
	case 0x9051:		/* 3C905-T4 */
		if(BUSTYPE(ether->tbdf) != BusPCI)
.
1839c
	case 0x7646:		/* 3CSOHO100-TX */
	case 0x9055:		/* 3C905B-TX */
	case 0x9200:		/* 3C905C-TX */
.
1837a
		ctlr->cbfns = KADDR(pcicfgr32(ctlr->pcidev, PciBAR2));
		/*FALLTHROUGH*/
.
1831,1836c
	switch(ctlr->did = eepromdata(ctlr, 0x03)){
.
1826a
	ether->ctlr = ctlr;
	port = ctlr->port;
	ether->port = port;
	ether->irq = ctlr->irq;
	if(ctlr->pcidev != nil)
		ether->tbdf = ctlr->pcidev->tbdf;
	else
		ether->tbdf = BUSUNKNOWN;

.
1824c
	if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
.
1822d
1811,1819c
		if(ether->port == 0 || ether->port == ctlr->port){
			ctlr->active = 1;
.
1803,1809c
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
.
1785,1786c
	int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
.
1783a
	uchar ea[Eaddrlen];
.
1778,1782c
	char *p;
.
1769c
	if(ctlr->pcidev && ctlr->pcidev->did == 0x5157)
		EEPROMCMD(port, EepromRead8bRegister, offset);
	else
		EEPROMCMD(port, EepromReadRegister, offset);
.
1765a
	int port;

	port = ctlr->port;

.
1764c
eepromdata(Ctlr* ctlr, int offset)
.
1744c
		setxcvr(ctlr, xcvr10BaseT);
.
1731c
		setxcvr(ctlr, xcvr100BaseTX);
.
1704c
	port = ctlr->port;
	if(ctlr->rxstatus9){
.
1695,1696c
	int media, port, x;
.
1693c
autoselect(Ctlr* ctlr)
.
1663,1678d
1549c
	port = ctlr->port;
	if(ctlr->rxstatus9){
.
1547c
	int port, x;
.
1545c
setxcvr(Ctlr* ctlr, int xcvr)
.
1540,1541c
	return nil;
.
1530,1538c
		if(cistrcmp(ether->type, tcmpcmcia[i]))
			continue;
		return tcmadapter(ether->port, ether->irq, nil);
.
1528a
	if(ether->type == nil)
		return nil;

.
1524c
static Ctlr*
.
1505,1512c
		tcmadapter(port, irq, p);
.
1499c
		}
.
1497c
					  0, "tcm59Xpci")) < 0){
			print("tcm59Xpci: port 0x%uX in use\n", port);
.
1487,1488d
1475c
		tcmadapter(port, irq, nil);
.
1457c
		}
.
1455c
		if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
			print("tcm5XXeisa: port 0x%uX in use\n", port);
.
1430c
		tcmadapter(port, irq, nil);
.
1396a
		}
.
1395c
		if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
			print("tcm509isa: port 0x%uX in use\n", port);
.
1284,1285c
	if(ctlrhead != nil)
		ctlrtail->next = ctlr;
	else
		ctlrhead = ctlr;
	ctlrtail = ctlr;

	return ctlr;
.
1274,1282c
	ctlr = malloc(sizeof(Ctlr));
	ctlr->port = port;
	ctlr->irq = irq;
	if(pcidev != nil){
		ctlr->pcidev = pcidev;
	}
.
1271,1272c
	Ctlr *ctlr;
.
1258,1269c
static Ctlr*
tcmadapter(int port, int irq, Pcidev* pcidev)
.
1197a
	len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
.
1174c
		ctlr->timer[1] += inb(port+TIMER) & 0xFF;
.
1172c
		ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
.
1167c
		if(ctlr->cbfns != nil)
			intrack3c575(ctlr->cbfns);
.
1006c
		ctlr->timer[0] += inb(port+TIMER) & 0xFF;
.
1004c
		ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
.
995a
		ctlr->bogusinterrupts++;
.
856d
782d
623,625c
	/*
	 * If this is a CardBus card, acknowledge any interrupts.
	 */
	if(ctlr->cbfns != nil)
		intrack3c575(ctlr->cbfns);
.
456a
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;

.
454a
	ulong*	cbfns;			/* CardBus functions */
.
446,452c
	int	xcvr;			/* transceiver type */
	int	rxstatus9;		/* old-style RxStatus register */
	int	rxearly;		/* RxEarlyThreshold */
	int	ts;			/* threshold shift */
.
432c
	long	interrupts;		/* statistics */
	long	bogusinterrupts;
.
425c
	int	ndn;			/* full-busmaster -based transmission */
.
420c
	int	nup;			/* full-busmaster -based reception */
.
416c
	Block*	txbp;			/* FIFO -based transmission */
.
414c
	Block*	rbp;			/* receive buffer */
.
411a
	Lock	wlock;			/* window access */

.
409,410c
typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	int	irq;
	Ctlr*	next;
	int	active;
	int	did;
.
400,401c
	ulong	np;			/* next pointer */
	ulong	control;		/* FSH or UpPktStatus */
.
358c
	FreeTIMER		= 0x0034,	/* 16-bits */
.
349c
	TIMER905		= 0x001A,	/* 8-bits */
.
251c
	deferTIMERSelect	= 0x001E,	/* mask */
.
186c
	TIMER			= 0x000A,
.
10,43d
## diffname pc/etherelnk3.c 2002/0524
## diff -e /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c
1871a
	case 0x6056:
.
1870c
	case 0x4500:
.
1779a
	case 0x4500:		/* 3C450 HomePNA Tornado */
.
## diffname pc/etherelnk3.c 2002/0530
## diff -e /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0530/sys/src/9/pc/etherelnk3.c
1500c
		ctlr = tcmadapter(ether->port, ether->irq, nil);
		ctlr->active = 1;
		return ctlr;
.
1492a
	Ctlr *ctlr;
.
1245,1247c
	ctlr->pcidev = pcidev;
.

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].