etherelnk3.c

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

C
2,135
字号
	if(on)		filter |= receiveAllFrames;	COMMAND(port, SetRxFilter, filter);}static voidmulticast(void* arg, uchar *addr, int on){	int filter, port;	Ether *ether;	USED(addr, on);	ether = (Ether*)arg;	port = ether->port;	filter = receiveBroadcast|receiveIndividual;	if(ether->nmaddr)		filter |= receiveMulticast;	if(ether->prom)		filter |= receiveAllFrames;	COMMAND(port, SetRxFilter, filter);}/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */static voidintrackcb(ulong *cbfn){	cbfn[1] = 0x8000;}static voidattach(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;	if(ctlr->busmaster == 1)		x &= ~(rxEarly|rxComplete);	else{		if(ctlr->dnenabled)			x &= ~transferInt;		if(ctlr->upenabled)			x &= ~(rxEarly|rxComplete);	}	COMMAND(port, SetIndicationEnable, x);	COMMAND(port, SetInterruptEnable, x);	COMMAND(port, RxEnable, 0);	COMMAND(port, TxEnable, 0);	/*	 * If this is a CardBus card, acknowledge any interrupts.	 */	if(ctlr->cbfn != nil)		intrackcb(ctlr->cbfn);			/*	 * Prime the busmaster channel for receiving directly into a	 * receive packet buffer if necessary.	 */	if(ctlr->busmaster == 1)		startdma(ether, PADDR(ctlr->rbp->rp));	else{		if(ctlr->upenabled)			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));	}	ctlr->attached = 1;	iunlock(&ctlr->wlock);}static voidstatistics(Ether* ether){	int port, i, 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);	for(i = 0; i < UpperFramesOk; i++)		ctlr->stats[i] += inb(port+i) & 0xFF;	u = inb(port+UpperFramesOk) & 0xFF;	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;	switch(ctlr->xcvr){	case xcvrMii:	case xcvr100BaseTX:	case xcvr100BaseFX:		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;	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.	 */	for(;;){		if(ctlr->txbp){			bp = ctlr->txbp;			ctlr->txbp = 0;		}		else{			bp = qget(ether->oq);			if(bp == nil)				break;		}		len = ROUNDUP(BLEN(bp), 4);		if(len+4 <= ins(port+TxFree)){			outl(port+Fifo, BLEN(bp));			outsl(port+Fifo, bp->rp, len/4);			freeb(bp);			ether->outpackets++;		}		else{			ctlr->txbp = bp;			if(ctlr->txbusy == 0){				ctlr->txbusy = 1;				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);			}			break;		}	}}static voidtxstart905(Ether* ether){	Ctlr *ctlr;	int port, stalled, timeo;	Block *bp;	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->bp){			freeb(pd->bp);			pd->bp = nil;		}		ctlr->dnq--;		pd = pd->next;	}	ctlr->dntail = pd;	stalled = 0;	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(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;	Block *bp;	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->overflows++;			if(pd->control & (upOversizedFrame|upRuntFrame))				ether->buffs++;			if(pd->control & upAlignmentError)				ether->frames++;			if(pd->control & upCRCError)				ether->crcs++;		}		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);			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;	Block *bp;	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:					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);				if(rxerror & rxOverrun)					ether->overflows++;				if(rxerror & (oversizedFrame|runtFrame))					ether->buffs++;				if(rxerror & alignmentError)					ether->frames++;				if(rxerror & crcError)					ether->crcs++;			}		}		/*		 * 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 = rbpalloc(iallocb)) == 0){			COMMAND(port, RxDiscard, 0);			while(STATUS(port) & commandInProgress)				;			if(ctlr->busmaster == 1)				startdma(ether, PADDR(ctlr->rbp->rp));			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);			ctlr->rbp->wp = ctlr->rbp->rp + len;			insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));		}		COMMAND(port, RxDiscard, 0);		while(STATUS(port) & commandInProgress)			;		if(ctlr->busmaster == 1)			ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));		etheriq(ether, ctlr->rbp, 1);		ctlr->rbp = bp;	}}static intejectable(int did){	switch (did) {	case 0x5157:		return 1;	default:		return 0;	}}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))){		ctlr->bogusinterrupts++;		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);				if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {				print("#l%d: Card ejected?\n", ether->ctlrno);				iunlock(&ctlr->wlock);				return;			}			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);

⌨️ 快捷键说明

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