分享
 
 
 

freebsd网卡驱动程序详解 (2)

王朝system·作者佚名  2006-11-23
窄屏简体版  字體: |||超大  

/* 开始在接口上输出.从队列中得到包并输出他们,在输出中,留出接收用一

部分时间,即打开中断再关闭中断,这样使接口接到的一些数据包不会丢失.

*/

static void

el_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 int

el_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 void

elread(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缓冲链中

*/

m = elget(buf,len,&sc->arpcom.ac_if);

if(m == 0)/*出错了*/

return;

ether_input(&sc->arpcom.ac_if,eh,m);/*传输给上一层的包括ifnet结构,以太网头部,一mbuf*/

}

/* 中断例程 */

static void

elintr(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_mballo

[1] [2] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有