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

📄 3com_driver.c

📁 Details description of Free BSD network source code.Those documents have explained each line of code
💻 C
📖 第 1 页 / 共 3 页
字号:
		register struct ifnet *ifp;		int bpf;	{		register struct ifaddr *ifa;		register struct sockaddr_dl *sdl;		if_attach(ifp);  此例程在if.c 中		ifp->if_type = IFT_ETHER;代表以太网		ifp->if_addrlen = 6;硬件地址长度是6		ifp->if_hdrlen = 14;包的头长度是6+6+2=14,其中2是协议类型		ifp->if_mtu = ETHERMTU;      为1500,多此一举,在前面你可看到,已									经填充了.		ifp->if_resolvemulti = ether_resolvemulti; 以太网解析多播例程指针		if (ifp->if_baudrate == 0)         波特率			ifp->if_baudrate = 10000000;		ifa = ifnet_addrs[ifp->if_index - 1];在ifnet_addrs[]数组中找到本地址指针		KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__));		sdl = (struct sockaddr_dl *)ifa->ifa_addr; ifa->ifa_addr在此时指向的是sockaddr_dl结构.		sdl->sdl_type = IFT_ETHER;		sdl->sdl_alen = ifp->if_addrlen;		bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);把硬件地址拷贝到sdl结构中		if (bpf)             bpf为真,即加入了BSD包过滤			bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));		if (ng_ether_attach_p != NULL)			(*ng_ether_attach_p)(ifp);	}	*/	printf("el%d: 3c501 address %6D\n",idev->id_unit,	  sc->arpcom.ac_enaddr, ":");	dprintf(("el_attach() finished.\n"));	return(1);}/* 该例程重设接口. 在el_watchdog()中调用,因为watchdog不在本驱动程序中支持,所以从不被调用*/static void el_reset(xsc)/*上面的一个函数,重设硬件*/	void *xsc;{	struct el_softc *sc = xsc;	int s;	dprintf(("elreset()\n"));	s = splimp();/*关网络中断*/	el_stop(sc);/*下面的一个函数*/	el_init(sc);/*重新初始化卡*/	splx(s);/*开网络中断*/}/*停止接口,在el_ioctl()和el_reset()中调用*/static void el_stop(xsc)	void *xsc;{	struct el_softc *sc = xsc;	outb(sc->el_base+EL_AC,0);/*用0写辅助命令寄存器*/}/* 初始化接口.  */static void el_init(xsc)	void *xsc;{	struct el_softc *sc = xsc;	struct ifnet *ifp;	int s;	u_short base;	ifp = &sc->arpcom.ac_if;/*定位ifnet结构*/	base = sc->el_base;/*网卡基本I/O地址*/	/* 如果地址不知道,什么也不做. */	if(TAILQ_EMPTY(&ifp->if_addrhead)) /* 在if.c中的if_attach例程									中已经填充,由el_attach调用									ether_attach时再调用if_attach */		return;	s = splimp();/*关网络中断*/	/* 重设板卡. */	dprintf(("Resetting board...\n"));	el_hardreset(sc);/*该函数在上面,重设硬件*/	/* 设置接收寄存器 rx */	dprintf(("Configuring rx...\n"));	if(ifp->if_flags & IFF_PROMISC)    /*是混杂模式?EL_RXC是0X6接收命令寄存器*/		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));	outb(base+EL_RBC,0);/*接收缓冲寄存器清0*/	/* 设置传输寄存器 TX */	dprintf(("Configuring tx...\n"));	outb(base+EL_TXC,0);	/* 开始接收 */	dprintf(("Starting reception...\n"));	outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/*EL_AC_IRQE是IRQ enable(可用) EL_AC_RX为接收寄存器*/	/* 设置一些开始使用的标志 */	ifp->if_flags |= IFF_RUNNING;/*加上正在运行标志*/	ifp->if_flags &= ~IFF_OACTIVE;/*去掉正在传输标志*/	/* 调用输出. */	el_start(ifp);	splx(s);/*开网络中断*/}/* 开始在接口上输出.从队列中得到包并输出他们,在输出中,留出接收用一	部分时间,即打开中断再关闭中断,这样使接口接到的一些数据包不会丢失.  */static voidel_start(struct ifnet *ifp){	struct el_softc *sc;	u_short base;	struct mbuf *m, *m0;	int s, i, len, retries, done;	/*  定位softc结构的指针*/	sc = ifp->if_softc;	base = sc->el_base;/*基地址在输入输出指令时常要用到*/	dprintf(("el_start()...\n"));	s = splimp();/*因为下面涉及到if_flags的操作,所以要关闭网络中断*/	/* 如果输出正在进行,则退出 */	if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)		return;	sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;/*加上输出正在进行传输标志*/	/* 主循环	 */	while(1) {/*唯一出口是准备传输的数据为空,即m0==NULL时*/		/* 从队列中移出下一数据包到m0中,请看头文件if_var.h		#define	IF_DEQUEUE(ifq, m) { \			(m) = (ifq)->ifq_head; \           ifq是一mbuf指针队列,把第一个mbuf指针放到m中			if (m) { \				if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) \重排队列        					(ifq)->ifq_tail = 0; \					(m)->m_nextpkt = 0; \					(ifq)->ifq_len--; \				} \			}						*/		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);/*    &sc->arpcom.ac_if.if_snd指向发送队列,												该宏取出第一个mubf的指针放到m0中,看上面的说明.												这是数据结构的好教材*/		/* 如果发送缓冲区指针为空,即已经发送完,则退出,此是该无穷循环的唯一出口. */		if(m0 == NULL) {			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;/*出去前当然要去掉输出正在进行标志*/			splx(s);			return;		}		/* 关闭接收 */		outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲,即系统总线网卡要用 */		outb(base+EL_RBC,0);/*接收缓冲寄存器清0*/		/* 拷贝mbuf中的数据到softc结构定义的成员el_pktbuf中,缓冲大小是EL_BUFSIZ即2048. */		len = 0;		for(m = m0; m != NULL; m = m->m_next) {   /* m0是一mbuf指针,也是一mbuf链的第一个,此													次要发送的是一mbuf链,不是一单个mbuf*/			if(m->m_len == 0)				continue;			bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);/*m->len是该mbuf链的数据长度,													sc->el_pktbuf是该卡的发送临时缓冲,要发													送的数据在这集中,然后传送到网卡上,太费													时间了,应该直接放置到网卡的存储器中.*/			len += m->m_len;						/*len是这次要发送的总数*/		}		m_freem(m0);                                /*释放该mbuf链*/		len = max(len,ETHER_MIN_LEN);  /*ETHER_MIN_LEN是发送的最小长度*/		/* 如果有BPF,就交给BPF验证 */		if(sc->arpcom.ac_if.if_bpf)			bpf_tap(&sc->arpcom.ac_if, sc->el_pktbuf, len);/*你当然可以在这写一点自己的验证过程*/		/* 传送数据包到板卡 */		dprintf(("el: xfr pkt length=%d...\n",len));		i = EL_BUFSIZ - len;/*EL_BUFSIZ=2048字节*/		outb(base+EL_GPBL,(i & 0xff));       /*告诉发送的长度*/		outb(base+EL_GPBH,((i>>8)&0xff));		outsb(base+EL_BUF,sc->el_pktbuf,len);/*传输数据到板卡*/		/* 开始发送数据包 */		retries=0;/*下面做循环用的,在发不出去时,循环15次*/		done=0;   /*done=1时发送成功了*/		while(!done) {			if(el_xmit(sc,len)) { /* 调用发送例程,其实只要传送base就可以了 */				done = -1;				break;			}			/* 检查输出后的状态,如果你要对watchdog支持,可以在这加上代码,即在5毫秒没发送出去,就调用el_watchdog() */			i = inb(base+EL_TXS);			dprintf(("tx status=0x%x\n",i));			if(!(i & EL_TXS_READY)) {            /* 如果传输状态寄存器不是准备接受新帧就绪 */				dprintf(("el: err txs=%x\n",i));  /*那就是出错了*/				sc->arpcom.ac_if.if_oerrors++;				if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {/*网络数据包碰撞*/					if((!(i & EL_TXC_DCOLL16)) && retries < 15) {/*做循环的目的是为了有错误时可重传15次*/						retries++;						outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲 */					}				}				else					done = 1;			}			else {				sc->arpcom.ac_if.if_opackets++;/*统计用的,说明该卡成功发送一包*/				done = 1;			}		}		if(done == -1)  /* 包没有传输(失败) */			continue;		/* 现在给板卡一个机会接收.注意:在linux中曾经ALEN先生批评此卡好恐怖(他说要进博物馆,哈哈),并说在传输时			会丢失进来的数据包,我查看了LINUX的驱动程序,确实是这样,但在FreeBSD中,给了一个机会,由此可证明他的			关于"丢失包的说法不一定成立",但关于一个缓冲和一次只能发送一数据包的说法确实是真的,还有多播方面也			不支持,我也希望大家最好不要去买这东西,和NE2000,PCI中的RTL8139芯片的网卡一样,性能太差了.*/		(void)inb(base+EL_AS);/*读辅助状态寄存器*/		outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/		splx(s);		/* 这有可能接收到中断(包到达) */		s = splimp();	}}/* 这是真正的传输包,由el_start()调用 */static intel_xmit(struct el_softc *sc,int len){	int gpl;	int i;	gpl = EL_BUFSIZ - len;	dprintf(("el: xmit..."));	outb((sc->el_base)+EL_GPBL,(gpl & 0xff));	outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff));	outb((sc->el_base)+EL_AC,EL_AC_TXFRX);/*真正的传送指令*/	i = 20000;	while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))/*如果传送还在忙,循环20000次等待*/		i--;	if(i == 0) {/*这里有一个bug,大家发现没有,i到了0时也有可能传送成功,解决办法是把(i>0)这条件放到前面*/				/*我稍微讲一下C,在编译C程序时,象while ( (a>b) && (i>0) )时,是这个样子			top:if a>b then					if i>0 then						执行体					endif				endif				goto top		也就是说,当i=0时候,inb((sc->el_base)+EL_AS)这指令还会执行,也有可能这时候传送完成了,而下面有给打出		一个什么"tx not ready"的东东,而且返回失败,有得重新传送一次.		*/		dprintf(("tx not ready\n"));		sc->arpcom.ac_if.if_oerrors++;		return(-1);	}	dprintf(("%d cycles.\n",(20000-i)));	return(0);/*成功*/}/* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用 */static __inline voidelread(struct el_softc *sc,caddr_t buf,int len){	register struct ether_header *eh;	struct mbuf *m;	eh = (struct ether_header *)buf;/*从buf中分出以太网头部*/	/*	 * elget函数是把包放入一mbuf缓冲链中	 */

⌨️ 快捷键说明

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