1. 程式人生 > >Zynq-Linux移植學習筆記之八-linux網路驅動

Zynq-Linux移植學習筆記之八-linux網路驅動

這一篇詳細介紹一下zynq下linux核心中網路驅動的執行過程。

1、基本層次

在linux中,網路可以分為下面三個層次:


Linux網路驅動涉及到後面兩層,網路協議層中需要了解skb和netif;硬體驅動層也就是mac層,需要了解dma和dec。

按內容來說,整個網路驅動又可以分為下面五個部分:

a)      control設定,包含控制器操作、mac屬性地址,stamp等內容

b)      phy介面相關設定,包含ethtool,mdio這些內容

c)      ethtool其他設定

d)      mac層設定,主要是dma+dec

e)      netif設定,skb處理過程,包含IPalign,skb回收,TX/RX skb介面部分

其中a、b、c部分屬於基本介面,d、e部分屬於資料和遷移,對a、b、d部分需要精通,對c、e部分需要熟悉。

用一張圖表示,網路驅動內部呼叫介面如下:


上圖中ethtool是網路中常用工具,該工具呼叫driver中的介面,也可以直接呼叫MDIO,phy的介面。

2、硬體驅動層實現原理

在硬體驅動層,主要是通過DMA進行網路資料包的收發,在控制器端維護一對TX/RX包含desc的佇列,mac層收到的每一包資料以及要通過mac層發出的資料資訊被儲存在這一對佇列中,zynq上linux驅動的程式碼實現位於

Macb.c (drivers\net\ethernet\cadence)中。

如下圖所示:


以RX為例,上圖中macb_queue為包含desc網路描述符的佇列,每一個單元內包含macb_dma_desc結構體,結構體中第一個addr欄位指向mac層收到的資料包。該佇列在操作過程中存在兩種模式,一種是ring mode,另一種是chain mode。目前macb_queue採用的是ring mode。Ring mode是該佇列是分配連續空間的一個數組,指標從頭開始遍歷執行,當執行到陣列最後一個元素時返回頭部繼續執行。P,q指標分別對應處理的資料和接收的資料,通過標誌位進行判斷。如果接收快而處理慢,網路就會丟包。相反,如果處理快而接收慢,那陣列大小為2即可。目前RX陣列常見的大小為512,正好佔滿cpu一個page。

Chain mode指空間可以不連續,用指標進行相連,等同於迴圈連結串列。

無論是ring mode還是chain mode,當cpu執行到最後一個元素時,會產生一箇中斷。

網路收發包由於用到了dma,也可以用下圖進行表示,這裡的BD表在網路中即為為macb_queue。


3、網路協議層原理

在網路協議層中使用了一個skb_buff結構體,定義於include/linux/skbuff.h中,它的含義為“套接字緩衝區”,用於在Linux網路子系統各層間傳輸資料。他是一個雙向連結串列。從程式碼中可以找到結構體定義,該結構體的解釋參看:

網路協議層與硬體驅動層之間的資料互動其實就是一個數據的拷貝過程,收到資料時存放在RX中,然後通過拷貝複製到skb結構體中。發數時將skb結構體中資料拷貝到TX佇列內,然後再通過mac層傳送。

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進行操作

a)分配:分配一個sk_buff結構,供協議棧程式碼使用

struct sk_buff *netdev_alloc_skb(unsignedint len); 

分配一個緩衝區。dev_alloc_skb()函式以GFP_ATOMIC優先順序進行skb的分配。

b)釋放:

void dev_kfree_skb(struct sk_buff *skb);

網路裝置驅動程式中使用dev_kfree_skb()對skb進行釋放。

sk_buff中比較重要的成員是指向資料包中資料的指標,如下圖所示:


用於定址資料包中資料的指標,head指向已分配空間開頭,data指向有效的octet開頭,tail指向有效的octet結尾,而end指向tail可以到達的最大地址。如果不這樣做而分配一個大小固定的緩衝區,如果buffer不夠用,則要申請一個更大的buffer,拷貝進去再增加,這樣降低了效能。

c)變更

unsigned char *skb_put(struct sk_buff *skb,int len);將taill指標向後移動len長度,並返回tail移動之前的值。用於向skb有效資料區域末尾新增資料。

unsigned char *skb_push(struct sk_buff*skb, int len);將data指標向前移動len長度。並返回移動之後的值。用於向skb有效資料區域前端新增資料(包頭)。

unsigned char *skb_pull(struct sk_buff*skb, int len);

void skb_reserve(struct sk_buff ×skb, intlen);

下圖分別對應了這四個函式