一步步理解linux字符設備驅動框架(轉)


/*

 *本文版權歸於凌陽教育。如轉載請注明

 *原作者和原文鏈接 http://blog.csdn.net/edudriver/article/details/18354313*

 *特此說明並保留對其追究法律責任的權利*

 */

     現實社會中存在着大量設備,各種設備有自己的工作方式以及硬件特性。但是如果每類設備(比如串口這類設備)都有自己不同的驅動框架的話,這無疑對驅動程序員來說是一個非常大的挑戰。所以為了簡化驅動程序員的工作,linux系統從千千萬萬設備中提取它們的共性,將這些設備分成3大類:字符設備,塊設備,網絡設備。具體字符設備與塊設備有什么區別,大家可以看一下這篇文章(http://www.cnblogs.com/qlee/archive/2011/07/27/2118406.html),今天我們主要介紹字符設備的驅動框架。

      字符設備是前面提到的這三類設備中最常見的設備,比如生活中大家常見的鍵盤、鼠標、觸摸屏等都屬於字符設備。所以掌握字符設備驅動框架是每個驅動程序員所必須的。下面我們通過先通過一個圖把字符驅動的整體概念建立起來:

      1、首選上層應用程序調用open函數打開/dev目錄下的一個文件(這個文件就是“設備文件”)

 

 

     2、我們知道open是庫里面的函數,所以庫里面的一部分代碼會被調用

  

      3、這時候庫里面會執行一些異常指令,執行這些異常指令最后導致的結果就是產生“系統調用”進入內核,這個時候已經從用戶空間進入了內核空間

 

      4、進入內核后,內核會接受到這個異常並開始處理,處理的結果是找到對應的驅動程序

  

      5、找到對應的驅動后,驅動程序中的open()函數就會被調用。因為驅動程序是在內核里面執行的,它的運行權限就比較大,所以在驅動的open()函數就可以對硬件設備進行操作了。

 

      這就是上層open函數到驅動中的open函數的整個過程。當然,如果你open一個文件后,下面緊跟着就會調用read、write和close函數,他們的調用過程跟open函數的調用過程是如出一轍的,這里面我們就不再具體介紹了。

      在整個的流程中,大家可能會感覺到看似整個過程非常完整,但是還是有幾點過不去。比如當在庫中執行一段異常指令就會通過系統調用進入內核,內核是怎么找到對應的驅動程序的呢?如果考慮到這個問題,我們就不得不介紹與字符設備驅動密切相關的兩個概念了:設備文件(就是open打開的/dev目錄下的文件)和主從設備號。

      設備文件:從文字上看就是設備的文件,這樣解釋也是可以的,這類文件就是專門給硬件設備服務的文件,而且這類文件都存在/dev目錄下,每當我們向內核中加一個設備驅動程序,這時候就會在相應的/dev目錄下生成相應的設備文件。總結起來:設備文件就是跟驅動程序對應起來的,是上層應用訪問驅動的接口。下面我們就來看一下這個設備文件的屬性:

     從這個圖中我們可以看到這個設備文件中非常重要的3個屬性:

      —設備類型:c和b,c代表這個設備文件對應的設備驅動程序是一個字符設備驅動程序,b代表這個設備文件對應的設備驅動程序是一個塊設備驅動程序。

      —主設備號(范圍:1~254):標識一類驅動程序。通過前面這個圖我們可以看出主設備號204是標識串口這一類驅動程序;主設備號31標識mtd這一類驅動程序。那這里就有一個問題了,我們知道一台電腦上不止一個串口,這多個串口的驅動程序都是用主設備號204來標識,那我們怎么從中找到某一個具體的串口驅動程序呢,這個時候就需要我們的從設備號了。

      —從設備號(范圍:0~255):對應一類驅動程序中某一個具體的驅動程序。當我們通過204這個主設備號找到串口這一類驅動程序后,再通過從設備號找到具體的某一個串口的驅動程序。

      所以通過上面的分析我們就知道了我們上層的open函數一調用是怎么找到具體的驅動程序並且調用該驅動程序中的open函數的,最后要歸功於設備文件中的主從設備號。所以我們從這里也得出了一個非常重要的結論:主從設備在跟設備文件綁定的同時也跟具體的驅動程序綁定在一起。

      好了以上就是我們對寫具體的字符設備驅動程序所做的熱身,下面我們就來看一下怎么寫具體的字符設備驅動程序。

      首先,我們先建立一個.c文件把模塊搭建起來。

      然后,我們就要在模塊初始化函數里面注冊字符設備並且生成相應的設備文件。我們只需要3個函數就可以完成我們這里的任務。

      int register_chrdev(unsigned int major,  const char *name,    

                                                                  const struct file_operations *fops)

      這個函數完成了對字符設備的注冊,在這個函數中有兩個非常值得我們注意的信息:主設備號和struct  file_operartions結構體指針變量。

      register_chrdev()的第一個參數就是主設備號,我們如果給第一個參數賦值為0的話,這時候內核會動態的給我們分配一個主設備號,分配的主設備號會通過這個函數的返回值返回。這里大家可能會思考從設備號我們需要在那里指定呢?在這里從設備號是由內核給我們指定的,指定的從設備號就是0~255。也就說這個主設備號對應的所有從設備號我們都占用了,我們可以通過主設備號和0~255中任何一個從設備號都可以找到我們注冊的這個字符設備驅動程序。 這樣我們通過第一個參數我們的主從設備號就可以跟我們的字符設備驅動程序綁定在一起了,下面我們就來看一下另一個非常重要的參數struct file_operatiosn *變量。

      我們在調用register_chrdev()這個函數之前要定義這樣的一個結構體變量,在這個結構體里面有什么呢?

 

      在這里只是標出我們這個結構體里面我們看起來比較熟悉的成員:open、read、write、ioctl和release(就是我們驅動里面的close函數),這些成員都是函數指針,這些函數指針都指向我們驅動里面的一些具體函數。只要是被我們open這個函數指針指向的函數就是我們驅動里面的open函數,還有read函數、write函數也是如此。所以總起來說我們驅動程序中的所有open、read、write、ioctl和release函數都被封裝到這個結構體里面。我們通過把這個結構體變量的地址傳給register_chrdev()函數,就可以把它跟字符驅動綁定在一起。這是我們register_chrdev的第三個參數,第二個參數就是我們給這個字符驅動起的名字,大家不要以為這個名字是設備文件的名字,它只是我們字符驅動的名字,我們可以通過cat /proc/devices命令查看內核里面主設備號使用情況的時候可以看到這個名字。通過上面的函數我們已經把字符設備驅動程序注冊進了內核,下面我們的任務就是給這個字符設備驅動生成設備文件。

      生成設備文件我們有兩種方法:一個是手動生成,一個是自動生成。這里我們只介紹自動生成設備節點的方法。

      自動生成設備節點的方法無非就是在register_chrdev()函數后面再調用兩個函數:class_create()和device_create()函數,下面我們就主要看一下我們怎么調用這兩個函數。

      struct class * class_key = class_create(THIS_MODULE, “key_class”);

      這個函數主要是內核用來給設備文件進行分類用的,具體怎么實現這里我們就不深入說了,如果大家想了解可以學習有關《設備模型》的知識。

      struct device *device_create(struct class *cls,  struct device *parent, 

                                                                             dev_t devt, void *drvdata,

                                                                                           const char *fmt, ...)

      這個函數就完成了創建設備節點的任務,第一個參數就是我們前面class_create()函數的返回值,第二個參數我們填上NULL就可以了,第三個參數就是主從設備號(MKDEV(主設備號,從設備號)),第四個參數為NULL,最后一個參數為設備文件的名字。通過調用這個函數我們的設備文件就已經生成成功了。

      最后,當我們不想用這個驅動程序的時候,我們必須從內核中把這個驅動程序刪除掉。我們需要注銷字符設備及刪除設備節點。我們同樣涉及到了3個函數。

      void unregister_chrdev(unsigned int major, const char *name)

      注銷字符設備驅動的函數。第一個參數主設備號,第二個參數設備的名字。

      device_destroy(struct class *class, dev_t devt)

      刪除設備節點函數。第一個參數class_create()函數的返回值,第二個參數創建設備文件時用到的主從設備號。

      void class_destroy(struct class *cls)

      刪除前面class_create()函數創建的變量。

      以上,就是我們寫字符設備驅動程序涉及到的主要函數,下面我們就看一下led燈驅動程序。






 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM