| 三、net_rx和net_send_packet3.1 net_rx在這部分將介紹cs8900驅(qū)動的兩個最重要的函數(shù),內(nèi)核通過該兩個函數(shù)實現(xiàn)了數(shù)據(jù)的收發(fā)。net_rx函數(shù)的主要功能是從cs8900的片上數(shù)據(jù)緩沖區(qū)中將數(shù)據(jù)傳送給sk_buff緩沖區(qū),sk_buff是網(wǎng)絡(luò)驅(qū)動程序與Linux內(nèi)核通信的緩沖區(qū)。該結(jié)構(gòu)可在<top_dir>\include\linux\skbuff.h中找到。net_rx函數(shù)的功能可總結(jié)如下:(該總結(jié)來源于:http://www.akae.cn/bbs/archiver/?tid-6657.html)A.獲取私有數(shù)據(jù)存放于lp中;B.獲取設(shè)備緩沖區(qū)狀態(tài)和緩沖長度;C.如果狀態(tài)不為RX_OK則計數(shù)接收數(shù)據(jù)錯誤次數(shù)count_rx_error()D.分配一個sk_buf區(qū)間E.字對齊,skb_reserve();F.插入數(shù)據(jù)到接收口,insw();G.寫入數(shù)據(jù);H.初始化sk_buff結(jié)構(gòu),eth_type_trans()I.進(jìn)入上層接收函數(shù)netif_rx();J.初始化設(shè)備的計數(shù);net_rx函數(shù)的注解如下所示:static void net_rx(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //lp指向驅(qū)動程序的私有數(shù)據(jù)區(qū) struct sk_buff *skb; //申請skb_buff指針 int status, length; int ioaddr = dev->base_addr; // 得到cs8900的基地址 status = readword(ioaddr, RX_FRAME_PORT); //獲取cs8900片上緩沖區(qū)的狀態(tài) length = readword(ioaddr, RX_FRAME_PORT); //獲取cs8900片上緩沖區(qū)的長度 if ((status & RX_OK) == 0) { //狀態(tài)為接收錯誤,調(diào)用count_rx_errors統(tǒng)計錯誤 count_rx_errors(status, lp); return; } /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2); //分配一個緩沖區(qū),dev_alloc_skb函數(shù)以三、net_rx和net_send_packet3.1 net_rx在這部分將介紹cs8900驅(qū)動的兩個最重要的函數(shù),內(nèi)核通過該兩個函數(shù)實現(xiàn)了數(shù)據(jù)的收發(fā)。net_rx函數(shù)的主要功能是從cs8900的片上數(shù)據(jù)緩沖區(qū)中將數(shù)據(jù)傳送給sk_buff緩沖區(qū),sk_buff是網(wǎng)絡(luò)驅(qū)動程序與Linux內(nèi)核通信的緩沖區(qū)。該結(jié)構(gòu)可在<top_dir>\include\linux\skbuff.h中找到。net_rx函數(shù)的功能可總結(jié)如下:(該總結(jié)來源于:http://www.akae.cn/bbs/archiver/?tid-6657.html)A.獲取私有數(shù)據(jù)存放于lp中;B.獲取設(shè)備緩沖區(qū)狀態(tài)和緩沖長度;C.如果狀態(tài)不為RX_OK則計數(shù)接收數(shù)據(jù)錯誤次數(shù)count_rx_error()D.分配一個sk_buf區(qū)間E.字對齊,skb_reserve();F.插入數(shù)據(jù)到接收口,insw();G.寫入數(shù)據(jù);H.初始化sk_buff結(jié)構(gòu),eth_type_trans()I.進(jìn)入上層接收函數(shù)netif_rx();J.初始化設(shè)備的計數(shù); net_rx函數(shù)的注解如下所示:static void net_rx(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //lp指向驅(qū)動程序的私有數(shù)據(jù)區(qū) struct sk_buff *skb; //申請skb_buff指針 int status, length; int ioaddr = dev->base_addr; // 得到cs8900的基地址 status = readword(ioaddr, RX_FRAME_PORT); //獲取cs8900片上緩沖區(qū)的狀態(tài) length = readword(ioaddr, RX_FRAME_PORT); //獲取cs8900片上緩沖區(qū)的長度 if ((status & RX_OK) == 0) { //狀態(tài)為接收錯誤,調(diào)用count_rx_errors統(tǒng)計錯誤 count_rx_errors(status, lp); return; } /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2); //分配一個緩沖區(qū),dev_alloc_skb函數(shù)以 //GFP_ATOMIC優(yōu)先級調(diào)用alloc_skb。alloc_skb的功能為分配一個緩沖區(qū) //并初始化skb->data,skb->tail和skb_head域。dev_alloc_skb和alloc_skb的區(qū) //別為,前者在skb->data和skb_head之間保留了一些空間,網(wǎng)絡(luò)層使用這 //一數(shù)據(jù)空間進(jìn)行優(yōu)化工作,驅(qū)動程序不該訪問該空間。 if (skb == NULL) { //skb緩沖區(qū)分配失敗?…… lp->stats.rx_dropped++; //直接將丟包數(shù)加1 return; } skb_reserve(skb, 2); /* longword align L3 header */ //該函數(shù)增加skb的data和tail, //該函數(shù)可填充緩沖區(qū)之前保留報文頭空間,大多數(shù)以太網(wǎng)在數(shù)據(jù)包之前 //保留2個字節(jié),這樣IP頭可在14字節(jié)的以太網(wǎng)頭之后,在16字節(jié)邊界上對 //齊。這里也空了兩個字節(jié),這兩個自己加上14字節(jié)的以太網(wǎng)頭剛好16字 //節(jié)。所以這里的主要作用是字對齊。 skb->dev = dev; readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1); //skb_put函 //數(shù)的作用是更新skb的tail和len成員,也即在緩沖區(qū)尾部添加數(shù)據(jù),該函數(shù)返 //回skb->tail的先前值。整句代碼的含義為,從cs8900的數(shù)據(jù)緩沖區(qū)中讀取 //length個字節(jié)數(shù)據(jù)到skb緩沖區(qū)。由于readwords是以讀取字(兩個字節(jié))為 //單位,所以length應(yīng)該保持字對齊,也即length右移一位。 if (length & 1) //因為前面length以字對齊,如果length為單字節(jié), //所以這里應(yīng)該補(bǔ)上最后一個字節(jié) skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);…… skb->protocol=eth_type_trans(skb,dev); //該函數(shù)定義在linux/net/ethernet/eth.c中, //該處可參見linux設(shè)備驅(qū)動程序相關(guān)章節(jié) netif_rx(skb); //通知內(nèi)核已經(jīng)接收到一個數(shù)據(jù)包,并封裝入一個套接字緩沖區(qū) dev->last_rx = jiffies; //更新最后的接收包時間 lp->stats.rx_packets++; //接收的總數(shù)據(jù)包數(shù)加1 lp->stats.rx_bytes += length; //接收的字節(jié)數(shù)加上length} 3.1 net_send_packet net_send_parcket為內(nèi)核提供了數(shù)據(jù)包發(fā)送功能,該函數(shù)在cs89x0_probe1中被賦予了net_device的hard_start_xmit域,當(dāng)內(nèi)核需要發(fā)送數(shù)據(jù)包時,將調(diào)用dev-> hard_start_xmit完成最后的數(shù)據(jù)包發(fā)送。該函數(shù)被調(diào)用的前提是,在調(diào)用該函數(shù)之前,內(nèi)核已經(jīng)將數(shù)據(jù)包放入了skb緩沖區(qū)中。該函數(shù)的主要任務(wù)有:A.獲取設(shè)備私有數(shù)據(jù)指針B.加環(huán)形鎖,spin_lock_irq();C.檢測緩沖區(qū)是否為滿,若滿則調(diào)用netif_stop_queue()暫停發(fā)送隊列;D.寫發(fā)送命令和發(fā)送長度,writeword();E.讀取發(fā)送總線狀態(tài)readreg();F.解環(huán)形鎖,spin_unlock_irq();G.設(shè)置傳輸時鐘計數(shù);H.釋放相應(yīng)sk_buff, dev_kfree_skb().下面為此函數(shù)的簡單注釋:static int net_send_packet(struct sk_buff *skb, struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //獲得驅(qū)動程序的私有數(shù)據(jù) …… spin_lock_irq(&lp->lock); //獲得自旋鎖,以便進(jìn)入臨界區(qū) netif_stop_queue(dev); //通知內(nèi)核暫停內(nèi)核與驅(qū)動程序間的數(shù)據(jù)傳遞,也即告訴 //內(nèi)核不要向skb緩沖區(qū)填充數(shù)據(jù)。 /* initiate a transmit sequence */ //初始化cs8900的發(fā)送對列,主要為寫命令和數(shù) //據(jù)長度,為數(shù)據(jù)發(fā)送做準(zhǔn)備 writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd); writeword(dev->base_addr, TX_LEN_PORT, skb->len); /* Test to see if the chip has allocated memory for the packet * / //查看cs8900是否為 //發(fā)送分配了地址空間。 if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { ……. spin_unlock_irq(&lp->lock); if (net_debug) printk("cs89x0: Tx buffer not free!\n"); return 1; } /* Write the contents of the packet */ writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1); //將數(shù) //據(jù)交給cs8900發(fā)送 spin_unlock_irq(&lp->lock); //發(fā)送結(jié)束,釋放自旋鎖 lp->stats.tx_bytes += skb->len; //累加發(fā)送的總字節(jié)數(shù) dev->trans_start = jiffies; //更新最后的傳輸時間 dev_kfree_skb (skb); //發(fā)送完畢,釋放skb緩沖區(qū) …… return 0;}總結(jié): 在cs8900驅(qū)動中,主要簡解了驅(qū)動程序中的部分重要函數(shù),包括初始化、打開/關(guān)閉網(wǎng)絡(luò)驅(qū)動和發(fā)送/接收數(shù)據(jù)。對于其余的驅(qū)動程序代碼,如超時處理、狀態(tài)獲取等函數(shù)沒做解釋,它們的實現(xiàn)也比較簡單。由于自己板子上沒有EEPROM,所以也沒有分析與EEPROM相關(guān)部分的代碼。DMA部分好像編譯進(jìn)去會錯,所以也沒有去分析,以后有時間再去弄弄DMA部分。 The End ------ anmnmnly ------ 2007.12.16 |