自動(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)建更加的方便自如。