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

📄 网口程序实例.txt

📁 关于数据发送方面和接收差不多,在上层协议放置好数据到mbuf链后,调用el_start函数,该函数把mbuf链中 的数据放置到本块网卡的发送队列缓冲el_pktbuf中,然后再调用el_xmit函
💻 TXT
📖 第 1 页 / 共 3 页
字号:
{ 
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 = &amp; 
        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 int 
el_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 void 
el_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 + -