自動創建設備文件
設備文件是非常重要的文件,是應用程序與設備驅動交換數據,控制硬件的橋梁。在驅動程序中open、release的實現過程中其中的一個參數struct inode實質就是設備文件的索引,沒有這個索引也就沒有后期的各種操作,通常設備文件也被稱為設備文件節點。因此沒有設備文件后期的各種實現都是多余的。設備文件的創建有兩種方法,其中就是在創建文件系統過程中用到的mknod命令。
該命令的形式如下:
mknod filename (type,c,b,l) 主設備號 次設備號
其中type說明是那一類設備(字符設備c,塊設備b,套接字l),主設備號用來確定那一類設備,而次設備號主要用來確定這一類設備中的某一個設備。
例如:mknod memdev0 c 555 0 就是創建了一個主設備號為555,次設備號為0的字符設備。
這種方法比較快速,但是在編寫設備驅動的時候很難確定那個設備號是可以使用的,因此很不方便開發。在2.4內核中引入了devfs,但是因為性能等方面的原因,在2.6內核中被udev逐漸取代。udev的設備命名策略、權限控制和事件處理都是在用戶態下完成的,它利用sysfs中的信息來進行創建設備文件節點等工作。其實對于我們寫程序而言并沒有多大的區別,這是內核的設計者考慮的問題。兩個都能夠實現設備文件的動態創建,具體的實現方法也很類似。在嵌入式中是采用mdev實現類似udev的動態創建設備文件,在制作文件系統的過程中應該注意在linux system項選上mdev,不過一般默認情況下都選擇上。
在驅動中動態添加設備文件節點會減少麻煩。
具體的實現主要包括兩個過程。
1、創建一個設備類,主要依據函數class_create()實現。
2、依據設備類創建一個設備文件,主要依據device_create()或者有些較低版本中的class_device_create()實現。
基本的實現過程應該是在設備驅動初始化過程中首先得到申請到設備號之后創建一個設備類,采用class_create()實現。
函數class_create()的形式如下:
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
參數的意義:owner是指設備的擁有者,因此可以直接THIS_MODULE復制給owner,而name是設備類的名字。返回值是一個設備類的指針。這樣就創建了一個設備類。
static struct class *myclass;
...
static int memdev_init(void)
{
...
/*如果定義了主設備號采用靜態申請的方式*/
if(mem_major)
result = register_chrdev_region(devno,2,"mem_dev");
else/*動態申請設備號*/
{
result = alloc_chrdev_region(&devno,0,2,"mem_dev");
mem_major = MAJOR(result);
}
/*錯誤處理*/
if(result < 0)
return result;
/*在設備號申請完成以后可以為設備創建一個設備類,用于設備文件的創建*/
myclass = class_create(THIS_MODULE,"memdev_class");
/*創建一個設備*/
/*初始化cdev,并將相關的文件操作添加進來*/
cdev_init(&cdev,&mem_fops);
...
}
在設備初始化完成、綁定好文件操作、設備添加到內核中以后然后根據設備類要創建設備文件,依據device_create實現函數,其中的函數形式如下實現。
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;
}
參數的意義分別是設備類指針、設備的父設備,設備號、以及設備的數據、然后是設備文件的名字,參數具有不定性。具體的實現如下:
...
/*初始化cdev,并將相關的文件操作添加進來*/
cdev_init(&cdev,&mem_fops);
/*設備引用*/
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注冊字符設備*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*以上設備添加完成*/
/*分配兩個內存空間,此處是在物理內存上實現分配,實質是創建兩個設備的描述*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)/*出錯的相應操作*/
{
result = -ENOMEM;
/*錯誤處理,采用典型的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為設備類
NULL 表示父設備為空
MKDEV(mem_major,i) 表示設備號
NULL 表示設備數據為空
后面的參數是用來設置 設備文件的名字
*/
mem_devp[i].size = MEMDEV_SIZE;
/*對設備的數據空間分配空間*/
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
/*問題,沒有進行錯誤的控制*/
memset(mem_devp[i].data,0,MEMDEV_SIZE);
/*初始化定義的互信息量*/
//mutex_init(&mem_devp[i].sem);
//初始化定義的自旋鎖ua
spin_lock_init(&mem_devp[i].lock);
}
...
以上的操作都是在模塊初始化過程中完成的。這樣在加載過程中就會在/dev目錄下添加好設備文件。在設備退出過程中我們當然也要釋放分配好的這些資源。具體的采用device_destroy釋放分配好的設備文件,
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);
}
}
參數主要是設備類和設備號。
同時也要釋放設備類。主要采用函數class_destroy()
void class_destroy(struct class *cls)
{
if ((cls == NULL) || (IS_ERR(cls)))
return;
class_unregister(cls);
}
參數是設備類。
設備的退出過程如下:
/*模塊清除函數*/
static void memdev_exit(void)
{
cdev_del(&cdev);/*注銷字符設備*/
/*釋放兩個物理內存*/
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);/*釋放設備結構體內存*/
class_destroy(myclass);
unregister_chrdev_region(MKDEV(mem_major,0),2);
}
基本的形式如上所示。
驅動的出錯順序與錯誤處理順序應該是一個相反的過程,這樣才能保證區域的包含關系。由于設備類的創建過程是在設備號申請的后面完成,因此釋放應該在設備號釋放之前注銷掉。因此形成一個先進后處理的關系,類似于一個堆棧的形式。
測試過程:
[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
以上的結果表明,采用上面的方式能夠自動的創建設備文件,相比手動創建更加的方便自如。