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

📄 网卡驱动程序详解.doc

📁 网卡驱动程序详解
💻 DOC
📖 第 1 页 / 共 4 页
字号:
    el_hardreset(sc);/*该函数在上面,重设硬件*/ <br>
    <br>
    /* 设置接收寄存器 rx */ <br>
    dprintf((&quot;Configuring rx...\n&quot;)); <br>
    if(ifp-&gt;if_flags &amp; IFF_PROMISC) /*是混杂模式?EL_RXC是0X6接收命令寄存器*/ <br>
    outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
    <br>
    else <br>
    outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
    <br>
    outb(base+EL_RBC,0);/*接收缓冲寄存器清0*/ <br>
    <br>
    /* 设置传输寄存器 TX */ <br>
    dprintf((&quot;Configuring tx...\n&quot;)); <br>
    outb(base+EL_TXC,0); <br>
    <br>
    /* 开始接收 */ <br>
    dprintf((&quot;Starting reception...\n&quot;)); <br>
    outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/*EL_AC_IRQE是IRQ enable(可用) EL_AC_RX为接收寄存器*/
    <br>
    <br>
    /* 设置一些开始使用的标志 */ <br>
    ifp-&gt;if_flags |= IFF_RUNNING;/*加上正在运行标志*/ <br>
    ifp-&gt;if_flags &amp;= ~IFF_OACTIVE;/*去掉正在传输标志*/ <br>
    <br>
    /* 调用输出. */ <br>
    el_start(ifp); <br>
    <br>
    splx(s);/*开网络中断*/ <br>
    } <br>
    <br>
    /* 开始在接口上输出.从队列中得到包并输出他们,在输出中,留出接收用一 <br>
    部分时间,即打开中断再关闭中断,这样使接口接到的一些数据包不会丢失. <br>
    <br>
    */ <br>
    static void <br>
    el_start(struct ifnet *ifp) <br>
    { <br>
    struct el_softc *sc; <br>
    u_short base; <br>
    struct mbuf *m, *m0; <br>
    int s, i, len, retries, done; <br>
    <br>
    /* 定位softc结构的指针*/ <br>
    sc = ifp-&gt;if_softc; <br>
    base = sc-&gt;el_base;/*基地址在输入输出指令时常要用到*/ <br>
    <br>
    dprintf((&quot;el_start()...\n&quot;)); <br>
    s = splimp();/*因为下面涉及到if_flags的操作,所以要关闭网络中断*/ <br>
    <br>
    /* 如果输出正在进行,则退出 */ <br>
    if(sc-&gt;arpcom.ac_if.if_flags &amp; IFF_OACTIVE) <br>
    return; <br>
    sc-&gt;arpcom.ac_if.if_flags |= IFF_OACTIVE;/*加上输出正在进行传输标志*/ <br>
    <br>
    /* 主循环 <br>
    */ <br>
    while(1) {/*唯一出口是准备传输的数据为空,即m0==NULL时*/ <br>
    /* 从队列中移出下一数据包到m0中,请看头文件if_var.h <br>
    #define IF_DEQUEUE(ifq, m) { \ <br>
    (m) = (ifq)-&gt;ifq_head; \ ifq是一mbuf指针队列,把第一个mbuf指针放到m中 <br>
    if (m) { \ <br>
    if (((ifq)-&gt;ifq_head = (m)-&gt;m_nextpkt) == 0) \重排队列 <br>
    (ifq)-&gt;ifq_tail = 0; \ <br>
    (m)-&gt;m_nextpkt = 0; \ <br>
    (ifq)-&gt;ifq_len--; \ <br>
    } \ <br>
    } <br>
    <br>
    <br>
    */ <br>
    IF_DEQUEUE(&amp;sc-&gt;arpcom.ac_if.if_snd,m0);/*
    &amp;sc-&gt;arpcom.ac_if.if_snd指向发送队列, <br>
    该宏取出第一个mubf的指针放到m0中,看上面的说明. <br>
    这是数据结构的好教材*/ <br>
    <br>
    /* 如果发送缓冲区指针为空,即已经发送完,则退出,此是该无穷循环的唯一出口. */ <br>
    if(m0 == NULL) { <br>
    sc-&gt;arpcom.ac_if.if_flags &amp;= ~IFF_OACTIVE;/*出去前当然要去掉输出正在进行标志*/ <br>
    splx(s); <br>
    return; <br>
    } <br>
    <br>
    /* 关闭接收 */ <br>
    outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲,即系统总线网卡要用 */ <br>
    outb(base+EL_RBC,0);/*接收缓冲寄存器清0*/ <br>
    <br>
    /* 拷贝mbuf中的数据到softc结构定义的成员el_pktbuf中,缓冲大小是EL_BUFSIZ即2048. */ <br>
    len = 0; <br>
    for(m = m0; m != NULL; m = m-&gt;m_next) { /* m0是一mbuf指针,也是一mbuf链的第一个,此 <br>
    次要发送的是一mbuf链,不是一单个mbuf*/ <br>
    if(m-&gt;m_len == 0) <br>
    continue; <br>
    bcopy(mtod(m,caddr_t),sc-&gt;el_pktbuf+len,m-&gt;m_len);/*m-&gt;len是该mbuf链的数据长度,
    <br>
    sc-&gt;el_pktbuf是该卡的发送临时缓冲,要发 <br>
    送的数据在这集中,然后传送到网卡上,太费 <br>
    时间了,应该直接放置到网卡的存储器中.*/ <br>
    len += m-&gt;m_len; /*len是这次要发送的总数*/ <br>
    } <br>
    m_freem(m0); /*释放该mbuf链*/ <br>
    <br>
    len = max(len,ETHER_MIN_LEN); /*ETHER_MIN_LEN是发送的最小长度*/ <br>
    <br>
    /* 如果有BPF,就交给BPF验证 */ <br>
    if(sc-&gt;arpcom.ac_if.if_bpf) <br>
    bpf_tap(&amp;sc-&gt;arpcom.ac_if, sc-&gt;el_pktbuf, len);/*你当然可以在这写一点自己的验证过程*/
    <br>
    <br>
    /* 传送数据包到板卡 */ <br>
    dprintf((&quot;el: xfr pkt length=%d...\n&quot;,len)); <br>
    i = EL_BUFSIZ - len;/*EL_BUFSIZ=2048字节*/ <br>
    outb(base+EL_GPBL,(i &amp; 0xff)); /*告诉发送的长度*/ <br>
    outb(base+EL_GPBH,((i&gt;&gt;&amp;0xff)); <br>
    outsb(base+EL_BUF,sc-&gt;el_pktbuf,len);/*传输数据到板卡*/ <br>
    <br>
    /* 开始发送数据包 */ <br>
    retries=0;/*下面做循环用的,在发不出去时,循环15次*/ <br>
    done=0; /*done=1时发送成功了*/ <br>
    while(!done) { <br>
    if(el_xmit(sc,len)) { /* 调用发送例程,其实只要传送base就可以了 */ <br>
    done = -1; <br>
    break; <br>
    } <br>
    /* 检查输出后的状态,如果你要对watchdog支持,可以在这加上代码,即在5毫秒没发送出去,就调用el_watchdog() */ <br>
    i = inb(base+EL_TXS); <br>
    dprintf((&quot;tx status=0x%x\n&quot;,i)); <br>
    if(!(i &amp; EL_TXS_READY)) { /* 如果传输状态寄存器不是准备接受新帧就绪 */ <br>
    dprintf((&quot;el: err txs=%x\n&quot;,i)); /*那就是出错了*/ <br>
    sc-&gt;arpcom.ac_if.if_oerrors++; <br>
    if(i &amp; (EL_TXS_COLL|EL_TXS_COLL16)) {/*网络数据包碰撞*/ <br>
    if((!(i &amp; EL_TXC_DCOLL16)) &amp;&amp; retries &lt; 15) {/*做循环的目的是为了有错误时可重传15次*/
    <br>
    retries++; <br>
    outb(base+EL_AC,EL_AC_HOST);/*EL_AC_HOST为系统总线可访问缓冲 */ <br>
    } <br>
    } <br>
    else <br>
    done = 1; <br>
    } <br>
    else { <br>
    sc-&gt;arpcom.ac_if.if_opackets++;/*统计用的,说明该卡成功发送一包*/ <br>
    done = 1; <br>
    } <br>
    } <br>
    if(done == -1) /* 包没有传输(失败) */ <br>
    continue; <br>
    <br>
    /* 现在给板卡一个机会接收.注意:在linux中曾经ALEN先生批评此卡好恐怖(他说要进博物馆,哈哈),并说在传输时 <br>
    会丢失进来的数据包,我查看了LINUX的驱动程序,确实是这样,但在FreeBSD中,给了一个机会,由此可证明他的 <br>
    关于&quot;丢失包的说法不一定成立&quot;,但关于一个缓冲和一次只能发送一数据包的说法确实是真的,还有多播方面也 <br>
    不支持,我也希望大家最好不要去买这东西,和NE2000,PCI中的RTL8139芯片的网卡一样,性能太差了.*/ <br>
    (void)inb(base+EL_AS);/*读辅助状态寄存器*/ <br>
    outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/ <br>
    splx(s); <br>
    /* 这有可能接收到中断(包到达) */ <br>
    s = splimp(); <br>
    } <br>
    } <br>
    <br>
    /* 这是真正的传输包,由el_start()调用 <br>
    */ <br>
    static int <br>
    el_xmit(struct el_softc *sc,int len) <br>
    { <br>
    int gpl; <br>
    int i; <br>
    <br>
    gpl = EL_BUFSIZ - len; <br>
    dprintf((&quot;el: xmit...&quot;)); <br>
    outb((sc-&gt;el_base)+EL_GPBL,(gpl &amp; 0xff)); <br>
    outb((sc-&gt;el_base)+EL_GPBH,((gpl&gt;&gt;&amp;0xff)); <br>
    outb((sc-&gt;el_base)+EL_AC,EL_AC_TXFRX);/*真正的传送指令*/ <br>
    i = 20000; <br>
    while((inb((sc-&gt;el_base)+EL_AS) &amp; EL_AS_TXBUSY) &amp;&amp;
    (i&gt;0))/*如果传送还在忙,循环20000次等待*/ <br>
    i--; <br>
    if(i == 0) {/*这里有一个bug,大家发现没有,i到了0时也有可能传送成功,解决办法是把(i&gt;0)这条件放到前面*/ <br>
    /*我稍微讲一下C,在编译C程序时,象while ( (a&gt;b) &amp;&amp; (i&gt;0) )时,是这个样子 <br>
    top:if a&gt;b then <br>
    if i&gt;0 then <br>
    执行体 <br>
    endif <br>
    endif <br>
    goto top <br>
    也就是说,当i=0时候,inb((sc-&gt;el_base)+EL_AS)这指令还会执行,也有可能这时候传送完成了,而下面有给打出 <br>
    一个什么&quot;tx not ready&quot;的东东,而且返回失败,有得重新传送一次. <br>
    */ <br>
    dprintf((&quot;tx not ready\n&quot;)); <br>
    sc-&gt;arpcom.ac_if.if_oerrors++; <br>
    return(-1); <br>
    } <br>
    dprintf((&quot;%d cycles.\n&quot;,(20000-i))); <br>
    return(0);/*成功*/ <br>
    } <br>
    <br>
    /* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用 */ <br>
    static __inline void <br>
    elread(struct el_softc *sc,caddr_t buf,int len) <br>
    { <br>
    register struct ether_header *eh; <br>
    struct mbuf *m; <br>
    <br>
    eh = (struct ether_header *)buf;/*从buf中分出以太网头部*/ <br>
    <br>
    /* <br>
    * elget函数是把包放入一mbuf缓冲链中 <br>
    */ <br>
    m = elget(buf,len,&amp;sc-&gt;arpcom.ac_if); <br>
    if(m == 0)/*出错了*/ <br>
    return; <br>
    <br>
    ether_input(&amp;sc-&gt;arpcom.ac_if,eh,m);/*传输给上一层的包括ifnet结构,以太网头部,一mbuf*/
    <br>
    } <br>
    <br>
    /* 中断例程 */ <br>
    static void <br>
    elintr(int unit) <br>
    { <br>
    register struct el_softc *sc; <br>
    register int base; <br>
    int stat, rxstat, len, done; <br>
    <br>
    /* 寻址softc和I/O基地址 */ <br>
    sc = &amp;el_softc[unit]; <br>
    base = sc-&gt;el_base; <br>
    <br>
    dprintf((&quot;elintr: &quot;)); <br>
    <br>
    /* 检查板卡状态 */ <br>
    stat = inb(base+EL_AS); <br>
    if(stat &amp; EL_AS_RXBUSY) {/*接收忙*/ <br>
    (void)inb(base+EL_RXC);/*读接收命令寄存器*/ <br>
    outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/ <br>
    return; <br>
    } <br>
    <br>
    done = 0; <br>
    while(!done) { <br>
    rxstat = inb(base+EL_RXS); <br>
    if(rxstat &amp; EL_RXS_STALE) {/*EL_RXS_STALE代表接受状态没有改变*/ <br>
    (void)inb(base+EL_RXC);/*读接收命令寄存器*/ <br>
    outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));/* 用IRQ(中断)使能和接收 写辅助命令寄存器*/ <br>
    return; <br>
    } <br>
    <br>
    /* 如果这有一个溢出发生,重新初始化板卡. */ <br>
    if(!(rxstat &amp; EL_RXS_NOFLOW)) { <br>
    dprintf((&quot;overflow.\n&quot;)); <br>
    el_hardreset(sc); <br>
    /* 使板卡回到接收模式 */ <br>
    if(sc-&gt;arpcom.ac_if.if_flags &amp; IFF_PROMISC) <br>
    outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
    <br>
    else <br>
    outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
    <br>
    (void)inb(base+EL_AS);/*读辅助状态寄存器*/ <br>
    outb(base+EL_RBC,0);/*清除接收缓冲*/ <br>
    (void)inb(base+EL_RXC);/*读接收命令寄存器*/ <br>

⌨️ 快捷键说明

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