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

📄 网卡驱动程序详解.doc

📁 网卡驱动程序详解
💻 DOC
📖 第 1 页 / 共 4 页
字号:
    本驱动程序中并不支持该函数,在我的rtl8139说明中有*/ <br>
    static void el_stop(void *);/*停止接口,在el_ioctl()和el_reset()中调用*/ <br>
    static int el_xmit(struct el_softc *,int);/*把数据包放到芯片内,发送到以太网上*/ <br>
    static ointhand2_t elintr;/*中断例程*/ <br>
    static __inline void elread(struct el_softc *,caddr_t,int);/* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用
    */ <br>
    static struct mbuf *elget(caddr_t,int,struct ifnet *); /* 从网卡上下载数据包,len是数据的长度,本地以太网头部被分开*/
    <br>
    static __inline void el_hardreset(void *);/* 这是一个子程序,目的是重设硬件.*/ <br>
    <br>
    /* isa_driver结构 为 autoconf准备 */ <br>
    /* isa_driver结构说明: <br>
    该结构来之于文件isa_device.h头文件 <br>
    结构成员: <br>
    /* <br>
    * 通用的设备驱动程序结构. <br>
    * <br>
    * 没一设备驱动程序定义一组例程入口,由设置程序在启动时使用. <br>
    <br>
    struct isa_driver { <br>
    int (*probe) __P((struct isa_device *idp)); <br>
    /* 测试设备是否存在 <br>
    int (*attach) __P((struct isa_device *idp)); <br>
    /* 为设备设置驱动程序 <br>
    char *name; /* 设备名称 <br>
    int sensitive_hw; /* 探测发生有冲突时为真,ISA设备的老毛病 <br>
    }; <br>
    */ <br>
    struct isa_driver eldriver = { <br>
    el_probe, el_attach, &quot;el&quot; <br>
    }; <br>
    <br>
    <br>
    /* 探测程序.查看是否卡存在和是否在正确的位置. */ <br>
    static int <br>
    el_probe(struct isa_device *idev) <br>
    { <br>
    /* <br>
    isa_device 是设备的通用结构,该结构说明在isa_device.h头文件中,说明如下: <br>
    struct isa_device { <br>
    int id_id; /* 设备的 id <br>
    struct isa_driver *id_driver; 指向设备的驱动程序结构 <br>
    int id_iobase; /* 基本IO地址 <br>
    int id_iosize; /* 基本IO地址的长度 <br>
    u_int id_irq; /* 中断 <br>
    int id_drq; /* DMA <br>
    caddr_t id_maddr; /* 在总线中的物理IO内存地址(即便要) <br>
    int id_msize; /* IO内存的大小 <br>
    union { <br>
    inthand2_t *id_i; <br>
    ointhand2_t *id_oi;中断例程指针 <br>
    } id_iu; /* 中断接口例程 <br>
    #define id_intr id_iu.id_i <br>
    #define id_ointr id_iu.id_oi <br>
    int id_unit; /* 在该类设备中是第几个 <br>
    int id_flags; /* flags <br>
    int id_enabled; /* 设备激活了吗 <br>
    struct isa_device *id_next; /* 在 userconfig()中用于isa_devlist <br>
    struct device *id_device; <br>
    }; <br>
    <br>
    */ <br>
    struct el_softc *sc; <br>
    u_short base; /* 仅仅为了方便 */ <br>
    u_char station_addr[ETHER_ADDR_LEN];/*以太网的硬件地址*/ <br>
    int i; <br>
    <br>
    /* 搜集一些信息 */ <br>
    sc = &amp;el_softc[idev-&gt;id_unit];/*sc是softc结构,如果你有NEL块el卡的话就有NEL个softc <br>
    结构,当然也有可能同时还有其他的xx_softc结构*/ <br>
    sc-&gt;el_base = idev-&gt;id_iobase;/*该块卡的基本I/O地址*/ <br>
    base = sc-&gt;el_base;/*有一点多余,只是为了方便下面的引用*/ <br>
    <br>
    /* 第一次检查地址,看看基本地址是否在0X280到0X3F0之内 */ <br>
    if((base &lt; 0x280) || (base &gt; 0x3f0)) { <br>
    printf(&quot;el%d: ioaddr must be between 0x280 and 0x3f0\n&quot;, <br>
    idev-&gt;id_unit); <br>
    return(0); <br>
    } <br>
    <br>
    /* 现在尝试从PROM中获取地址,看看是否包含了3COM供应商的标识代码. <br>
    */ <br>
    dprintf((&quot;Probing 3c501 at 0x%x...\n&quot;,base));/*在调试时会打印出*/ <br>
    <br>
    /* 重置板卡 */ <br>
    dprintf((&quot;Resetting board...\n&quot;)); <br>
    outb(base+EL_AC,EL_AC_RESET);/*我们一般定义基地址为0X300,EL_AC=0E,是辅助命令寄存器*/ <br>
    DELAY(5);/*延迟5毫秒*/ <br>
    outb(base+EL_AC,0); <br>
    dprintf((&quot;Reading station address...\n&quot;)); <br>
    /* 读硬件地址,共六次 */ <br>
    for(i=0;i&lt;ETHER_ADDR_LEN;i++) { <br>
    outb(base+EL_GPBL,i); <br>
    station_addr[i] = inb(base+EL_EAW);/*EL_EAW是该卡的地址口,station_addr是函数内部变量, <br>
    下面判断了生产厂家后就没用的*/ <br>
    } <br>
    dprintf((&quot;Address is %6D\n&quot;,station_addr, &quot;:&quot;)); <br>
    <br>
    /* 如果厂商标识代码正确,那么返回1. <br>
    */ <br>
    if((station_addr[0] != 0x02) || (station_addr[1] != 0x60) <br>
    || (station_addr[2] != 0x8c)) { <br>
    dprintf((&quot;Bad vendor code.\n&quot;));/*3COM厂商此种卡的代码为02608C*/ <br>
    return(0); <br>
    } else { <br>
    dprintf((&quot;Vendor code ok.\n&quot;)); <br>
    /* 把地址拷贝到arpcom结构中 */ <br>
    bcopy(station_addr,sc-&gt;arpcom.ac_enaddr,ETHER_ADDR_LEN); <br>
    return(1); <br>
    } <br>
    } <br>
    <br>
    /* 这是一个子程序,目的是重设硬件. 在el_init()中调用,在elintr()中调用,产生中断,有溢出发生时调用*/ <br>
    static __inline void <br>
    el_hardreset(xsc) <br>
    void *xsc; <br>
    { <br>
    register struct el_softc *sc = xsc;/*记住在C中,寄存器变量只能有三个,可加快速度*/ <br>
    register int base; <br>
    register int j; <br>
    <br>
    base = sc-&gt;el_base; <br>
    <br>
    /* 第一步,重设板卡,和el_probe中的一样(前面) */ <br>
    outb(base+EL_AC,EL_AC_RESET); <br>
    DELAY(5); <br>
    outb(base+EL_AC,0); <br>
    <br>
    /* 又把地址填回去,为什么?没有为什么,就是厂商规定的,一些端口填什么数据时会怎么样,只有厂商知道,我相信, <br>
    在同一厂商之间的网卡,交换机,路由器进行秘密通讯是非常可能的,他可以不返回到CPU层*/ <br>
    for(j=0;j&lt;ETHER_ADDR_LEN;j++) <br>
    outb(base+j,sc-&gt;arpcom.ac_enaddr[j]); <br>
    } <br>
    <br>
    /* 连接该接口到核心数据结构.被调用时,我们已经知道该卡已经存在在给定的I/O <br>
    * 地址,我们还假定中断号是正确的. <br>
    */ <br>
    static int <br>
    el_attach(struct isa_device *idev) <br>
    { <br>
    struct el_softc *sc; <br>
    struct ifnet *ifp;/*该结构是一个巨大的结构,在STEVEN的书中有描述,我也写了一篇*/ <br>
    u_short base;/*没用上,可以去掉*/ <br>
    <br>
    dprintf((&quot;Attaching el%d...\n&quot;,idev-&gt;id_unit)); <br>
    <br>
    /* 放置一些指针. */ <br>
    idev-&gt;id_ointr = elintr;/*放置中断例程指针,中断例程在下面*/ <br>
    sc = &amp;el_softc[idev-&gt;id_unit];/*定位本设备的softc结构指针*/ <br>
    ifp = &amp;sc-&gt;arpcom.ac_if;/*定位ifnet结构*/ <br>
    base = sc-&gt;el_base;/*从程序来看,这一句可以去掉,根本没用,因为在该函数中没用到base*/ <br>
    <br>
    /* 重设板卡 */ <br>
    dprintf((&quot;Resetting board...\n&quot;)); <br>
    el_hardreset(sc);/*该程序在上面*/ <br>
    <br>
    /* 初始化ifnet结构,该结构的成员经常用来被ether网子程序,arp,bridge等调用 */ <br>
    ifp-&gt;if_softc = sc;/*该网卡的IFP(通用接口结构)的专用结构指针(softc结构)*/ <br>
    ifp-&gt;if_unit = idev-&gt;id_unit;/*第几块网卡*/ <br>
    ifp-&gt;if_name = &quot;el&quot;;/*网络卡的名称*/ <br>
    ifp-&gt;if_mtu = ETHERMTU;/*1500*/ <br>
    ifp-&gt;if_output = ether_output;/*以太网的输出子程序指针(不要搞错了,是向IP层 <br>
    输出,按我们的理解是数据输入了,再转送到上一层协议)*/ <br>
    ifp-&gt;if_start = el_start;/*把数据包从硬件接口输出去*/ <br>
    ifp-&gt;if_ioctl = el_ioctl;/*控制网卡的函树指针*/ <br>
    ifp-&gt;if_watchdog = el_watchdog;/*一般该函数用于包在一定时间内没发送出去,就调用他,在 <br>
    本驱动程序中并不支持该函数,在我的rtl8139说明中有*/ <br>
    ifp-&gt;if_init = el_init; /*不用说,是初始化,在probe,attach之后被调用*/ <br>
    ifp-&gt;if_flags = (IFF_BROADCAST | IFF_SIMPLEX);/*支持广播和单播*/ <br>
    <br>
    /* 调用通用以太网初始化例程 */ <br>
    dprintf((&quot;Attaching interface...\n&quot;)); <br>
    ether_ifattach(ifp, ETHER_BPF_SUPPORTED); <br>
    /* <br>
    在if_ethersubr.c中的ether_ifattach例程 <br>
    void ether_ifattach(ifp, bpf) 调用时,ETHER_BPF_SUPPORTED是BSD的 <br>
    包过滤器,如果在编译时设置文件没有 <br>
    打开包过滤器,那么代表0,否则是1 <br>
    register struct ifnet *ifp; <br>
    int bpf; <br>
    { <br>
    register struct ifaddr *ifa; <br>
    register struct sockaddr_dl *sdl; <br>
    <br>
    if_attach(ifp); 此例程在if.c 中 <br>
    ifp-&gt;if_type = IFT_ETHER;代表以太网 <br>
    ifp-&gt;if_addrlen = 6;硬件地址长度是6 <br>
    ifp-&gt;if_hdrlen = 14;包的头长度是6+6+2=14,其中2是协议类型 <br>
    ifp-&gt;if_mtu = ETHERMTU; 为1500,多此一举,在前面你可看到,已 <br>
    经填充了. <br>
    ifp-&gt;if_resolvemulti = ether_resolvemulti; 以太网解析多播例程指针 <br>
    if (ifp-&gt;if_baudrate == 0) 波特率 <br>
    ifp-&gt;if_baudrate = 10000000; <br>
    ifa = ifnet_addrs[ifp-&gt;if_index - 1];在ifnet_addrs[]数组中找到本地址指针 <br>
    KASSERT(ifa != NULL, (&quot;%s: no lladdr!\n&quot;, __FUNCTION__)); <br>
    sdl = (struct sockaddr_dl *)ifa-&gt;ifa_addr; ifa-&gt;ifa_addr在此时指向的是sockaddr_dl结构.
    <br>
    sdl-&gt;sdl_type = IFT_ETHER; <br>
    sdl-&gt;sdl_alen = ifp-&gt;if_addrlen; <br>
    bcopy((IFP2AC(ifp))-&gt;ac_enaddr, LLADDR(sdl), ifp-&gt;if_addrlen);把硬件地址拷贝到sdl结构中
    <br>
    if (bpf) bpf为真,即加入了BSD包过滤 <br>
    bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); <br>
    if (ng_ether_attach_p != NULL) <br>
    (*ng_ether_attach_p)(ifp); <br>
    } <br>
    <br>
    */ <br>
    printf(&quot;el%d: 3c501 address %6D\n&quot;,idev-&gt;id_unit, <br>
    sc-&gt;arpcom.ac_enaddr, &quot;:&quot;); <br>
    <br>
    dprintf((&quot;el_attach() finished.\n&quot;)); <br>
    return(1); <br>
    } <br>
    <br>
    /* 该例程重设接口. 在el_watchdog()中调用,因为watchdog不在本驱动程序中支持,所以从不被调用*/ <br>
    static void <br>
    el_reset(xsc)/*上面的一个函数,重设硬件*/ <br>
    void *xsc; <br>
    { <br>
    struct el_softc *sc = xsc; <br>
    int s; <br>
    <br>
    dprintf((&quot;elreset()\n&quot;)); <br>
    s = splimp();/*关网络中断*/ <br>
    el_stop(sc);/*下面的一个函数*/ <br>
    el_init(sc);/*重新初始化卡*/ <br>
    splx(s);/*开网络中断*/ <br>
    } <br>
    /*停止接口,在el_ioctl()和el_reset()中调用*/ <br>
    static void el_stop(xsc) <br>
    void *xsc; <br>
    { <br>
    struct el_softc *sc = xsc; <br>
    <br>
    outb(sc-&gt;el_base+EL_AC,0);/*用0写辅助命令寄存器*/ <br>
    } <br>
    <br>
    /* 初始化接口. */ <br>
    static void <br>
    el_init(xsc) <br>
    void *xsc; <br>
    { <br>
    struct el_softc *sc = xsc; <br>
    struct ifnet *ifp; <br>
    int s; <br>
    u_short base; <br>
    <br>
    ifp = &amp;sc-&gt;arpcom.ac_if;/*定位ifnet结构*/ <br>
    base = sc-&gt;el_base;/*网卡基本I/O地址*/ <br>
    <br>
    /* 如果地址不知道,什么也不做. */ <br>
    if(TAILQ_EMPTY(&amp;ifp-&gt;if_addrhead)) /* 在if.c中的if_attach例程 <br>
    中已经填充,由el_attach调用 <br>
    ether_attach时再调用if_attach */ <br>
    return; <br>
    <br>
    s = splimp();/*关网络中断*/ <br>
    <br>
    /* 重设板卡. */ <br>
    dprintf((&quot;Resetting board...\n&quot;)); <br>

⌨️ 快捷键说明

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