etherelnk3.c

来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,932 行 · 第 1/3 页

C
1,932
字号
		txrxreset(port);		COMMAND(port, AcknowledgeInterrupt, 0xFF);		ctlr = tcmadapter(port, irq, p);		switch(p->did){		default:			break;		case 0x5157:			ctlr->eepromcmd = EepromRead8bRegister;			ctlr->cbfnpa = p->mem[2].bar&~0x0F;			ctlr->cbfn = KADDR(ctlr->cbfnpa);			break;		case 0x6056:			ctlr->eepromcmd = EepromReadOffRegister;			ctlr->cbfnpa = p->mem[2].bar&~0x0F;			ctlr->cbfn = KADDR(ctlr->cbfnpa);			break;		}		pcisetbme(p);	}}static char* tcmpcmcia[] = {	"3C589",			/* 3COM 589[ABCD] */	"3C562",			/* 3COM 562 */	"589E",				/* 3COM Megahertz 589E */	nil,};static Ctlr*tcm5XXpcmcia(Ether* ether){	int i;	Ctlr *ctlr;	if(ether->type == nil)		return nil;	for(i = 0; tcmpcmcia[i] != nil; i++){		if(cistrcmp(ether->type, tcmpcmcia[i]))			continue;		ctlr = tcmadapter(ether->port, ether->irq, nil);		ctlr->active = 1;		return ctlr;	}	return nil;}static voidsetxcvr(Ctlr* ctlr, int xcvr){	int port, x;	port = ctlr->port;	if(ctlr->rxstatus9){		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);	}	txrxreset(port);}static voidsetfullduplex(int port){	int x;	COMMAND(port, SelectRegisterWindow, Wfifo);	x = ins(port+MacControl);	outs(port+MacControl, fullDuplexEnable|x);	txrxreset(port);}static intmiimdi(int port, int n){	int data, i;	/*	 * Read n bits from the MII Management Register.	 */	data = 0;	for(i = n-1; i >= 0; i--){		if(ins(port) & mgmtData)			data |= (1<<i);		microdelay(1);		outs(port, mgmtClk);		microdelay(1);		outs(port, 0);		microdelay(1);	}	return data;}static voidmiimdo(int port, int bits, int n){	int i, mdo;	/*	 * Write n bits to the MII Management Register.	 */	for(i = n-1; i >= 0; i--){		if(bits & (1<<i))			mdo = mgmtDir|mgmtData;		else			mdo = mgmtDir;		outs(port, mdo);		microdelay(1);		outs(port, mdo|mgmtClk);		microdelay(1);		outs(port, mdo);		microdelay(1);	}}static intmiir(int port, int phyad, int regad){	int data, w;	w = (STATUS(port)>>13) & 0x07;	COMMAND(port, SelectRegisterWindow, Wdiagnostic);	port += PhysicalMgmt;	/*	 * Preamble;	 * ST+OP+PHYAD+REGAD;	 * TA + 16 data bits.	 */	miimdo(port, 0xFFFFFFFF, 32);	miimdo(port, 0x1800|(phyad<<5)|regad, 14);	data = miimdi(port, 18);	port -= PhysicalMgmt;	COMMAND(port, SelectRegisterWindow, w);	if(data & 0x10000)		return -1;	return data & 0xFFFF;}static intscanphy(int port){	int i, x;	for(i = 0; i < 32; i++){		if((x = miir(port, i, 2)) == -1 || x == 0)			continue;		x <<= 6;		x |= miir(port, i, 3)>>10;		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));		USED(x);		return i;	}	return 24;}#ifdef notdefstatic 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 */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};static intautoselect(Ctlr* ctlr){	int media, port, x;	/*	 * Pathetic attempt at automatic media selection.	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX	 * cards operational.	 * It's a bonus if it works for anything else.	 */	port = ctlr->port;	if(ctlr->rxstatus9){		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{		COMMAND(port, SelectRegisterWindow, Wfifo);		media = ins(port+ResetOptions);	}	XCVRDEBUG("autoselect: media %uX\n", media);	if(media & miiConnector)		return xcvrMii;	COMMAND(port, SelectRegisterWindow, Wdiagnostic);	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));	if(media & baseTXAvailable){		/*		 * Must have InternalConfig register.		 */		setxcvr(ctlr, xcvr100BaseTX);		COMMAND(port, SelectRegisterWindow, Wdiagnostic);		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);		outs(port+MediaStatus, linkBeatEnable|x);		delay(10);		if(ins(port+MediaStatus) & linkBeatDetect)			return xcvr100BaseTX;		outs(port+MediaStatus, x);	}	if(media & base10TAvailable){		setxcvr(ctlr, xcvr10BaseT);		COMMAND(port, SelectRegisterWindow, Wdiagnostic);		x = ins(port+MediaStatus) & ~dcConverterEnabled;		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);		delay(100);		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));		if(ins(port+MediaStatus) & linkBeatDetect)			return xcvr10BaseT;		outs(port+MediaStatus, x);	}	/*	 * Botch.	 */	return autoSelect;}static inteepromdata(Ctlr* ctlr, int offset){	int port;	port = ctlr->port;	COMMAND(port, SelectRegisterWindow, Wsetup);	while(EEPROMBUSY(port))		;	EEPROMCMD(port, ctlr->eepromcmd, offset);	while(EEPROMBUSY(port))		;	return EEPROMDATA(port);}static voidresetctlr(Ctlr *ctlr){	int port, x;	port = ctlr->port;	txrxreset(port);	x = ins(port+ResetOp905B);	XCVRDEBUG("905[BC] reset ops 0x%uX\n", x);	x &= ~0x4010;	if(ctlr->did == 0x5157){		x |= 0x0010;			/* Invert LED */		outs(port+ResetOp905B, x);	}	if(ctlr->did == 0x6056){		x |= 0x4000;		outs(port+ResetOp905B, x);		COMMAND(port, SelectRegisterWindow, Wsetup);		outs(port, 0x0800);	}}intelnk3reset(Ether* ether){	char *p;	Ctlr *ctlr;	uchar ea[Eaddrlen];	static int scandone;	int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;	/*	 * Scan for adapter on PCI, EISA and finally	 * using the little ISA configuration dance.	 */	if(scandone == 0){		tcm59Xpci();		tcm5XXeisa();		tcm509isa();		scandone = 1;	}	/*	 * Any adapter matches if no ether->port is supplied,	 * otherwise the ports must match.	 */	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){		if(ctlr->active)			continue;		if(ether->port == 0 || ether->port == ctlr->port){			ctlr->active = 1;			break;		}	}	if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)		return -1;	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;	/*	 * Read the DeviceID from the EEPROM, it's at offset 0x03,	 * and do something depending on capabilities.	 */	switch(ctlr->did = eepromdata(ctlr, 0x03)){	case 0x5157:		/* 3C575 Cyclone */	case 0x6056:		/*FALLTHROUGH*/	case 0x4500:		/* 3C450 HomePNA Tornado */	case 0x7646:		/* 3CSOHO100-TX */	case 0x9055:		/* 3C905B-TX */	case 0x9200:		/* 3C905C-TX */	case 0x9201:		/* 3C920 */	case 0x9805:		/* 3C9805: 3C980-TX Python-T 10/100baseTX */		/*FALLTHROUGH*/	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)			goto buggery;		ctlr->busmaster = 2;		goto vortex;	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;	vortex:		COMMAND(port, SelectRegisterWindow, Wfifo);		ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);		ctlr->rxearly = 8188;		ctlr->rxstatus9 = 0;		break;	buggery:	default:		ctlr->busmaster = 0;		COMMAND(port, SelectRegisterWindow, Wsetup);		x = ins(port+AddressConfig);		ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;		if(x & autoSelect9)			ctlr->xcvr |= autoSelect;		ctlr->rxearly = 2044;		ctlr->rxstatus9 = 1;		break;	}	if(ctlr->rxearly >= 2048)		ctlr->ts = 2;	/*	 * 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){		for(i = 0; i < Eaddrlen/2; i++){			x = eepromdata(ctlr, i);			ether->ea[2*i] = x>>8;			ether->ea[2*i+1] = x;		}	}	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.	 */	XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);	/*	 * 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)				ctlr->xcvr = media[j].xcvr;	}		/*	 * forgive me, but i am weak	 */	switch(ctlr->did){	default:		if(ctlr->xcvr & autoSelect)			ctlr->xcvr = autoselect(ctlr);		break;	case 0x5157:	case 0x6056:	case 0x4500:	case 0x7646:	case 0x9055:	case 0x9200:	case 0x9201:	case 0x9805:		ctlr->xcvr = xcvrMii;		resetctlr(ctlr);		break;	}	XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);	switch(ctlr->xcvr){	case xcvrMii:		/*		 * Quick hack.		 */		if(ctlr->did == 0x5157)			phyaddr = 0;		else if(ctlr->did == 0x6056)			phyaddr = scanphy(port);		else			phyaddr = 24;		for(i = 0; i < 7; i++)			XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));			XCVRDEBUG("\n");		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");		anar = miir(port, phyaddr, 0x04);		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;		anar &= anlpar;		miir(port, phyaddr, 0x00);		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",			anar, anlpar, miir(port, phyaddr, 0x00),			miir(port, phyaddr, 0x01));		for(i = 0; i < ether->nopt; i++){			if(cistrcmp(ether->opt[i], "fullduplex") == 0)				anar |= 0x0100;			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)				anar |= 0x0100;			else if(cistrcmp(ether->opt[i], "force100") == 0)				anar |= 0x0080;		}		XCVRDEBUG("mii anar: %uX\n", anar);		if(anar & 0x0100){		/* 100BASE-TXFD */			//ether->mbps = 100;			setfullduplex(port);		}		else if(anar & 0x0200){		/* 100BASE-T4 */			/* nothing to do */		}		else if(anar & 0x0080){		/* 100BASE-TX */			//ether->mbps = 100;		}		else if(anar & 0x0040)		/* 10BASE-TFD */			setfullduplex(port);		else{				/* 10BASE-T */			/* nothing to do */;		}		break;	case xcvr100BaseTX:	case xcvr100BaseFX:		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;	case xcvr10BaseT:		/*		 * Enable Link Beat and Jabber to start the		 * transceiver.		 */		COMMAND(port, SelectRegisterWindow, Wdiagnostic);		x = ins(port+MediaStatus) & ~dcConverterEnabled;		x |= linkBeatEnable|jabberGuardEnable;		outs(port+MediaStatus, x);		if((ctlr->did & 0xFF00) == 0x5900)			ctlr->busmaster = 0;		break;	case xcvr10Base2:		COMMAND(port, SelectRegisterWindow, Wdiagnostic);		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);		outs(port+MediaStatus, x);		/*		 * 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);	if(ctlr->busmaster == 2)		x = port+TxStatus905;	else		x = port+TxStatus;	while(inb(x))		outb(x, 0);	/*	 * Clear out the	 * adapter statistics, clear the statistics logged into ctlr.	 */	ilock(&ctlr->wlock);	statistics(ether);	memset(ctlr->stats, 0, sizeof(ctlr->stats));	//COMMAND(port, StatisticsEnable, 0);	/*	 * Allocate any receive buffers.	 */	if (ctlr->busmaster == 2) {		ctlr->dnenabled = 1;		/*		 * 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.		 */		ctlr->upenabled = 1;		x = eepromdata(ctlr, 0x0F);		if(!(x & 0x01))			outl(port+PktStatus, upRxEarlyEnable);		ctlr->nup = Nup;		ctlr->ndn = Ndn;		init905(ctlr);		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));	}	/*	 * Set a base TxStartThresh which will be incremented	 * if any txUnderrun errors occur and ensure no RxEarly	 * interrupts happen.	 */	ctlr->txthreshold = ETHERMAXTU/2;	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);	COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);	iunlock(&ctlr->wlock);	/*	 * Linkage to the generic ethernet driver.	 */	ether->attach = attach;	ether->transmit = transmit;	ether->interrupt = interrupt;	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?