 |
 |
培訓信息 |
|
|
|
 |
 |
贊助商 |
|
|
|
|
 |
 |
Linux-2.6.20的cs8900驅動分析(三)(轉載) |
|
|
| Linux-2.6.20的cs8900驅動分析(三)(轉載) |
| 作者:佚名 來源:單片機 錄入:jdzj868 更新時間:2009-8-12 16:51:54 點擊數:0 |
【字體:
】 |
http://blog.chinaunix.net/u1/49924/showart_488190.html Linux-2.6.20的cs8900驅動分析(三)
| | | | | 三、net_rx和net_send_packet3.1 net_rx在這部分將介紹cs8900驅動的兩個最重要的函數,內核通過該兩個函數實現了數據的收發(fā)。net_rx函數的主要功能是從cs8900的片上數據緩沖區(qū)中將數據傳送給sk_buff緩沖區(qū),sk_buff是網絡驅動程序與Linux內核通信的緩沖區(qū)。該結構可在<top_dir>\include\linux\skbuff.h中找到。net_rx函數的功能可總結如下:(該總結來源于:http://www.akae.cn/bbs/archiver/?tid-6657.html)A.獲取私有數據存放于lp中;B.獲取設備緩沖區(qū)狀態(tài)和緩沖長度;C.如果狀態(tài)不為RX_OK則計數接收數據錯誤次數count_rx_error()D.分配一個sk_buf區(qū)間E.字對齊,skb_reserve();F.插入數據到接收口,insw();G.寫入數據;H.初始化sk_buff結構,eth_type_trans()I.進入上層接收函數netif_rx();J.初始化設備的計數;net_rx函數的注解如下所示:static void net_rx(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //lp指向驅動程序的私有數據區(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)為接收錯誤,調用count_rx_errors統計錯誤 count_rx_errors(status, lp); return; } /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2); //分配一個緩沖區(qū),dev_alloc_skb函數以三、net_rx和net_send_packet3.1 net_rx在這部分將介紹cs8900驅動的兩個最重要的函數,內核通過該兩個函數實現了數據的收發(fā)。net_rx函數的主要功能是從cs8900的片上數據緩沖區(qū)中將數據傳送給sk_buff緩沖區(qū),sk_buff是網絡驅動程序與Linux內核通信的緩沖區(qū)。該結構可在<top_dir>\include\linux\skbuff.h中找到。net_rx函數的功能可總結如下:(該總結來源于:http://www.akae.cn/bbs/archiver/?tid-6657.html)A.獲取私有數據存放于lp中;B.獲取設備緩沖區(qū)狀態(tài)和緩沖長度;C.如果狀態(tài)不為RX_OK則計數接收數據錯誤次數count_rx_error()D.分配一個sk_buf區(qū)間E.字對齊,skb_reserve();F.插入數據到接收口,insw();G.寫入數據;H.初始化sk_buff結構,eth_type_trans()I.進入上層接收函數netif_rx();J.初始化設備的計數; net_rx函數的注解如下所示:static void net_rx(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //lp指向驅動程序的私有數據區(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)為接收錯誤,調用count_rx_errors統計錯誤 count_rx_errors(status, lp); return; } /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2); //分配一個緩沖區(qū),dev_alloc_skb函數以 //GFP_ATOMIC優(yōu)先級調用alloc_skb。alloc_skb的功能為分配一個緩沖區(qū) //并初始化skb->data,skb->tail和skb_head域。dev_alloc_skb和alloc_skb的區(qū) //別為,前者在skb->data和skb_head之間保留了一些空間,網絡層使用這 //一數據空間進行優(yōu)化工作,驅動程序不該訪問該空間。 if (skb == NULL) { //skb緩沖區(qū)分配失?…… lp->stats.rx_dropped++; //直接將丟包數加1 return; } skb_reserve(skb, 2); /* longword align L3 header */ //該函數增加skb的data和tail, //該函數可填充緩沖區(qū)之前保留報文頭空間,大多數以太網在數據包之前 //保留2個字節(jié),這樣IP頭可在14字節(jié)的以太網頭之后,在16字節(jié)邊界上對 //齊。這里也空了兩個字節(jié),這兩個自己加上14字節(jié)的以太網頭剛好16字 //節(jié)。所以這里的主要作用是字對齊。 skb->dev = dev; readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1); //skb_put函 //數的作用是更新skb的tail和len成員,也即在緩沖區(qū)尾部添加數據,該函數返 //回skb->tail的先前值。整句代碼的含義為,從cs8900的數據緩沖區(qū)中讀取 //length個字節(jié)數據到skb緩沖區(qū)。由于readwords是以讀取字(兩個字節(jié))為 //單位,所以length應該保持字對齊,也即length右移一位。 if (length & 1) //因為前面length以字對齊,如果length為單字節(jié), //所以這里應該補上最后一個字節(jié) skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);…… skb->protocol=eth_type_trans(skb,dev); //該函數定義在linux/net/ethernet/eth.c中, //該處可參見linux設備驅動程序相關章節(jié) netif_rx(skb); //通知內核已經接收到一個數據包,并封裝入一個套接字緩沖區(qū) dev->last_rx = jiffies; //更新最后的接收包時間 lp->stats.rx_packets++; //接收的總數據包數加1 lp->stats.rx_bytes += length; //接收的字節(jié)數加上length} 3.1 net_send_packet net_send_parcket為內核提供了數據包發(fā)送功能,該函數在cs89x0_probe1中被賦予了net_device的hard_start_xmit域,當內核需要發(fā)送數據包時,將調用dev-> hard_start_xmit完成最后的數據包發(fā)送。該函數被調用的前提是,在調用該函數之前,內核已經將數據包放入了skb緩沖區(qū)中。該函數的主要任務有:A.獲取設備私有數據指針B.加環(huán)形鎖,spin_lock_irq();C.檢測緩沖區(qū)是否為滿,若滿則調用netif_stop_queue()暫停發(fā)送隊列;D.寫發(fā)送命令和發(fā)送長度,writeword();E.讀取發(fā)送總線狀態(tài)readreg();F.解環(huán)形鎖,spin_unlock_irq();G.設置傳輸時鐘計數;H.釋放相應sk_buff, dev_kfree_skb().下面為此函數的簡單注釋:static int net_send_packet(struct sk_buff *skb, struct net_device *dev){ struct net_local *lp = netdev_priv(dev); //獲得驅動程序的私有數據 …… spin_lock_irq(&lp->lock); //獲得自旋鎖,以便進入臨界區(qū) netif_stop_queue(dev); //通知內核暫停內核與驅動程序間的數據傳遞,也即告訴 //內核不要向skb緩沖區(qū)填充數據。 /* initiate a transmit sequence */ //初始化cs8900的發(fā)送對列,主要為寫命令和數 //據長度,為數據發(fā)送做準備 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); //將數 //據交給cs8900發(fā)送 spin_unlock_irq(&lp->lock); //發(fā)送結束,釋放自旋鎖 lp->stats.tx_bytes += skb->len; //累加發(fā)送的總字節(jié)數 dev->trans_start = jiffies; //更新最后的傳輸時間 dev_kfree_skb (skb); //發(fā)送完畢,釋放skb緩沖區(qū) …… return 0;}總結: 在cs8900驅動中,主要簡解了驅動程序中的部分重要函數,包括初始化、打開/關閉網絡驅動和發(fā)送/接收數據。對于其余的驅動程序代碼,如超時處理、狀態(tài)獲取等函數沒做解釋,它們的實現也比較簡單。由于自己板子上沒有EEPROM,所以也沒有分析與EEPROM相關部分的代碼。DMA部分好像編譯進去會錯,所以也沒有去分析,以后有時間再去弄弄DMA部分。 The End ------ anmnmnly ------ 2007.12.16 | |
|
|
發(fā)表評論 告訴好友 打印此文 收藏此頁 關閉窗口 返回頂部 |
|
|
 |
 |
網友評論:(只顯示最新5條。) |
|
|
|
|
|