Linux裝置驅動工程師之路——網路裝置驅動基本原理和架構
K-Style
轉載請註明來自于衡陽師範學院08電2 K-Style http://blog.csdn.net/ayangke,QQ:843308498 郵箱:yangkeemail@qq.com
1.Linux網路子系統
Linux網路子系統的頂部是系統調用介面層。它為使用者空間提供的應用程式提供了一種訪問核心網路子系統的方法(socket)。位於其下面是一個協議無關層,它提供一種通用的方法來使用傳輸層協議。然後是具體協議的實現,在Linux中包括核心的協議TCP,UDP,當然還有IP。然後是裝置無關層,它提供了協議與裝置驅動通訊的通用介面,最下面是裝置的驅動程式。
裝置無關介面將協議與各種網路驅動串連在一起,這一層提供一組通用函數供底層網路裝置驅動使用,讓它們可以對高層協議棧進行操作。需要從協議層向裝置發生資料,需要調用dev_queue_xmit函數,這個函數對資料進行列隊,然後交由底層驅動程式的hard_start_xmit方法最終完成傳輸。接收通常是使用netif_rx執行的。當底層裝置程式接收到一個報文(發生中斷)時,就會調用netif_rx將資料上傳至裝置無關層。
為裝置無關層到驅動層的體繫結構
2.網路裝置描述(structnet_device)
每一個網路裝置都由struct net_device來描述,該結構可使用如下核心功能進行動態分配
struct net_device *alloc_net(intsizeof_priv, const char *mask, void(*setup)(struct net_deive *))
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 (*init)(struct net_device *dev)
初始化函數,該函數在register_netdev時被調用來完成對net_device結構的初始化
int (*open)(struct net_device *dev)
開啟介面。ifconfig啟用時,介面將被開啟
int (*stop)(struct net_deivce *dev)
停止介面,ifconfig eth% down時調用
int (*hard_start_xmit)(struct sk_buf*skb,struct net_device *dev)
資料發送函數
int (*do_ioctl)(struct net_deive *dev,struct ifreq *ifr, int cmd)
處理特定於介面的ioctl命令(sock_ioctl)進行調用。
int (*set_mac_address)(struct net_device*dev, void *addr)
改變MAC地址的函數,需要硬體支援該功能。
網路裝置的註冊
網路裝置註冊方式與字元驅動不同之處在於它沒有主次裝置號,並使用下面的函數註冊
int register_netdev(struct net_deivce*dev)
網路裝置的登出
void unregister_netdev(struct net_device*dev)
3.網路資料包描述(sk_buff)
Linux核心中每個網路資料包都由一個通訊端緩衝區結構structsk_buff描述,既每個sk_buff結構就是一個包,指向sk_buff的指標通常被稱作skb
sk_buff中重要的資料成員
struct device *dev;處理該包得裝置
__u32 sadd;r//IP元地址
__u32 daddr;//IP目的地址
__u32 raddr;//IP路由器地址
unsigned char *head;//分配空間的開始
unsigned char *data;//有效資料的開始
unsigned char *tail;//有效資料的結束
unsigned char *end;//分配空間的結束
unsigned long len;//有效資料的長度
sk_buff操作
struct sk_buff *alloc_skb(unsigned intlen, int priority)
分配一個sk_buff結構,供協議棧代碼使用
struct sk_buff *dev_alloc_skb(unsignedint len)
分配一個sk_buff結構。供驅動代碼使用
void kfree_skb(struct sk_buff *skb)
void dev_kfree_skb(struct sk_buff *skb)
釋放sk_buff結構
unsigned char *skb_push(struct sk_buff*skb,int len)
將data指標向前移動len長度。並返回移動之後的值。用於向skb有效資料區域前端添加資料(包頭)。
unsigned char *skb_put(struct sk_buff*skb, int len)
將taill指標向後移動len長度,並返回tail移動之前的值。用於向skb有效資料區域末尾添加資料。
4.驅動的實現
1).初始化(init)
裝置探測工作在init方法中進行,一般調用一個稱之為probe方法的函數
初始化的主要工作時檢測裝置,配置和初始化硬體,最後向系統申請這些資源。此外填充該裝置的dev結構,我們調用核心提供的ether_setup方法來設定一些乙太網路預設的設定。
2)開啟(open)
open這個方法在網路裝置驅動程式裡是網路裝置被啟用時被調用(即裝置狀態由down變成up)
實際上很多在初始化的工作可以放到這裡來做。比如說資源的申請,硬體的啟用。如果dev->open返回非0,則硬體狀態還是down
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中的一些資訊。
中斷有可能是收到資料產生也可能是發送完成產生,中斷處理常式要對中斷類型進行判斷,如果是收到資料中斷則開始接收資料,如果是發送完成中斷,則處理髮送完成後的一些操作,比如說重啟發送隊列。