?? 網口程序實例.txt
字號:
{
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 = &
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 + -