etherelnk3.c

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

C
1,849
字号
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);		STATUS(port);		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);		break;	}	COMMAND(port, SelectRegisterWindow, w);}static voidtxstart(Ether* ether){	int port, len;	Ctlr *ctlr;	Msgbuf *mb;	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.	 */	for(;;){		if(ctlr->txmb != nil){			mb = ctlr->txmb;			ctlr->txmb = nil;		}		else{			mb = etheroq(ether);			if(mb == nil)				break;		}		len = ROUNDUP(mb->count, 4);		if(len+4 <= ins(port+TxFree)){			outl(port+Fifo, mb->count);			outsl(port+Fifo, mb->data, len/4);			mbfree(mb);		}		else{			ctlr->txmb = mb;			if(ctlr->txbusy == 0){				ctlr->txbusy = 1;				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);			}			break;		}	}}static voidtxstart905(Ether* ether){	Ctlr *ctlr;	int port, stalled, timeo;	Msgbuf *mb;	Pd *pd;	ctlr = ether->ctlr;	port = ether->port;	/*	 * Free any completed packets.	 */	pd = ctlr->dntail;	while(ctlr->dnq){		if(PADDR(&pd->np) == inl(port+DnListPtr))			break;		if(pd->mb != nil){			mbfree(pd->mb);			pd->mb = nil;		}		ctlr->dnq--;		pd = pd->next;	}	ctlr->dntail = pd;	stalled = 0;	while(ctlr->dnq < (ctlr->ndn-1)){		mb = etheroq(ether);		if(mb == nil)			break;		pd = ctlr->dnhead->next;		pd->np = 0;		pd->control = dnIndicate|mb->count;		pd->addr = PADDR(mb->data);		pd->len = updnLastFrag|mb->count;		pd->mb = mb;		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;		}		coherence();		ctlr->dnhead->np = PADDR(&pd->np);		ctlr->dnhead->control &= ~dnIndicate;		ctlr->dnhead = pd;		if(ctlr->dnq == 0)			ctlr->dntail = pd;		ctlr->dnq++;		ctlr->dnqueued++;	}	if(ctlr->dnq > ctlr->dnqmax)		ctlr->dnqmax = ctlr->dnq;	/*	 * If the adapter is not currently processing anything	 * and there is something on the queue, start it processing.	 */	if(inl(port+DnListPtr) == 0 && ctlr->dnq)		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));	if(stalled)		COMMAND(port, Stall, dnUnStall);}static voidtransmit(Ether* ether){	Ctlr *ctlr;	int port, w;	port = ether->port;	ctlr = ether->ctlr;	ilock(&ctlr->wlock);	if(ctlr->dnenabled)		txstart905(ether);	else{		w = (STATUS(port)>>13) & 0x07;		COMMAND(port, SelectRegisterWindow, Wop);		txstart(ether);		COMMAND(port, SelectRegisterWindow, w);	}	iunlock(&ctlr->wlock);}static voidreceive905(Ether* ether){	Ctlr *ctlr;	int len, port, q;	Pd *pd;	Msgbuf *mb;	ctlr = ether->ctlr;	port = ether->port;	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)				ether->ifc.rcverr++;			else if(pd->control & (upOversizedFrame|upRuntFrame))				ether->ifc.rcverr++;			else if(pd->control & upAlignmentError)				ether->ifc.rcverr++;			if(pd->control & upCRCError)				ether->ifc.sumerr++;		}		else if(mb = mballoc(sizeof(Enpkt)+4, 0, Mbeth1)){			len = pd->control & rxBytes;			pd->mb->count = len;			etheriq(ether, pd->mb);			pd->mb = mb;			pd->addr = PADDR(mb->data);			coherence();		}		pd->control = 0;		COMMAND(port, Stall, upUnStall);		q++;	}	ctlr->uphead = pd;	ctlr->upqueued += q;	if(q > ctlr->upqmax)		ctlr->upqmax = q;}static voidreceive(Ether* ether){	int len, port, rxerror, rxstatus;	Ctlr *ctlr;	Msgbuf *mb, *rmb;	port = ether->port;	ctlr = ether->ctlr;	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){		if(ctlr->busmaster == 1 && (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){				switch(rxstatus & rxError9){				case rxOverrun9:				case oversizedFrame9:				case runtFrame9:				case alignmentError9:					ether->ifc.rcverr++;					break;				case crcError9:					ether->ifc.sumerr++;					break;				}			}			else{				rxerror = inb(port+RxError);				if(rxerror & (alignmentError|oversizedFrame|runtFrame|rxOverrun))					ether->ifc.rcverr++;				if(rxerror & crcError)					ether->ifc.sumerr++;			}		}		/*		 * If there was an error or a new receive buffer can't be		 * allocated, discard the packet and go on to the next.		 */		rmb = ctlr->rmb;		if((rxstatus & rxError) || (mb = allocrmb()) == nil){			COMMAND(port, RxDiscard, 0);			while(STATUS(port) & commandInProgress)				;			if(ctlr->busmaster == 1)				startdma(ether, PADDR(rmb->data));			continue;		}		/*		 * 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 || ctlr->busmaster == 2){			len = (rxstatus & rxBytes9);			rmb->count = len;			insl(port+Fifo, rmb->data, HOWMANY(len, 4));		}		COMMAND(port, RxDiscard, 0);		while(STATUS(port) & commandInProgress)			;		if(ctlr->busmaster == 1)			rmb->count = startdma(ether, PADDR(mb->data))-rmb->data;		etheriq(ether, rmb);		ctlr->rmb = mb;	}}static voidinterrupt(Ureg*, void* arg){	Ether *ether;	int port, status, s, txstatus, w, x;	Ctlr *ctlr;	ether = arg;	port = ether->port;	ctlr = ether->ctlr;	ilock(&ctlr->wlock);	status = STATUS(port);	if(!(status & (interruptMask|interruptLatch))){		iunlock(&ctlr->wlock);		return;	}	w = (status>>13) & 0x07;	COMMAND(port, SelectRegisterWindow, Wop);	ctlr->interrupts++;	if(ctlr->busmaster == 2)		ctlr->timer[0] += inb(port+Timer905) & 0xFF;	else		ctlr->timer[0] += inb(port+Timer) & 0xFF;	do{		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("#l%d: status 0x%ux, diag 0x%ux\n",			    ether->ctlrno, status, x);			if(x & txOverrun){				if(ctlr->busmaster == 0)					COMMAND(port, TxReset, 0);				else					COMMAND(port, TxReset, (updnReset|dmaReset));				COMMAND(port, TxEnable, 0);			}			if(x & rxUnderrun){				/*				 * This shouldn't happen...				 * Reset the receiver and restore the filter and RxEarly				 * threshold before re-enabling.				 * Need to restart any busmastering?				 */				COMMAND(port, SelectRegisterWindow, Wstate);				s = (port+RxFilter) & 0x000F;				COMMAND(port, SelectRegisterWindow, Wop);				COMMAND(port, RxReset, 0);				while(STATUS(port) & commandInProgress)					;				COMMAND(port, SetRxFilter, s);				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);				COMMAND(port, RxEnable, 0);			}			status &= ~hostError;		}		if(status & (transferInt|rxComplete)){			receive(ether);			status &= ~(transferInt|rxComplete);		}		if(status & (upComplete)){			COMMAND(port, AcknowledgeInterrupt, upComplete);			receive905(ether);			status &= ~upComplete;			ctlr->upinterrupts++;		}		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, taking care not to reset the dma logic			 * as a busmaster receive may be in progress.			 * For all conditions enable the transmitter.			 */			if(ctlr->busmaster == 2)				txstatus = port+TxStatus905;			else				txstatus = port+TxStatus;			s = 0;			do{				if(x = inb(txstatus))					outb(txstatus, 0);				s |= x;			}while(STATUS(port) & txComplete);			if(s & txUnderrun){				if(ctlr->dnenabled){					while(inl(port+PktStatus) & dnInProg)						;				}				COMMAND(port, SelectRegisterWindow, Wdiagnostic);				while(ins(port+MediaStatus) & txInProg)					;				COMMAND(port, SelectRegisterWindow, Wop);				if(ctlr->txthreshold < ETHERMAXTU)					ctlr->txthreshold += ETHERMINTU;			}			/*			 * 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.			 * On busy or badly configured networks maxCollisions can			 * happen frequently enough for messages to be annoying so			 * keep quiet about them by popular request.			 */			if(s & (txJabber|txUnderrun|maxCollisions)){				if(ctlr->busmaster == 0)					COMMAND(port, TxReset, 0);				else					COMMAND(port, TxReset, (updnReset|dmaReset));				while(STATUS(port) & commandInProgress)					;				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);				if(ctlr->busmaster == 2)					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));				if(ctlr->dnenabled)					status |= dnComplete;			}			if(s & ~(txStatusComplete|maxCollisions))				print("#l%d: txstatus 0x%ux, threshold %d\n",			    		ether->ctlrno, s, ctlr->txthreshold);			COMMAND(port, TxEnable, 0);			ether->ifc.txerr++;			status &= ~txComplete;			status |= txAvailable;		}		if(status & txAvailable){			COMMAND(port, AcknowledgeInterrupt, txAvailable);			ctlr->txbusy = 0;			txstart(ether);			status &= ~txAvailable;		}		if(status & dnComplete){			COMMAND(port, AcknowledgeInterrupt, dnComplete);			txstart905(ether);			status &= ~dnComplete;			ctlr->dninterrupts++;		}		if(status & updateStats){			statistics(ether);			status &= ~updateStats;		}		/*		 * Currently, this shouldn't happen.		 */		if(status & rxEarly){			COMMAND(port, AcknowledgeInterrupt, rxEarly);			status &= ~rxEarly;		}		/*		 * Panic if there are any interrupts not dealt with.		 */		if(status & interruptMask)			panic("#l%d: interrupt mask 0x%ux\n", ether->ctlrno, status);		COMMAND(port, AcknowledgeInterrupt, interruptLatch);	}while((status = STATUS(port)) & (interruptMask|interruptLatch));	if(ctlr->busmaster == 2)		ctlr->timer[1] += inb(port+Timer905) & 0xFF;	else		ctlr->timer[1] += inb(port+Timer) & 0xFF;	COMMAND(port, SelectRegisterWindow, w);	iunlock(&ctlr->wlock);}static voidtxrxreset(int port){	COMMAND(port, TxReset, 0);	while(STATUS(port) & commandInProgress)		;	COMMAND(port, RxReset, 0);	while(STATUS(port) & commandInProgress)		;}static voidtcmadapter(int port, int irq, int tbdf){	Ctlr *ctlr;	ctlr = ialloc(sizeof(Ctlr), 0);	ctlr->port = port;	ctlr->irq = irq;	ctlr->tbdf = tbdf;	if(ctlrhead != nil)		ctlrtail->next = ctlr;	else		ctlrhead = ctlr;	ctlrtail = ctlr;}/* * 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 voididseq(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 a global reset is done here on all adapters it will confuse	 * any ISA cards configured for EISA mode.	 */	if(untag == 0){		outb(IDport, 0xD0);		untag = 1;	}}static ulongactivate(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.	 *    A delay seems necessary 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 voidtcm509isa(void){	int irq, port;	/*	 * Attempt to activate all adapters. 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

⌨️ 快捷键说明

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