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

📄 3com_driver.c

📁 Details description of Free BSD network source code.Those documents have explained each line of code
💻 C
📖 第 1 页 / 共 3 页
字号:
	m = elget(buf,len,&sc->arpcom.ac_if);	if(m == 0)/*出错了*/		return;	ether_input(&sc->arpcom.ac_if,eh,m);/*传输给上一层的包括ifnet结构,以太网头部,一mbuf*/}/* 中断例程 */static voidelintr(int unit){	register struct el_softc *sc;	register int base;	int stat, rxstat, len, done;	/* 寻址softc和I/O基地址 */	sc = &el_softc[unit];	base = sc->el_base;	dprintf(("elintr: "));	/* 检查板卡状态 */	stat = inb(base+EL_AS);	if(stat & EL_AS_RXBUSY) {/*接收忙*/		(void)inb(base+EL_RXC);/*读接收命令寄存器*/		outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/		return;	}	done = 0;	while(!done) {		rxstat = inb(base+EL_RXS);		if(rxstat & EL_RXS_STALE) {/*EL_RXS_STALE代表接受状态没有改变*/			(void)inb(base+EL_RXC);/*读接收命令寄存器*/			outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/			return;		}		/* 如果这有一个溢出发生,重新初始化板卡. */		if(!(rxstat & EL_RXS_NOFLOW)) {			dprintf(("overflow.\n"));			el_hardreset(sc);			/* 使板卡回到接收模式 */			if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)				outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));			else				outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));			(void)inb(base+EL_AS);/*读辅助状态寄存器*/			outb(base+EL_RBC,0);/*清除接收缓冲*/			(void)inb(base+EL_RXC);/*读接收命令寄存器*/			outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/			return;		}		/* 到这应该是进来了一数据包 */		len = inb(base+EL_RBL);		len |= inb(base+EL_RBH) << 8;/*包长度的高低位组合为该包的长度*/		dprintf(("receive len=%d rxstat=%x ",len,rxstat));		outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲 */		/* 如果包太短或太长,回到接收模式并返回		 */		if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {/*如果包小于以太网头部的长度或大于最大长度*/			if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)/*为重置硬件准备if_flags*/				outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));			else				outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));			(void)inb(base+EL_AS);/*读辅助状态寄存器*/			outb(base+EL_RBC,0);/*清除接收缓冲*/			(void)inb(base+EL_RXC);/*读接收命令寄存器*/			outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/			return;		}		sc->arpcom.ac_if.if_ipackets++;/*统计使用,说明接收包总数*/		/* 拷贝数据到我们的缓冲 */		outb(base+EL_GPBL,0);		outb(base+EL_GPBH,0);		insb(base+EL_BUF,sc->el_pktbuf,len);/*从端口读一串数据到指定地址()*/		outb(base+EL_RBC,0);		outb(base+EL_AC,EL_AC_RX);		dprintf(("%6D-->",sc->el_pktbuf+6,":"));/*也放置到el_pktbuf中,发送也放在他中,在发送时有一个开中断接数据包的过程													不过那时候el_pktbuf中没有数据,不会相互影响.*/		dprintf(("%6D\n",sc->el_pktbuf,":"));		/* 把数据传递到上一层 */		len -= sizeof(struct ether_header);		elread(sc,(caddr_t)(sc->el_pktbuf),len);		/* 对状态? */		stat = inb(base+EL_AS);		/* 如果忙不为真则继续 */		if(!(stat & EL_AS_RXBUSY))			dprintf(("<rescan> "));		else			done = 1; /*退出循环*/	}	(void)inb(base+EL_RXC);	outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));	return;}/* * 从网卡上下载数据包,len是数据的长度,本地以太网头部被分开 */static struct mbuf *elget(buf, totlen, ifp)/*由elread调用,buf是指向sc->el_pktbuf缓冲区,并且数据已经存在,						totlen是整个数据包长度-sizeof(struct ether_header)即以太网头部的长度*/        caddr_t buf;        int totlen;        struct ifnet *ifp;{        struct mbuf *top, **mp, *m;        int len;        register caddr_t cp;        char *epkt;        buf += sizeof(struct ether_header);/*调用前buf指向...请看下图		   |--------------------------------整个数据----------------------------------------------| 		   |--ether头部14字节----|--------IP或ARP或其他协议头部及数据-----------------------------|		   ^	调用前buf指向这		                         ^						执行之后buf指向这		        因为在向上传递数据的过程是一层一层的剥,在本次要剥掉ether_header即以太网头部		*/        cp = buf;/*放入寄存器中*/        epkt = cp + totlen;/*epkt在计算后指向数据的尾部*/        MGETHDR(m, M_DONTWAIT, MT_DATA);/*得到一标记了头部的mbuf*/		/*MGETHDR宏说明		#define	MGETHDR(m, how, type) do {					\			struct mbuf *_mm;						\			int _mhow = (how);						\			int _mtype = (type);						\			int _ms = splimp();						\屏蔽中断									\			if (mmbfree == NULL)						\mmbfree是内存管理初始化时的全局变量,意思是还有可用的内存块吗?				(void)m_mballoc(1, _mhow);				\没有就分配一个,1代表分配一个MSIZE大小的块,该函数调用kmem_malloc														\核心内存分配函数,返回的可用mbuf指针在mmbfree中			_mm = mmbfree;							\			if (_mm != NULL) {						\				mmbfree = _mm->m_next;					\如果上面的m_mballoc函数执行了,_mm->m_next肯定为NULL				mbtypes[MT_FREE]--;					\				_mm->m_type = _mtype;					\看上下文可知,_mtype是MT_DATA				mbtypes[_mtype]++;					\				_mm->m_next = NULL;					\从这开始是初始化mbuf一些指针				_mm->m_nextpkt = NULL;					\				_mm->m_data = _mm->m_pktdat;				\				_mm->m_flags = M_PKTHDR;				\加入mbuf链首标志,即该链的第一个包,该宏和MGET的不同之处				_mm->m_pkthdr.rcvif = NULL;				\				_mm->m_pkthdr.csum_flags = 0;				\				_mm->m_pkthdr.aux = (struct mbuf *)NULL;		\				(m) = _mm;						\				splx(_ms);						\恢复中断			} else {							\				splx(_ms);						\				_mm = m_retryhdr(_mhow, _mtype);			\再来一次MGETHDR,不过m_retryhdr已经定义为空,防止死循环				if (_mm == NULL && _mhow == M_WAIT)			\还为空					(m) = m_mballoc_wait(MGETHDR_C, _mtype);	\强制用阻塞型				else							\					(m) = _mm;					\			}								\		} while (0)*/        if (m == 0)                return (0);        m->m_pkthdr.rcvif = ifp;/*指向接收该包的网络卡的ifp指针,后面好多协议要用到他*/        m->m_pkthdr.len = totlen;/*已经把以太网头部剥离,数据长度没算他了*/        m->m_len = MHLEN;/*该出是链首,所以该mbuf的长度是MHLEN,而不是MLEN*/		/* 这就是MHLEN		#define MSIZE		256		/*  mbuf的大小 *		#define	MLEN		(MSIZE - sizeof(struct m_hdr))	/* 普通数据区的长度*		#define	MHLEN		(MLEN - sizeof(struct pkthdr))	/* 链首数据区的长度		*/        top = 0;        mp = &top;        while (totlen > 0) {                if (top) {/*如果不是链的第一个*/                        MGET(m, M_DONTWAIT, MT_DATA);/*MGET和MGETHDR差不多,只不过少一个m_flags = M_PKTHDR*/                        if (m == 0) {                                m_freem(top);                                return (0);                        }                        m->m_len = MLEN;/*非链首mbuf的长度为MLEN,这个if(top)就代表不是链首mbuf*/                }/*如果跳过了上面哪个if,那肯定是链的第一个mbuf,并且m已经在循环外就分配好了.*/                len = min(totlen, epkt - cp);/*epkt在计算后指向数据的尾部,cp指向首部*/                if (len >= MINCLSIZE) {/*#define	MINCLSIZE	(MHLEN + 1)  这意味着只要数据大于MHLEN,就要分配一个簇*/                        MCLGET(m, M_DONTWAIT);/*看到宏展开后好恐怖,有空我再说一说*/                        if (m->m_flags & M_EXT)/*在mbuf中注明是扩展型mbuf(即带有簇)*/                                m->m_len = len = min(len, MCLBYTES);/*如果大于2048则先装2048吧,装的语句在下面*/                        else                                len = m->m_len;                } else {                        /*                         * 如果到这了,就意味着要么这个包小于MINCLSIZE,要么是后面一点尾巴且小于MINCLSIZE.                         */                        if (len < m->m_len) {                                if (top == 0 && len + max_linkhdr <= m->m_len)                                        m->m_data += max_linkhdr;                                m->m_len = len;                        } else                                len = m->m_len;                }                bcopy(cp, mtod(m, caddr_t), (unsigned)len);/*第一次数据移动,费时的操作*/                cp += len;                *mp = m;                mp = &m->m_next;/*把mbuf链接起来*/                totlen -= len;                if (cp == epkt)                        cp = buf;        }        return (top);/*返回装填数据的mbuf链首*/}/*总结:在该函数中,所做的事情非常费时,主要是做内存的申请,大批数据的拷贝,如果象NFS传送数据,会出现大量的簇的申请和大量   簇的数据的拷贝,一次循环需要拷贝2048个32位的双字.如果是发给本机的,那还行,如果是本机做为桥转发及防活墙,即数据不上传   到IP层处理,那么可以直接改写mbuf的分配方案,根据不同的网络流量可初始化一定数量的大容量的缓冲链(可以以一个以太网的整   页数来分配,如是100M以太网是1514字节,可分配2048字节,是有一点浪费,但性能可提高,sc->el_pktbuf可变为一队列,用来和其他   网卡的接收队列进行数据交换.这意味着光数据进入就少拷贝一次,性能将大大提高,目前我正在研究中.)*//* * 处理一个IOCTL请求.  */static intel_ioctl(ifp, command, data)	register struct ifnet *ifp;	u_long command;  /*IOCTL的命令*/	caddr_t data;{	int s, error = 0;	s = splimp();    /*先关闭网络中断*/	switch (command) {        case SIOCSIFADDR:	case SIOCGIFADDR:	case SIOCSIFMTU:		error = ether_ioctl(ifp, command, data);		break;	case SIOCSIFFLAGS:		/*		 * 如果接口已经DOWN但FLAG还有RUNNING, 那么先停止它		 */		if (((ifp->if_flags & IFF_UP) == 0) &&		    (ifp->if_flags & IFF_RUNNING)) {			el_stop(ifp->if_softc);			ifp->if_flags &= ~IFF_RUNNING;/*在FALG中去掉IFF_RUNNING标志*/		} else {		/*		 * 如果接口已经DOWN,FLAG没有RUNNING, 只要调用el_init例程		 */			if ((ifp->if_flags & IFF_UP) &&		    	    ((ifp->if_flags & IFF_RUNNING) == 0))				el_init(ifp->if_softc);		}		break;	default:		error = EINVAL;	}	(void) splx(s);	return (error);}/* 一般是数据在规定的时间内没有发出后被调用的程序,目前该驱动程序不支持 */static voidel_watchdog(struct ifnet *ifp){	log(LOG_ERR,"el%d: device timeout\n", ifp->if_unit);	ifp->if_oerrors++;	el_reset(ifp->if_softc);}

⌨️ 快捷键说明

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