1. 程式人生 > >Linux網路驅動框架

Linux網路驅動框架

網路裝置介面層:

網路裝置介面層的主要功能是為千變萬化的網路裝置定義了統一,抽象的資料結構net_device結構體,以不變應萬變,實現多種硬體在軟體層次上的統一。

    每一個網路裝置都由struct net_device來描述,該結構可使用如下核心函式進行動態分配

struct net_device *alloc_netdev(int sizeof_priv, const char *mask, void(*setup)(struct net_device *))

sizeof_priv是私有資料區大小;mask是裝置名,setup是初始化函式,在註冊該裝置時,該函式被呼叫。也就是net_deivce的init成員。

struct net_device *alloc_etherdev(intsizeof_priv)

這個函式和上面的函式不同之處在於核心知道會將該裝置做一個乙太網裝置看待並做一些相關的初始化。

net_device結構可分為全域性成員、硬體相關成員、介面相關成員、裝置方法成員和公用成員等五個部分

    主要全域性成員

char name[INFAMSIZ]    裝置名,如:eh%d

unsigned long state  裝置狀態

unsigned long base_addr  I/O基地址

unsigned int irq   中斷號

    主要裝置方法有

首先看開啟和關閉網路裝置的函式:

int (*open)(struct net_device *dev);

開啟介面。ifconfig啟用時,介面將被開啟

int (*stop)(struct net_device *dev);  

停止介面,ifconfig eth% down時呼叫
要注意的是ifconfig是interface config的縮寫,通常我們在使用者空間輸入:
ifconfig eth0 up  會呼叫這裡的open函式。
在使用者空間輸入:ifconfig eth0 down  會呼叫這裡的stop函式。
在使用ifconfig向介面賦予地址時,要執行兩個任務。首先,它通過ioctl(SIOCSIFADDR)(Socket I/O Control Set Interface Address)賦予地址,然後通過ioctl(SIOCSIFFLAGS)(Socket I/O Control Set Interface Flags)設定dev->flag中的IFF_UP標誌以開啟介面。這個呼叫會使得裝置的open方法得到呼叫。類似的,在介面關閉時,ifconfig使用ioctl(SIOCSIFFLAGS)來清理IFF_UP標誌,然後呼叫stop函式。


int (*init)(struct net_device *dev)

初始化函式,該函式在register_netdev時被呼叫來完成對net_device結構的初始化

int (*hard_start_xmit)(struct sk_buf*skb,struct net_device *dev)

資料傳送函式

int (*hard_header)(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); 

該方法根據先前檢索到的源和目的硬體地址建立硬體頭
int (*rebuild_header)(struct sk_buff *skb);
乙太網的mac地址是固定的,為了高效,第一個包去詢問mac地址,得到對應的mac地址後就會作為cache把mac地址儲存起來。以後每次發包不用詢問了,直接把包的地址拷貝出來。
void (*tx_timeout)(struct net_device *dev);  
如果資料包傳送在超時時間內失敗,這時該方法被呼叫,這個方法應該解決失敗的問題,並重新開始傳送資料。
struct net_device_stats *(*get_stats)(struct net_device *dev);  
當應用程式需要獲得介面的統計資訊時,這個方法被呼叫。
int (*set_config)(struct net_device *dev, struct ifmap *map);  
改變介面的配置,比如改變I/O埠和中斷號等,現在的驅動程式通常無需該方法。
int (*do_ioctl)(struct net_device *dev, struct ifmap *map);  
用來實現自定義的ioctl命令,如果不需要可以為NULL。
void (*set_multicast_list)(struct net_device *dev);  
當裝置的組播列表改變或裝置標誌改變時,該方法被呼叫。
int (*set_mac_address)(struct net_device *dev, void *addr);  
如果介面支援mac地址改變,則可以實現該函式。

裝置驅動介面層:

net_device結構體的成員(屬性和函式指標)需要被裝置驅動功能層的具體數值和函式賦予。對具體的設定xxx,工程師應該編寫裝置驅動功能層的函式,這些函式型如xxx_open(),xxx_stop(),xxx_tx(),xxx_hard_header(),xxx_get_stats(),xxx_tx_timeout()等。

網路裝置與媒介層:

網路裝置與媒介層直接對應於實際的硬體裝置。

    網路裝置的註冊

網路設備註冊方式與字元驅動不同之處在於它沒有主次裝置號,並使用下面的函式註冊

int register_netdev(struct net_deivce*dev)

網路裝置的登出

void unregister_netdev(struct net_device*dev)

四、驅動的實現

    1).初始化(init)

裝置探測工作在init方法中進行,一般呼叫一個稱之為probe方法的函式

初始化的主要工作時檢測裝置,配置和初始化硬體,最後向系統申請這些資源。此外填充該裝置的dev結構,我們呼叫核心提供的ether_setup方法來設定一些乙太網預設的設定。

    2)開啟(open)

open這個方法在網路裝置驅動程式裡是網路裝置被啟用時被呼叫(即裝置狀態由down變成up)

實際上很多在初始化的工作可以放到這裡來做。比如說資源的申請,硬體的啟用。如果dev->open返回非0,則硬體狀態還是down,
註冊中斷、DMA等;設定暫存器,啟動裝置;啟動傳送佇列

 一般註冊中斷都在init中做,但在網絡卡驅動程式中,註冊中斷大部分都是放在open中註冊,因為要經常關閉和重啟網絡卡

    3)關閉(stop)

stop方法做和open相反的工作

可以釋放某些資源以減少系統負擔

stop是在裝置狀態由up轉為down時被呼叫

    4)傳送(hard_start_xmit)

在系統呼叫的驅動程式的hard_start_xmit時,傳送的資料放在一個sk_buff結構中。一般的驅動程式傳給硬體發出去。也有一些特殊的裝置比如說loopback把資料組成一個接收資料在傳送給系統或者dummy裝置直接丟棄資料。

如果傳送成功,hard_start_xmit方法釋放sk_buff。如果裝置暫時無法處理,比如硬體忙,則返回1。

    5)接收

驅動程式並存在一個接受方法。當有資料收到時驅動程式呼叫netif_rx函式將skb交交給裝置無關層。

一般裝置收到資料後都會產生一箇中斷,在中斷處理程式中驅動程式申請一塊sk_buff(skb)從硬體中讀取資料位置到申請號的緩衝區裡。

接下來填充sk_buff中的一些資訊。

中斷有可能是收到資料產生也可能是傳送完成產生,中斷處理程式要對中斷型別進行判斷,如果是收到資料中斷則開始接收資料,如果是傳送完成中斷,則處理髮送完成後的一些操作,比如說重啟發送佇列。
接收流程:
1、分配skb=dev_alloc_skb(pkt->datalen+2)
2、從硬體中讀取資料到skb
3、呼叫netif_rx將資料交給協議棧

中斷處理

網路介面通常支援3種類型的中斷:

報文到達中斷

報文傳送完成中斷

出錯中斷。

中斷處理程式可通過檢視網絡卡的中斷狀態暫存器,來分辨出中斷型別。