⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ethersmc.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
/* * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. */#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 {	IoSize		= 0x10,		/* port pool size */	TxTimeout	= 150,};enum {	/* PCMCIA related */	TupleFunce	= 0x22,	TfNodeId	= 0x04,};enum {	/* bank 0 registers */	Tcr		= 0x0000,	/* transmit control */	Eph		= 0x0002,	/* ethernet protocol handler */	Rcr		= 0x0004,	/* receiver control */	Counter		= 0x0006,	/* statistics counter */	MemInfo		= 0x0008,	MemCfg		= 0x000A,};enum {	/* bank 1 registers */	Config		= 0x0000,	BaseAddr	= 0x0002,	Addr0		= 0x0004,	/* ethernet address */	Addr1		= 0x0006,	Addr2		= 0x0008,	General		= 0x000A,	Control		= 0x000C,};enum {	/* bank 2 registers */	MmuCmd		= 0x0000,	PktNo		= 0x0002,	AllocRes	= 0x0003,	FifoPorts	= 0x0004,	Pointer		= 0x0006,	Data1		= 0x0008,	Interrupt	= 0x000C,	IntrMask	= 0x000D,};enum {	/* bank 3 registers */	Mcast0		= 0x0000,	Mcast2		= 0x0002,	Mcast4		= 0x0004,	Mcast6		= 0x0006,	Revision	= 0x000A,};enum {	BankSelect	= 0x000E	/* bank select register */};enum {	BsrMask		= 0xFF00,	/* mask for chip identification */	BsrId		= 0x3300,};enum {	/* Tcr values */	TcrClear	= 0x0000,	TcrEnable	= 0x0001,	/* enable transmit */	TcrLoop		= 0x0002,	/* enable internal analogue loopback */	TcrForceCol	= 0x0004,	/* force collision on next tx */	TcrPadEn	= 0x0080,	/* pad short packets to 64 bytes */	TcrNoCrc	= 0x0100,	/* do not append CRC */	TcrMonCns	= 0x0400,	/* monitor carrier status */	TcrFduplx	= 0x0800,	TcrStpSqet	= 0x1000,	TcrEphLoop	= 0x2000,	TcrNormal	= TcrEnable,};enum {	/* Eph values */	EphTxOk		= 0x0001,	Eph1Col		= 0x0002,	/* single collision */	EphMCol		= 0x0004,	/* multiple collisions */  	EphTxMcast	= 0x0008,	/* multicast transmit */	Eph16Col	= 0x0010,	/* 16 collisions, tx disabled */	EphSqet		= 0x0020,	/* SQE test failed, tx disabled */	EphTxBcast	= 0x0040,	/* broadcast tx */	EphDefr		= 0x0080,	/* deffered tx */	EphLatCol	= 0x0200,	/* late collision, tx disabled */	EphLostCarr	= 0x0400,	/* lost carrier, tx disabled */	EphExcDefr	= 0x0800,	/* excessive defferals */	EphCntRol	= 0x1000,	/* ECR counter(s) rolled over */	EphRxOvrn	= 0x2000,	/* receiver overrun, packets dropped */	EphLinkOk	= 0x4000,	EphTxUnrn	= 0x8000,	/* tx underrun */};enum {	/* Rcr values */	RcrClear	= 0x0000,	RcrPromisc	= 0x0002,	RcrAllMcast	= 0x0004,	RcrEnable	= 0x0100,	RcrStripCrc	= 0x0200,	RcrSoftReset	= 0x8000,	RcrNormal	= RcrStripCrc | RcrEnable,};enum { /* Counter value masks */	CntColMask	= 0x000F,	/* collisions */	CntMColMask	= 0x00F0,	/* multiple collisions */	CntDtxMask	= 0x0F00,	/* deferred transmits */	CntExDtxMask	= 0xF000,	/* excessively deferred transmits */	CntColShr	= 1,	CntMColShr	= 4,	CntDtxShr	= 8,};enum { /* MemInfo value masks */	MirTotalMask	= 0x00FF,	MirFreeMask	= 0xFF00,};enum {	/* Config values */	CfgIrqSel0	= 0x0002,	CfgIrqSel1	= 0x0004,	CfgDisLink	= 0x0040,	/* disable 10BaseT link test */	Cfg16Bit	= 0x0080,	CfgAuiSelect	= 0x0100,	CfgSetSqlch	= 0x0200,	CfgFullStep	= 0x0400,	CfgNoWait	= 0x1000,	CfgMiiSelect	= 0x8000,};enum {	/* Control values */	CtlStore	= 0x0001,	/* store to EEPROM */	CtlReload	= 0x0002,	/* reload EEPROM into registers */	CtlEeSelect	= 0x0004,	/* select registers for reload/store */	CtlTeEnable	= 0x0020,	/* tx error detection via eph irq */	CtlCrEnable	= 0x0040,	/* counter rollover via eph irq */	CtlLeEnable	= 0x0080,	/* link error detection via eph irq*/	CtlAutoRls	= 0x0800,	/* auto release mode */	CtlPowerDn	= 0x2000,};enum {	/* MmuCmd values */	McBusy		= 0x0001,	McAlloc		= 0x0020,	/* | with number of 256 byte packets - 1 */	McReset		= 0x0040,	McRelease	= 0x0080,	/* dequeue (but not free) current rx packet */	McFreePkt	= 0x00A0,	/* dequeue and free current rx packet */	McEnqueue	= 0x00C0,	/* enqueue the packet for tx */	McTxReset	= 0x00E0,	/* reset transmit queues */};enum { /* AllocRes values */	ArFailed	= 0x80,};	  enum {	/* FifoPorts values */	FpTxEmpty	= 0x0080,	FpRxEmpty	= 0x8000,	FpTxMask	= 0x007F,	FpRxMask	= 0x7F00,};enum {	/* Pointer values */	PtrRead		= 0x2000,	PtrAutoInc	= 0x4000,	PtrRcv		= 0x8000,};enum {	/* Interrupt values */	IntRcv		= 0x0001,	IntTxError	= 0x0002,	IntTxEmpty	= 0x0004,	IntAlloc	= 0x0008,	IntRxOvrn	= 0x0010,	IntEph		= 0x0020,};enum { /* transmit status bits */	TsSuccess	= 0x0001,	Ts16Col		= 0x00A0,	TsLatCol	= 0x0200,	TsLostCar	= 0x0400,};enum { /* receive status bits */	RsMcast		= 0x0001,	RsTooShort	= 0x0400,	RsTooLong	= 0x0800,	RsOddFrame	= 0x1000,	RsBadCrc	= 0x2000,	RsAlgnErr	= 0x8000,	RsError		= RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,};enum {	RxLenMask	= 0x07FF,	/* significant rx len bits */	HdrSize		= 6,		/* packet header length */	PageSize	= 256,		/* page length */};typedef struct Smc91xx Smc91xx;struct Smc91xx {	Lock;	ushort rev;	int attached;	Block *txbp;	ulong txtime;	ulong rovrn;	ulong lcar;	ulong col;	ulong scol;	ulong mcol;	ulong lcol;	ulong dfr;};#define SELECT_BANK(x) outs(port + BankSelect, x)static intreadnodeid(int slot, Ether* ether){	uchar data[Eaddrlen + 1];	int len;	len = sizeof(data);	if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)		return -1;	if (data[0] != Eaddrlen)		return -1;	memmove(ether->ea, &data[1], Eaddrlen);	return 0;}static voidchipreset(Ether* ether){	int port;	int i;	port = ether->port;	/* reset the chip */	SELECT_BANK(0);	outs(port + Rcr, RcrSoftReset);	delay(1);	outs(port + Rcr, RcrClear);	outs(port + Tcr, TcrClear);	SELECT_BANK(1);	outs(port + Control, CtlAutoRls | CtlTeEnable |		CtlCrEnable);	for(i = 0; i < 6; i++) {		outb(port + Addr0 +  i, ether->ea[i]);	}	SELECT_BANK(2);	outs(port + MmuCmd, McReset);}static voidchipenable(Ether* ether){	int port;	port = ether->port;	SELECT_BANK(0);	outs(port + Tcr, TcrNormal);	outs(port + Rcr, RcrNormal);	SELECT_BANK(2);	outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);}static voidattach(Ether *ether){	Smc91xx* ctlr;	ctlr = ether->ctlr;	ilock(ctlr);		if (ctlr->attached) {		iunlock(ctlr);		return;	}	chipenable(ether);	ctlr->attached = 1;	iunlock(ctlr);}static voidtxstart(Ether* ether){	int port;	Smc91xx* ctlr;	Block* bp;	int len, npages;	int pno;	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr;	if (ctlr->txbp) {		bp = ctlr->txbp;		ctlr->txbp = 0;	} else {		bp = qget(ether->oq);		if (bp == 0)			return;		len = BLEN(bp);		npages = (len + HdrSize) / PageSize;		outs(port + MmuCmd, McAlloc | npages);	}	pno = inb(port + AllocRes);	if (pno & ArFailed) {		outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);		ctlr->txbp = bp;		ctlr->txtime = MACHP(0)->ticks;		return;	}	outb(port + PktNo, pno);	outs(port + Pointer, PtrAutoInc);	len = BLEN(bp);	outs(port + Data1, 0);	outb(port + Data1, (len + HdrSize) & 0xFF);	outb(port + Data1, (len + HdrSize) >> 8);	outss(port + Data1, bp->rp, len / 2);	if ((len & 1) == 0) {		outs(port + Data1, 0);	} else {		outb(port + Data1, bp->rp[len - 1]);		outb(port + Data1, 0x20);	/* no info what 0x20 means */	}	outb(port + IntrMask, inb(port + IntrMask) |			IntTxError | IntTxEmpty);	outs(port + MmuCmd, McEnqueue);	freeb(bp);}static voidreceive(Ether* ether){	int port;	Block* bp;	int pktno, status, len;	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	pktno = ins(port + FifoPorts);	if (pktno & FpRxEmpty) {		return;	}	outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);	status = ins(port + Data1);	len = ins(port + Data1) & RxLenMask - HdrSize;		if (status & RsOddFrame)		len++;		if ((status & RsError) || (bp = iallocb(len)) == 0) {		if (status & RsAlgnErr)			ether->frames++;		if (status & (RsTooShort | RsTooLong))			ether->buffs++;		if (status & RsBadCrc)			ether->crcs++;		outs(port + MmuCmd, McRelease);		return;	}	/* packet length is padded to word */	inss(port + Data1, bp->rp, len / 2);	bp->wp = bp->rp + (len & ~1);		if (len & 1) {		*bp->wp = inb(port + Data1);		bp->wp++;	}	  	etheriq(ether, bp, 1);	ether->inpackets++;	outs(port + MmuCmd, McRelease);}static voidtxerror(Ether* ether){	int port;	Smc91xx* ctlr;	int save_pkt;	int pktno, status;	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr;	save_pkt = inb(port + PktNo);	pktno = ins(port + FifoPorts) & FpTxMask;	outb(port + PktNo, pktno);	outs(port + Pointer, PtrAutoInc | PtrRead);	status = ins(port + Data1);		if (status & TsLostCar)		ctlr->lcar++;	if (status & TsLatCol)		ctlr->lcol++;	if (status & Ts16Col)		ctlr->scol++;	ether->oerrs++;		SELECT_BANK(0);	outs(port + Tcr, ins(port + Tcr) | TcrEnable);		SELECT_BANK(2);	outs(port + MmuCmd, McFreePkt);	outb(port + PktNo, save_pkt);}static voideph_irq(Ether* ether){	int port;	Smc91xx* ctlr;	ushort status;	int n;	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr;	SELECT_BANK(0);	status = ins(port + Eph);	if (status & EphCntRol) {		/* read the counter register even if we don't need it */		/* otherwise we will keep getting this interrupt */		n = ins(port + Counter);		ctlr->col += (n & CntColMask) >> CntColShr;		ctlr->mcol += (n & CntMColMask) >> CntMColShr;		ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;	}	/* if there was a transmit error, Tcr is disabled */	outs(port + Tcr, ins(port + Tcr) | TcrEnable);	/* clear a link error interrupt */	SELECT_BANK(1);	outs(port + Control, CtlAutoRls);	outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);	SELECT_BANK(2);}static voidtransmit(Ether* ether){	Smc91xx* ctlr;	int port, n;	ctlr = ether->ctlr;	port = ether->port;	ilock(ctlr);	if (ctlr->txbp) {		n = TK2MS(MACHP(0)->ticks - ctlr->txtime);		if (n > TxTimeout) {			chipreset(ether);			chipenable(ether);			freeb(ctlr->txbp);			ctlr->txbp = 0;		}		iunlock(ctlr);		return;	}	SELECT_BANK(2);	txstart(ether);	iunlock(ctlr);}static voidinterrupt(Ureg*, void *arg){	int port;	Smc91xx* ctlr;	Ether* ether;	int save_bank;	int save_pointer;	int mask, status;	ether = arg;	port = ether->port;	ctlr = ether->ctlr;		ilock(ctlr);	save_bank = ins(port + BankSelect);	SELECT_BANK(2);	save_pointer = ins(port + Pointer);		mask = inb(port + IntrMask);	outb(port + IntrMask, 0);	while ((status = inb(port + Interrupt) & mask) != 0) {		if (status & IntRcv) {			receive(ether);		}		if (status & IntTxError) {			txerror(ether);		}		if (status & IntTxEmpty) {			outb(port + Interrupt, IntTxEmpty);			outb(port + IntrMask, mask & ~IntTxEmpty);			txstart(ether);			mask = inb(port + IntrMask);		}		if (status & IntAlloc) {			outb(port + IntrMask, mask & ~IntAlloc);			txstart(ether);;			mask = inb(port + IntrMask);		}		if (status & IntRxOvrn) {			ctlr->rovrn++;			ether->misses++;			outb(port + Interrupt,IntRxOvrn);		}		if (status & IntEph)			eph_irq(ether);	}		outb(port + IntrMask, mask);	outs(port + Pointer, save_pointer);	outs(port + BankSelect, save_bank);	iunlock(ctlr);}static voidpromiscuous(void* arg, int on){	int port;	Smc91xx *ctlr;	Ether* ether;	ushort x;	ether = arg;	port = ether->port;	ctlr = ether->ctlr;	ilock(ctlr);	SELECT_BANK(0);	x = ins(port + Rcr);	if (on)		x |= RcrPromisc;	else		x &= ~RcrPromisc;		outs(port + Rcr, x);	iunlock(ctlr);}static voidmulticast(void* arg, uchar *addr, int on){	int port;	Smc91xx*ctlr;	Ether *ether;	ushort x;		USED(addr, on);	ether = arg;	port = ether->port;	ctlr = ether->ctlr;	ilock(ctlr);		SELECT_BANK(0);	x = ins(port + Rcr);		if (ether->nmaddr)		x |= RcrAllMcast;	else		x &= ~RcrAllMcast;		outs(port + Rcr, x);	iunlock(ctlr);}static longifstat(Ether* ether, void* a, long n, ulong offset){	static char *chiprev[] = {		[3] 	"92",		[5]	"95",		[7]	"100",		[8]	"100-FD",		[9]	"110",	};	Smc91xx* ctlr;	char* p;	int r, len;	char* s;		if (n == 0)		return 0;	ctlr = ether->ctlr;	p = malloc(READSTR);	s = 0;	if (ctlr->rev > 0) {		r = ctlr->rev >> 4;		if (r < nelem(chiprev))			s = chiprev[r];		if (r == 4) {			if ((ctlr->rev & 0x0F) >= 6)				s = "96";			else				s = "94";		}	}	len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");	len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);	len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);	len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);	len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);	len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);	len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);	len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);	USED(len);	n = readstr(offset, a, n, p);	free(p);		return n;}static intreset(Ether* ether){	int port;	int i, x;	char* type;	Smc91xx* ctlr;	int slot;	uchar ea[Eaddrlen];	if (ether->irq == 0)		ether->irq = 9;	if (ether->port == 0)		ether->port = 0x100;	type = "8020";	for(i = 0; i < ether->nopt; i++) {		if (cistrncmp(ether->opt[i], "id=", 3))			continue;		type = &ether->opt[i][3];		break;	}	if ((slot = pcmspecial(type, ether)) < 0)		return -1;	if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {		pcmspecialclose(slot);		return -1;	}	ether->ctlr = malloc(sizeof(Smc91xx));	ctlr = ether->ctlr;	if (ctlr == 0) {		iofree(ether->port);		pcmspecialclose(slot);		return -1;	}	ilock(ctlr);	ctlr->rev = 0;	ctlr->txbp = nil;	ctlr->attached = 0;	ctlr->rovrn = 0;	ctlr->lcar = 0;	ctlr->col = 0;	ctlr->scol = 0;	ctlr->mcol = 0;	ctlr->lcol = 0;	ctlr->dfr = 0;	port = ether->port;	SELECT_BANK(1);	if ((ins(port + BankSelect) & BsrMask) != BsrId) {		outs(port + Control, 0);	/* try powering up the chip */		delay(55);	}	outs(port + Config, ins(port + Config) | Cfg16Bit);	x = ins(port + BaseAddr);	if (((ins(port + BankSelect) & BsrMask) != BsrId) ||		((x >> 8) == (x & 0xFF))) {		iunlock(ctlr);		iofree(port);		pcmspecialclose(slot);		return -1;	}	SELECT_BANK(3);	ctlr->rev = ins(port + Revision) & 0xFF;	memset(ea, 0, Eaddrlen);	if (memcmp(ea, ether->ea, Eaddrlen) == 0) {		if (readnodeid(slot, ether) < 0) {			print("Smc91cXX: cannot find ethernet address\n");			iunlock(ctlr);			iofree(port);			pcmspecialclose(slot);			return -1;		}	}	chipreset(ether);	ether->attach = attach;	ether->transmit = transmit;	ether->interrupt = interrupt;	ether->ifstat = ifstat;	ether->promiscuous = promiscuous;	ether->multicast = multicast;	ether->arg = ether;	iunlock(ctlr);	return 0;}voidethersmclink(void){	addethercard("smc91cXX", reset);}

⌨️ 快捷键说明

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