1. 程式人生 > >Linux網路裝置驅動(一)

Linux網路裝置驅動(一)

一.網路裝置驅動基礎

1.乙太網基礎理論

乙太網是區域網的一種,它使用載波監聽多路訪問及衝突檢測技術(CSMA/CD),並以10M/S的速率執行在多種型別的電纜上,常用的網絡卡晶片有DM9000、DM9161、CS8900晶片。

乙太網的拓撲結構有匯流排型和星型,乙太網的工作模式有半雙工和全雙工。目前雙絞線是乙太網最普通的傳輸介質,它多用於從主機到集線器或交換機的連線,光纖主要用於交換機間的級聯和交換機到路由器間的點到點的鏈路上。

在區域網中,多個節點是共享傳輸介質的,這就必須有某種機制來決定某個時刻,哪個裝置佔用傳輸介質來傳輸資料,因此,區域網的鏈路層要有介質訪問控制的功能,即資料鏈路層分為邏輯鏈路控制LLC子層和介質訪問控制MAC子層。

乙太網的幀結構主要有Ethernet II 、Ethernet 802.3
raw、Ethernet802.3SAP、Ethernet802.3SNAP 乙太網控制器實現了MAC層功能,而且必須與PHY(物理層收發器)聯合使用,前者與OSI的資料鏈路層相關,後者實現物理層功能。MII(媒體無關介面)是連線快速乙太網MAC和PHY的標準介面,乙太網的裝置驅動是通過MII與PHY通訊,以配置PHY ID、速率、雙工模式等引數。 2.Linux網路驅動層次 Linux網路驅動可以劃分四層,即網路協議介面層、網路裝置介面層、裝置驅動功能層和裝置物理媒介層,核心中是通過以dev_base為頭指標的裝置連結串列來管理所有的網路裝置。網路裝置驅動的編寫主要是網路裝置net_device的初始化和資料包的收發函式。 網路協議介面層 網路協議介面層最主要的功能是給上層協議提供了透明的資料包傳送和接收介面,當上層的API或IP需要傳送資料包時,它將呼叫網路協議介面層的dev_queue_xmit函式傳送一個內容為sk_buff的資料;當上層對資料包的接收資料,則是通過向netif_rx函式傳遞一個sk_buff資料結構的指標來完成的。Sk_buff套接字緩衝區為linux網路層提供了高效的緩衝區處理和流量控制機制,當傳送資料包時,Linux核心的網路處理模組必須建立一個需要傳輸的資料包sk_buff,然後將sk_buff遞交給下層,各層在sk_buff中新增不同的協議頭直至交給網路裝置傳送。同樣的,當網路裝置從網路媒介上接收到資料包後,必須去掉不同的協議頭再交給使用者。 下面是sk_buff的部分結構體成員定義 struct
sk_buff { …… unsigned int len, data_len; __u16 mac_len, hdr_len; sk_buff_data_t transport_header; //傳輸層的頭 sk_buff_data_t network_header; //網路層的頭 sk_buff_data_t mac_header; //MAC層的頭
sk_buff_data_t tail; //有效資料的尾指標 sk_buff_data_t end; unsigned char *head, //整個緩衝區的頭 *data; //有效資料的頭指標 unsigned int truesize; atomic_t users; }; 對於sk_buff操作,除了分配和釋放外,需要知道如下幾個函式:在緩衝區尾部增加資料skb_put;在緩衝區開頭增加資料skb_push;在緩衝區開頭移除資料skb_pull;調整整個緩衝區的頭部skb_reserve。 網路裝置介面層 網路裝置介面層主要是為變化多端的網路定義了一個統一且抽象的net_device,實現了多種硬體在軟體層次上的統一。網路裝置驅動主要是填充net_device的成員並註冊net_device來實現硬體操作函式和核心的掛接。通常情況下,網路裝置驅動以中斷方式接受資料,而net_device中則定義了poll_controller這種純輪詢的介面方式,另外由於寬頻介面每秒會收到幾千個資料包,如果使用中斷方式會導致系統性能下降,為了提高linux在寬頻系統上的效能,網路子系統開發者建立了一種基於輪詢的資料接收方式是NAPI(New API),其資料接收流程是”接收中斷來臨—關閉接收中斷—以輪詢方式接收完所有資料—開啟接收中斷—接收中斷來臨……”,與NAPI相關的函式有新增NAPI,使能NAPI,排程NAPI,具體函式是netif_napi_add,napi_enable,napi_schedule。最後需要注意的是對於NAPI方式和中斷方式接收資料,驅動設計上還是有一些不同的,比如NAPI方式是用netif_receive_skb函式將資料包傳遞給核心,而不是使用netif_rx函式。 裝置驅動功能層 對於具體的裝置,工程師應該實現net_device中的open,stop,tx,hard_header,get_stats,tx_timeout,interruppt等函式。 網路裝置媒介層 網路裝置媒介層直接對應實際的硬體裝置,我們需要定義一組讀寫裝置內部暫存器的函式,如ior,iow。 二.網路裝置驅動移植 下面我們主要講解基於mini2440的DM9000網絡卡驅動的移植 首先看內涵DM9000程式碼在/driver/net/dm9000.c 檢視/driver/net/Makefile obj-$(CONFIG_DM9000) += dm9000.o 檢視/driver/net/Konfig menuconfig NET_ETHERNET bool "Ethernet (10 or 100Mbit)" config DM9000 tristate "DM9000 support" depends on ARM || BLACKFIN || MIPS select CRC32 select MII 所以配置核心make menuconfig 時,需要選中這一項。 根據開發板電路圖知道DM9000的AEN埠接到了nGCS4上,同時DM9000的INT埠接到了IRQ_EINT7上,另外DM9000的CMD埠接到了LADDR2上,最後發現DM9000上資料線是SD0-SD15,即資料線的位數是16位。 根據mini2440地址空間的分配與片選訊號的定義知道,引腳nGCS4對應的空間的起始地址為0x20000000,這個由系統地址線控制。同時我們知道DM9000使用的中斷號就是IRQ_EINT7。另外,DM9000上的CMD訊號是控制地址埠還是資料埠的,如果CMD為0,即LADDR2為0表示訪問地址暫存器,當CMD為1,即LADDR2為1表示訪問資料暫存器。 下面我們進行DM9000驅動的移植,在mach-mini2440.c中新增如下程式碼 #include <linux/dm9000.h> #define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300) static struct resource mini2440_dm9k_resource[] = { [0] = { //地址埠 .start = MACH_MINI2440_DM9K_BASE, .end = MACH_MINI2440_DM9K_BASE + 3, .flags = IORESOURCE_MEM }, [1] = { //資料埠 .start = MACH_MINI2440_DM9K_BASE + 4, .end = MACH_MINI2440_DM9K_BASE + 7, .flags = IORESOURCE_MEM }, [2] = { //中斷號 .start = IRQ_EINT7, .end = IRQ_EINT7, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,//高電平觸發 } }; static struct dm9000_plat_data mini2440_dm9k_pdata = { //資料線的位數是16位,沒有使用E2PROM .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), .dev_addr = { 0x08,0x90,0x90,0x90,0x90,0x90}, //MAC地址 }; static struct platform_device mini2440_device_eth = { .name = "dm9000", //裝置名 .id = -1, .num_resources = ARRAY_SIZE(mini2440_dm9k_resource), .resource = mini2440_dm9k_resource, //資源 .dev = { .platform_data = &mini2440_dm9k_pdata, //私有資料 }, }; 最後在mini2440的BSP檔案mach-mini2440.c中新增如下程式碼 static struct platform_device *mini2440_devices[] __initdata = { …… & mini2440_device_eth, //新增 }; 這樣移植完畢後編譯核心生成核心映象。