Linux驅(qū)動之雜項設(shè)備(張棲銀詳談)

一、雜項設(shè)備驅(qū)動介紹

1.1 系統(tǒng)介紹

本文是基于linux-2.6.32內(nèi)核進(jìn)行分析的,如果使用的是其他版本的內(nèi)核,其內(nèi)核調(diào)用的函數(shù)可能有所不同,但是其實現(xiàn)原理是相通的。

1.2 雜項設(shè)備驅(qū)動的引入

在前面一小節(jié)里面,我們詳細(xì)介紹了字符設(shè)備驅(qū)動程序,知道字符設(shè)備指那些必須以串行順序依次進(jìn)行訪問,且沒有經(jīng)過系統(tǒng)快速緩沖的設(shè)備,了解了Linux內(nèi)核中驅(qū)動的框架和組成,以及編寫的步驟等。但是,當(dāng)我們寫的驅(qū)動程序多了之后,就會發(fā)現(xiàn):部分硬件并不符合預(yù)先定義的字符設(shè)備的范疇,而且普通字符設(shè)備的主設(shè)備號不管是靜態(tài)分配還是動態(tài)分配,都會消耗一個主設(shè)備號(目前一個系統(tǒng)最多只能有255個字符設(shè)備),比較浪費(fèi)主設(shè)備號資源。因此,而引入了雜項設(shè)備驅(qū)動。

在Linux里面,把無法歸類的五花八門的設(shè)備定義為混雜設(shè)備(用miscdevice結(jié)構(gòu)體描述)。雜項設(shè)備是一個典型的字符設(shè)備(與接下來要介紹的輸入子系統(tǒng)一樣,呵呵),其主設(shè)備號固定為10。其內(nèi)部實現(xiàn)就是用主設(shè)備號10來調(diào)用register_chrdev()實現(xiàn)的;并且在內(nèi)部還調(diào)用了class_create()和device_create ()為每個雜項設(shè)備創(chuàng)建設(shè)備節(jié)點,從而避免了我們通過mknod命令或自行調(diào)用該兩個函數(shù)來創(chuàng)建設(shè)備節(jié)點的麻煩。

從以上這點來說,雜項設(shè)備就是將我們平常編寫字符設(shè)備的驅(qū)動進(jìn)行了再次封裝,降低了我們編寫字符設(shè)備驅(qū)動的難度,同時節(jié)約了主設(shè)備號資源。

1.3 雜項設(shè)備與字符設(shè)備實現(xiàn)比較

在進(jìn)行字符設(shè)備驅(qū)動程序開發(fā)的過程中,我們的實現(xiàn)步驟如下:

  • 申請一個字符設(shè)備號:可以自己指定,也可系統(tǒng)自動分配;
  • 構(gòu)造一個file_operations結(jié)構(gòu)體,其包含對硬件的所有操作;
  • 實現(xiàn)file_operations結(jié)構(gòu)體中的成員函數(shù);
  • 將字符設(shè)備注冊進(jìn)系統(tǒng)中:register_chrdev();
  • 創(chuàng)建設(shè)備類和設(shè)備節(jié)點:class_create()、device_create();
  • 告訴內(nèi)核入口與出口函數(shù):module_init()、module_exit();

雜項設(shè)備驅(qū)動也是字符設(shè)備驅(qū)動,那么其注冊的過程與字符設(shè)備驅(qū)動一樣,也必須經(jīng)過上面的這些步驟,只是雜項設(shè)備驅(qū)動中的對申請字符設(shè)備號、注冊字符設(shè)備到系統(tǒng)、創(chuàng)建設(shè)備類和設(shè)備節(jié)點進(jìn)行了封裝,我們只需要完成如下幾步開發(fā)即可:

  • 構(gòu)造一個file_operations結(jié)構(gòu)體,其中包含對硬件的所有操作;
  • 實現(xiàn)file_operations結(jié)構(gòu)體中的成員函數(shù);
  • 構(gòu)造一個雜項設(shè)備驅(qū)動(struct miscdevice)實體,并賦值前面定義的file_operations實體;
  • 在入口函數(shù)處調(diào)用misc_register()向系統(tǒng)注冊雜項設(shè)備;
  • 在出口函數(shù)處調(diào)用misc_deregister()從系統(tǒng)注銷雜項設(shè)備;
  • 告訴內(nèi)核入口與出口函數(shù):module_init()、module_exit();

從中也可以得出一個結(jié)論:無論Linux內(nèi)核對驅(qū)動框架設(shè)計的如何好,內(nèi)核實現(xiàn)了多少的代碼,與硬件相關(guān)部分的代碼還是需要我們?nèi)崿F(xiàn)。

本文的重點內(nèi)容就是講解如上幾點內(nèi)容:

  • 雜項子系統(tǒng)如何向內(nèi)核注冊;
  • 雜項子系統(tǒng)如何實現(xiàn)字符設(shè)備注冊;
  • 雜項子系統(tǒng)如何創(chuàng)建設(shè)備類和設(shè)備文件;
  • 如何編寫一個雜項設(shè)備驅(qū)動程序;

1.4 雜項設(shè)備和字符設(shè)備與應(yīng)用層數(shù)據(jù)交互比較

編寫過字符設(shè)備驅(qū)動的人都知道,應(yīng)用程序與驅(qū)動之間實現(xiàn)數(shù)據(jù)交互就是通過應(yīng)用API的read()、write()調(diào)用,從而產(chǎn)生一個SWI軟件中斷,然后通過主設(shè)備號找到對應(yīng)的struct cdev結(jié)構(gòu)體實體,從而找到具體硬件設(shè)備的struct file_operations結(jié)構(gòu)體,然后具體調(diào)用底層的drv_read()、drv_write(),我們就是在具體的drv_read()和drv_write()中實現(xiàn)對硬件的操作的,其過程如下:

read()—>swi_read()—>drv_read()—>硬件操作

那么對于雜項設(shè)備驅(qū)動呢?前面已經(jīng)說了,雜項設(shè)備也是字符設(shè)備,那么它也必須經(jīng)歷上面的這些步驟,只是中間穿插了幾個通過次設(shè)備號找到對應(yīng)struct file_operation結(jié)構(gòu)體的過程而已而已。那么是如何穿插的呢:

首先在mist.c(雜項子系統(tǒng)的核心)文件的打開函數(shù)中獲取此設(shè)備號,然后次設(shè)備號找到對應(yīng)的fops(也即struct file_operations結(jié)構(gòu)體)填充struct file中的f_op成員,那么之后應(yīng)用調(diào)用read()、write()函數(shù)就是調(diào)用具體次設(shè)備號對應(yīng)的struct file_operations結(jié)構(gòu)體中的成員了;最后再調(diào)用fops中的open()函數(shù)打開具體的函數(shù):

open()—>
    swi_open()—>
        misc_open()
        {
            int minor = iminor(inode); //獲取次設(shè)備號
            new_fops = fops_get(c->fops);
            old_fops = file->f_op;
            file->f_op = new_fops;
            file->f_op = fops_get(old_fops);
        }
read()/write()—>
    swi_read()/swi_write()—>
        new_fops->drv_read()/drv_write()

這里說明一下:misc.c是雜項子系統(tǒng)的核心,內(nèi)核已經(jīng)實現(xiàn),我們只需要實現(xiàn)底層的硬件操作函數(shù)集合,然后調(diào)用misc_register()告訴雜項子系統(tǒng)就好了。

通過前面的介紹,不太理解也沒有關(guān)系。你只需要記住,其實雜項子系統(tǒng)就是一個典型的字符設(shè)備系統(tǒng),它也逃不過字符設(shè)備的框架,其應(yīng)用與驅(qū)動交互的流程也和字符設(shè)備驅(qū)動一樣,沒有什么不同就是了(要從心里小瞧它)。

要想搞明白雜項子系統(tǒng)的框架,只需要弄明白應(yīng)用程序是如何與具體的硬件設(shè)備驅(qū)動進(jìn)行交互的就行了。本文的重點就是對雜項子系統(tǒng)的注冊、雜項子系統(tǒng)中驅(qū)動與應(yīng)用數(shù)據(jù)交互過程,以及如何編寫雜項子系統(tǒng)進(jìn)行分析的。

二、雜項子系統(tǒng)實現(xiàn)

2.1 雜項子系統(tǒng)

雜項設(shè)備是主設(shè)備號為10的驅(qū)動設(shè)備,比較適用于功能簡單的設(shè)備。它有自己的設(shè)備結(jié)構(gòu)體(/include/linux/miscdevice.h):

struct miscdevice  {
    int minor;                      //次設(shè)備號
    const char *name;               //設(shè)備名字
    const struct file_operations *fops;  //文件操作集合
    struct list_head list;              //連接到misc_list的鏈表
    struct device *parent;            //父設(shè)備
    struct device *this_device;        //當(dāng)前設(shè)備,是device_create的返回值
    const char *nodename;
    mode_t mode;
};

miscdevice在本質(zhì)上仍然屬于字符設(shè)備,只是被增加了一層封裝而已,因此其驅(qū)動的主體工作還是file_operations的成員函數(shù)。

其中minor是次設(shè)備號,雜項子系統(tǒng)主要是依賴minor來對不同的雜項設(shè)備驅(qū)動進(jìn)行管理,如果該項設(shè)置為MISC_DYNAMIC_MINOR,表示由系統(tǒng)自行分配次設(shè)備號。name是調(diào)用misc_register()注冊雜項設(shè)備時創(chuàng)建節(jié)點文件的名字;list是雙向鏈表,用于將要注冊的雜項設(shè)備注冊進(jìn)misc_list鏈表中。如下圖所示:


image
image

接下來的內(nèi)容,將詳細(xì)介紹misc.c雜項子系統(tǒng)的實現(xiàn)過程,主要包括:

  1. 雜項子系統(tǒng)的注冊;
  2. 雜項子系統(tǒng)之設(shè)備注冊;
  3. 雜項子系統(tǒng)之設(shè)備注銷;
  4. 雜項子系統(tǒng)之設(shè)備使用;

2.2 雜項子系統(tǒng)實現(xiàn)

2.2.1 雜項子系統(tǒng)的注冊

還有 79% 的精彩內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
支付 ¥2.99 繼續(xù)閱讀

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容