歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux服務器 >> Linux中寫個簡單的網卡驅動程序

Linux中寫個簡單的網卡驅動程序

日期:2017/3/2 16:32:44   编辑:Linux服務器

學習應該是一個先把問題簡單化,再把問題復雜化的過程。一開始就著手處理復雜的問題,難免讓人有心驚膽顫,捉襟見肘的感覺。讀Linux網卡驅動 也是一樣。那長長的源碼夾雜著那些我們陌生的變量和符號,望而生畏便是理所當然的了。不要擔心,事情總有解決的辦法,先把一些我們管不著的代碼切割出去,留下必須的部分,把框架掌握了,那其他的事情自然就水到渠成了,這是筆者的心得。

  一般在使用的Linux網卡驅動代碼動辄3000行左右,這個代碼量以及它所表達出來的知識量無疑是龐大的,我們有沒有辦法縮短一下這個代碼量,使我們的學習變的簡單些呢?經過筆者的不懈努力,在仍然能夠使網絡設備正常工作的前提下,把它縮減到了600多行,我們把暫時還用不上的功能先割出去。這樣一來,事情就簡單多了,真的就剩下一個框架了。

  下面我們就來剖析這個可以執行的框架。

  限於篇幅,以下分析用到的所有涉及到內核中的函數代碼,我都不予列出,但給出在哪個具體文件中,請讀者自行查閱。

  首先,我們來看看設備的初始化。當我們正確編譯完我們的程序後,我們就需要把生成的目標文件加載到內核中去,我們會先 ifconfig eth0 down和rmmod 8139too來卸載正在使用的網卡驅動,然後insmod 8139too.o把我們的驅動加載進去(其中8139too.o是我們編譯生成的目標文件)。就像C程序有主函數main()一樣,模塊也有第一個執行的函數,即 module_init(rtl8139_init_module);在我們的程序中,rtl8139_init_module()在insmod之後首 先執行,它的代碼如下:

  static int __init rtl8139_init_module (void)

  {

  return pci_module_init (&rtl8139_pci_driver);

  }

  它直接調用了pci_module_init(),這個函數代碼在Linux/drivers/net/eepro100.c中,並且把 rtl8139_pci_driver(這個結構是在我們的驅動代碼裡定義的,它是驅動程序和PCI設備聯系的紐帶)的地址作為參數傳給了它。 rtl8139_pci_driver定義如下:

  static struct pci_driver rtl8139_pci_driver = {

  name: MODNAME,

  id_table: rtl8139_pci_tbl,

  probe: rtl8139_init_one,

  remove: rtl8139_remove_one,

  };

  pci_module_init()在驅動代碼裡沒有定義,你一定想到了,它是Linux內核提供給模塊是一個標准接口,那麼這個接口都干了些什麼?筆者跟蹤了這個函數,裡面調用了pci_register_driver(),這個函數代碼在Linux/drivers/pci/pci.c 中,pci_register_driver做了三件事情。

  ①是把帶過來的參數rtl8139_pci_driver在內核中進行了注冊。內核中有一個PCI設備的大的鏈表,這裡負責把這個PCI驅動掛到裡面去。

  ②是查看總線上所有PCI設備(網卡設備屬於PCI設備的一種)的配置空間,如果發現標識信息與rtl8139_pci_driver中的id_table相同,即rtl8139_pci_tbl,而它的定義如下:

  static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = {

  {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},

  {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0,0 },

  {0,}

  };

  那麼就說明這個驅動程序就是用來驅動這個設備的,於是調用rtl8139_pci_driver中的probe函數即 rtl8139_init_one,這個函數是在我們的驅動程序中定義了的,它是用來初始化整個設備和做一些准備工作。這裡需要注意一下 pci_device_id是內核定義的用來辨別不同PCI設備的一個結構,例如在我們這裡0x10ec代表的是Realtek公司,我們掃描PCI設備配置空間如果發現有Realtek公司制造的設備時,兩者就對上了。當然對上了公司號後還得看其他的設備號什麼的,都對上了才說明這個驅動是可以為這個設備服務的。

  ③是把這個rtl8139_pci_driver結構掛在這個設備的數據結構(pci_dev)上,表示這個設備從此就有了自己的驅動了。而驅動也找到了它服務的對象了。

  PCI是一個總線標准,PCI總線上的設備就是PCI設備,這些設備有很多類型,當然也包括網卡設備,每一個PCI設備在內核中抽象為一個數據結構pci_dev,它描述了一個PCI設備的所有的特性,具體請查詢相關文檔,本文限於篇幅無法詳細描述。但是有幾個地方和驅動程序的關系特別大,必須予以說明。PCI設備都遵守PCI標准,這個部分所有的PCI設備都是一樣的,每個PCI設備都有一段寄存器存儲著配置空間,這一部分格式是一樣的,比如第一個寄存器總是生產商號碼,如Realtek就是10ec,而Intel則是另一個數字,這些都是商家像標准組織申請的,是肯定不同的。我就可以通過配置空間來辨別其生產商,設備號,不論你什麼平台,x86也好,ppc也好,他們都是同一的標准格式。當然光有這些PCI配置空間的統一格式還是不夠的,比如 說人類,都有鼻子和眼睛,但並不是所有人的鼻子和眼睛都長的一樣的。網卡設備是PCI設備必須遵守規則,在設備裡集成了PCI配置空間,但它是一個網卡就必須同時集成能控制網卡工作的寄存器。而寄存器的訪問就成了一個問題。在Linux裡面我們是把這些寄存器映射到主存虛擬空間上的,換句話說我們的CPU 訪存指令就可以訪問到這些處於外設中的控制寄存器。總結一下,PCI設備主要包括兩類空間,一個是配置空間,它是操作系統或BIOS控制外設的統一格式的空 間,CPU指令不能訪問,訪問這個空間要借助BIOS功能,事實上Linux的訪問配置空間的函數是通過CPU指令驅使BIOS來完成讀寫訪問的。

  而另一類是普通的控制寄存器空間,這一部分映射完後CPU可以訪問來控制設備工作。

  現在我們回到上面pci_register_driver的第二步,如果找到相關設備和我們的pci_device_id結構數12345下一頁

Copyright © Linux教程網 All Rights Reserved