專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

設(shè)備驅(qū)動(dòng)總結(jié)4 自動(dòng)創(chuàng)建設(shè)備文件

作者:公平   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月14日   【字體:

自動(dòng)創(chuàng)建設(shè)備文件
設(shè)備文件是非常重要的文件,是應(yīng)用程序與設(shè)備驅(qū)動(dòng)交換數(shù)據(jù),控制硬件的橋梁。在驅(qū)動(dòng)程序中open、release的實(shí)現(xiàn)過程中其中的一個(gè)參數(shù)struct inode實(shí)質(zhì)就是設(shè)備文件的索引,沒有這個(gè)索引也就沒有后期的各種操作,通常設(shè)備文件也被稱為設(shè)備文件節(jié)點(diǎn)。因此沒有設(shè)備文件后期的各種實(shí)現(xiàn)都是多余的。設(shè)備文件的創(chuàng)建有兩種方法,其中就是在創(chuàng)建文件系統(tǒng)過程中用到的mknod命令。
該命令的形式如下:
mknod filename (type,c,b,l) 主設(shè)備號(hào) 次設(shè)備號(hào)
其中type說明是那一類設(shè)備(字符設(shè)備c,塊設(shè)備b,套接字l),主設(shè)備號(hào)用來確定那一類設(shè)備,而次設(shè)備號(hào)主要用來確定這一類設(shè)備中的某一個(gè)設(shè)備。
例如:mknod memdev0  c 555 0 就是創(chuàng)建了一個(gè)主設(shè)備號(hào)為555,次設(shè)備號(hào)為0的字符設(shè)備。

這種方法比較快速,但是在編寫設(shè)備驅(qū)動(dòng)的時(shí)候很難確定那個(gè)設(shè)備號(hào)是可以使用的,因此很不方便開發(fā)。在2.4內(nèi)核中引入了devfs,但是因?yàn)樾阅艿确矫娴脑�,�?.6內(nèi)核中被udev逐漸取代。udev的設(shè)備命名策略、權(quán)限控制和事件處理都是在用戶態(tài)下完成的,它利用sysfs中的信息來進(jìn)行創(chuàng)建設(shè)備文件節(jié)點(diǎn)等工作。其實(shí)對于我們寫程序而言并沒有多大的區(qū)別,這是內(nèi)核的設(shè)計(jì)者考慮的問題。兩個(gè)都能夠?qū)崿F(xiàn)設(shè)備文件的動(dòng)態(tài)創(chuàng)建,具體的實(shí)現(xiàn)方法也很類似。在嵌入式中是采用mdev實(shí)現(xiàn)類似udev的動(dòng)態(tài)創(chuàng)建設(shè)備文件,在制作文件系統(tǒng)的過程中應(yīng)該注意在linux system項(xiàng)選上mdev,不過一般默認(rèn)情況下都選擇上。

在驅(qū)動(dòng)中動(dòng)態(tài)添加設(shè)備文件節(jié)點(diǎn)會(huì)減少麻煩。
具體的實(shí)現(xiàn)主要包括兩個(gè)過程。
1、創(chuàng)建一個(gè)設(shè)備類,主要依據(jù)函數(shù)class_create()實(shí)現(xiàn)。
2、依據(jù)設(shè)備類創(chuàng)建一個(gè)設(shè)備文件,主要依據(jù)device_create()或者有些較低版本中的class_device_create()實(shí)現(xiàn)。

基本的實(shí)現(xiàn)過程應(yīng)該是在設(shè)備驅(qū)動(dòng)初始化過程中首先得到申請到設(shè)備號(hào)之后創(chuàng)建一個(gè)設(shè)備類,采用class_create()實(shí)現(xiàn)。
函數(shù)class_create()的形式如下:

    #define class_create(owner, name)        \
    ({                        \
        static struct lock_class_key __key;    \
        __class_create(owner, name, &__key);    \
    })

參數(shù)的意義:owner是指設(shè)備的擁有者,因此可以直接THIS_MODULE復(fù)制給owner,而name是設(shè)備類的名字。返回值是一個(gè)設(shè)備類的指針。這樣就創(chuàng)建了一個(gè)設(shè)備類。

    static struct class *myclass;
    ...
    static int memdev_init(void)
    {
      ...
     /*如果定義了主設(shè)備號(hào)采用靜態(tài)申請的方式*/
            if(mem_major)
                    result = register_chrdev_region(devno,2,"mem_dev");
            else/*動(dòng)態(tài)申請?jiān)O(shè)備號(hào)*/
            {
                    result = alloc_chrdev_region(&devno,0,2,"mem_dev");
                    mem_major = MAJOR(result);
            }
            /*錯(cuò)誤處理*/
            if(result < 0)
                    return result;

            /*在設(shè)備號(hào)申請完成以后可以為設(shè)備創(chuàng)建一個(gè)設(shè)備類,用于設(shè)備文件的創(chuàng)建*/
            myclass = class_create(THIS_MODULE,"memdev_class");
            /*創(chuàng)建一個(gè)設(shè)備*/

            /*初始化cdev,并將相關(guān)的文件操作添加進(jìn)來*/
            cdev_init(&cdev,&mem_fops);
       ...
    }

在設(shè)備初始化完成、綁定好文件操作、設(shè)備添加到內(nèi)核中以后然后根據(jù)設(shè)備類要?jiǎng)?chuàng)建設(shè)備文件,依據(jù)device_create實(shí)現(xiàn)函數(shù),其中的函數(shù)形式如下實(shí)現(xiàn)。

    struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
    {
        va_list vargs;
        struct device *dev;

        va_start(vargs, fmt);
        dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
        va_end(vargs);
        return dev;
    }

參數(shù)的意義分別是設(shè)備類指針、設(shè)備的父設(shè)備,設(shè)備號(hào)、以及設(shè)備的數(shù)據(jù)、然后是設(shè)備文件的名字,參數(shù)具有不定性。具體的實(shí)現(xiàn)如下:

    ...

           /*初始化cdev,并將相關(guān)的文件操作添加進(jìn)來*/
            cdev_init(&cdev,&mem_fops);
            /*設(shè)備引用*/
            cdev.owner = THIS_MODULE;
            cdev.ops = &mem_fops;
            /*注冊字符設(shè)備*/
            cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
            /*以上設(shè)備添加完成*/

            /*分配兩個(gè)內(nèi)存空間,此處是在物理內(nèi)存上實(shí)現(xiàn)分配,實(shí)質(zhì)是創(chuàng)建兩個(gè)設(shè)備的描述*/
            mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
            if(!mem_devp)/*出錯(cuò)的相應(yīng)操作*/
            {
                    result = -ENOMEM;
                    /*錯(cuò)誤處理,采用典型的goto語句*/
                    goto fail_malloc;
            }

            /*清除空間*/
            memset(mem_devp,0,sizeof(struct mem_dev));

            for(i = 0; i < MEMDEV_NR_DEVS; ++i)
            {
                    device_create(myclass,NULL,MKDEV(mem_major,i),NULL,"memdev%d",i);
                    /*
                    myclass為設(shè)備類
                    NULL 表示父設(shè)備為空
                    MKDEV(mem_major,i) 表示設(shè)備號(hào)
                    NULL 表示設(shè)備數(shù)據(jù)為空
                    后面的參數(shù)是用來設(shè)置 設(shè)備文件的名字
                    */
                    mem_devp[i].size = MEMDEV_SIZE;
                    /*對設(shè)備的數(shù)據(jù)空間分配空間*/
                    mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
                    /*問題,沒有進(jìn)行錯(cuò)誤的控制*/
                    memset(mem_devp[i].data,0,MEMDEV_SIZE);

                    /*初始化定義的互信息量*/
                    //mutex_init(&mem_devp[i].sem);
                    //初始化定義的自旋鎖ua
                    spin_lock_init(&mem_devp[i].lock);
            }
    ...

以上的操作都是在模塊初始化過程中完成的。這樣在加載過程中就會(huì)在/dev目錄下添加好設(shè)備文件。在設(shè)備退出過程中我們當(dāng)然也要釋放分配好的這些資源。具體的采用device_destroy釋放分配好的設(shè)備文件,

    void device_destroy(struct class *class, dev_t devt)
    {
        struct device *dev;

        dev = class_find_device(class, NULL, &devt, __match_devt);
        if (dev) {
            put_device(dev);
            device_unregister(dev);
        }
    }

參數(shù)主要是設(shè)備類和設(shè)備號(hào)。
同時(shí)也要釋放設(shè)備類。主要采用函數(shù)class_destroy()

    void class_destroy(struct class *cls)
    {
        if ((cls == NULL) || (IS_ERR(cls)))
            return;

        class_unregister(cls);
    }

參數(shù)是設(shè)備類。
設(shè)備的退出過程如下:

    /*模塊清除函數(shù)*/
    static void memdev_exit(void)
    {
            cdev_del(&cdev);/*注銷字符設(shè)備*/
            /*釋放兩個(gè)物理內(nèi)存*/

            kfree(mem_devp[0].data);
            kfree(mem_devp[1].data);
            device_destroy(myclass,MKDEV(mem_major,0));
            device_destroy(myclass,MKDEV(mem_major,1));

            kfree(mem_devp);/*釋放設(shè)備結(jié)構(gòu)體內(nèi)存*/

            class_destroy(myclass);
            unregister_chrdev_region(MKDEV(mem_major,0),2);
    }

基本的形式如上所示。
驅(qū)動(dòng)的出錯(cuò)順序與錯(cuò)誤處理順序應(yīng)該是一個(gè)相反的過程,這樣才能保證區(qū)域的包含關(guān)系。由于設(shè)備類的創(chuàng)建過程是在設(shè)備號(hào)申請的后面完成,因此釋放應(yīng)該在設(shè)備號(hào)釋放之前注銷掉。因此形成一個(gè)先進(jìn)后處理的關(guān)系,類似于一個(gè)堆棧的形式。
測試過程:
[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem 1, 1 Dec  5 12:55 /dev/mem
[gong@Gong-Computer mem_waitqueue]$ sudo insmod memwait_queue.ko
[sudo] password for gong:
[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem   1, 1 Dec  5 12:55 /dev/mem
crw------- 1 root root 555, 0 Dec  5 16:50 /dev/memdev0
crw------- 1 root root 555, 1 Dec  5 16:50 /dev/memdev1
[gong@Gong-Computer mem_waitqueue]$ sudo rmmod memwait_queue
[gong@Gong-Computer mem_waitqueue]$ ls -al /dev/mem*
crw-r----- 1 root kmem 1, 1 Dec  5 12:55 /dev/mem

以上的結(jié)果表明,采用上面的方式能夠自動(dòng)的創(chuàng)建設(shè)備文件,相比手動(dòng)創(chuàng)建更加的方便自如。

關(guān)閉窗口

相關(guān)文章