etherelnk3.c

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

C
1,932
字号
		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;	RingBuf *tb;	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(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){		len = ROUNDUP(tb->len, 4);		if(len+4 <= ins(port+TxFree)){			outl(port+Fifo, tb->len);			outsl(port+Fifo, tb->pkt, len/4);			tb->owner = Host;			ether->ti = NEXT(ether->ti, ether->ntb);		}		else{			if(ctlr->txbusy == 0){				ctlr->txbusy = 1;				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);			}			break;		}	}}static voidtxstart905(Ether* ether){	Ctlr *ctlr;	int port, stalled, timeo;	RingBuf *tb;	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;		ctlr->dnq--;		pd = pd->next;	}	ctlr->dntail = pd;	stalled = 0;	while(ctlr->dnq < (ctlr->ndn-1)){		tb = &ether->tb[ether->ti];		if(tb->owner != Interface)			break;		pd = ctlr->dnhead->next;		pd->np = 0;		pd->control = dnIndicate|tb->len;		memmove(pd->vaddr, tb->pkt, tb->len);		pd->len = updnLastFrag|tb->len;		tb->owner = Host;		ether->ti = NEXT(ether->ti, ether->ntb);		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;	RingBuf *rb;	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)){			rb = &ether->rb[ether->ri];			if (rb->owner == Interface) {				len = pd->control & rxBytes;				rb->len = len;				memmove(rb->pkt, pd->vaddr, len);				rb->owner = Host;				ether->ri = NEXT(ether->ri, ether->nrb);			}		}		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, rxstatus;	RingBuf *rb;	Ctlr *ctlr;	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) == 0){			/*			 * Packet received. Read it into the next free			 * ring buffer, if any. Must read len bytes padded			 * to a doubleword, can be picked out 32-bits at			 * a time. The CRC is already stripped off.			 */			rb = &ether->rb[ether->ri];			if(rb->owner == Interface){				len = (rxstatus & rxBytes9);				rb->len = len;				insl(port+Fifo, rb->pkt, HOWMANY(len, 4));				rb->owner = Host;				ether->ri = NEXT(ether->ri, ether->nrb);			}elseif(debug) print("toss...");		}elseif(debug) print("error...");		/*		 * All done, discard the packet.		 */		COMMAND(port, RxDiscard, 0);		while(STATUS(port) & commandInProgress)			;	}}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);			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);		if(ctlr->cbfn != nil)			intrackcb(ctlr->cbfn);	}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){	while(STATUS(port) & commandInProgress)		;	COMMAND(port, RxReset, 0);	while(STATUS(port) & commandInProgress)		;}static Ctlr*tcmadapter(int port, int irq, Pcidev* pcidev){	Ctlr *ctlr;	ctlr = malloc(sizeof(Ctlr));	ctlr->port = port;	ctlr->irq = irq;	ctlr->pcidev = pcidev;	ctlr->eepromcmd = EepromReadRegister;	if(ctlrhead != nil)		ctlrtail->next = ctlr;	else		ctlrhead = ctlr;	ctlrtail = ctlr;	return 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		 *    (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);		txrxreset(port);		COMMAND(port, AcknowledgeInterrupt, 0xFF);		irq = (ins(port+ResourceConfig)>>12) & 0x0F;		tcmadapter(port, irq, nil);	}}static voidtcm5XXeisa(void){	ushort x;	int irq, port, slot;	/*	 * Check if this is an EISA machine.	 * If not, nothing to do.	 */	if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))		return;	/*	 * Continue through the EISA slots looking for a match on both	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.	 * If an adapter is found, select window 0, enable it and clear	 * out any lingering status and interrupts.	 */	for(slot = 1; slot < MaxEISA; slot++){		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);		txrxreset(port);		COMMAND(port, AcknowledgeInterrupt, 0xFF);		irq = (ins(port+ResourceConfig)>>12) & 0x0F;		tcmadapter(port, irq, nil);	}}static voidtcm59Xpci(void){	Pcidev *p;	Ctlr *ctlr;	int irq, port;	p = nil;	while(p = pcimatch(p, 0x10B7, 0)){		if(p->ccrb != 0x02 || p->ccru != 0)			continue;		/*		 * Not prepared to deal with memory-mapped		 * devices yet.		 */		if(!(p->mem[0].bar & 0x01))			continue;		port = p->mem[0].bar & ~0x01;		irq = p->intl;

⌨️ 快捷键说明

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