上一篇(見 http://www.linuxidc.com/Linux/2012-03/56415.htm ),用一個簡單的虛擬網卡驅動,大致地介紹了一下網卡驅動的基本架構
這裡,再針對DM9000網卡驅動具體地分析一下網卡驅動的架構
首先,Linux網絡設備驅動從上到下分為四層:
1、網絡協議接口層:向網絡協議(ARP&IP)提供統一的數據包發送接口,通過dev_queue_xmit()函數發送數據,並通過netif_rx()函數接收數據。這一層的存在使得上層協議獨立於具體的設備
2、網絡設備接口層:向網絡協議接口層提供統一的用於描述具體網絡設備屬性和操作的結構體net_device,該結構體是設備驅動功能層中各函數的容器,從宏觀上規劃了具體操作硬件的設備驅動功能層的結構
3、設備驅動功能層:各函數是網絡設備接口層net_device數據結構的具體成員,是驅使網絡設備硬件完成相應動作的程序,通過dev_start_xmit()函數啟動發送操作,並通過網絡設備上的中斷觸發接收操作(open() --> dev_interrupt --> dev_rx())
4、網絡設備與媒介層:是完成數據包發送和接收的物理實體,包括網絡適配器和具體的傳輸媒介,網絡適配器被驅動功能層中的函數物理上驅動。對於Linux系統而言,網絡設備和媒介都可以是虛擬的。
在設計具體的網絡設備驅動程序是,我們需要完成的主要工作是編寫設備驅動功能層的相關函數以填充net_device數據結構體的內容,並將net_device注冊入內核。
DM9000網卡驅動位於內核源碼的driver/net/dm9000.c,它是基於平台驅動架構的
ps:此處按代碼的書寫順序分析架構
typedef struct board_info {
u32 io_addr; //寄存器I/O基地址
u32 io_data; //數據I/O基地址
... ...
}board_info_t;
static struct net_device *dm9000_dev = NULL;
static int irq = DM9KS_IRQ;
static int iobase = DM9KS_MIN_IO;
static unsigned char dev_addr[MAX_ADDR_LEN]; // hw address
//12、如下實現各net_device(dm9000_xxx)各成員函數
//13、網卡打開函數
static int dm9000_open(struct net_device *dev)
{
ret = request_irq(dev->irq, &dm9000_interrupt, 0, dev->name, dev); //申請端口、IRQ等(注冊中斷)
... ...
netif_start_queue(dev); //激活設備發送隊列
}
//17、當我們在打開網卡的時候即有申請IRQ中斷,因為網卡是通過中斷來實現數據的接收的
static void dm9000_interrupt(int irq, void *dev_id)
{
...
switch(status & IRQ_EVENT_MASK){ //當有中斷來到時,判斷中斷的類型
case IRQ_RECEIVER_EVENT: //當中斷類型為有數據來到時
dm9000_rx(dev); //調用網卡的數據接收函數
break;
//其他類型的中斷
... ...
}
}
//18、網卡數據包接收函數
static void dm9000_tx(struct dm9000_device *dev)
{
... ...
length = get_rev_len(...); //從硬件讀取到接收數據包有效數據的長度
skb = dev_alloc_skb(length + 2); //分配新的套接字緩沖區
skb->reserve(skb, 2); //對齊
skb->dev = dev;
insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1); //讀取硬件上接收到的數據
if(length & 1)
{
skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT); //並放入數據緩沖區
skb->protocol = eth_type_trans(skb, dev); //解析收到數據包上層協議的類型
netif_rx(skb); //將數據包上交給上層協議(ARP&IP)
dev->last_rx = jiffies; //記錄接收時間戳
... ...
}
}
//14、關閉網卡
static int dm9000_stop(struct net_device *dev)
{
free_irq(dev->irq, dev); //釋放端口、IRQ等
... ...
netif_stop_queue(dev); //停止設備傳送包
}