設備文件是非常重要的文件,是應用程序與設備驅動交換數據,控制硬件的橋樑。在驅動程序中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_create()實現。
class_create(owner, name)
參數的意義:owner是指設備的擁有者,因此可以直接THIS_MODULE複製給owner,而name是設備類的名字。返回值是一個設備類的指針。這樣就創建了一個設備類。
設備添加到內核中以後然後根據設備類要創建設備文件,依據device_create實現函數,其中的函數形式如下實現。
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
參數的意義分別是設備類指針、設備的父設備,設備號、以及設備的數據、然後是設備文件的名字,參數具有不定性。
在設備退出過程中我們當然也要釋放分配好的這些資源。具體的採用device_destroy釋放分配好的設備文件,
- void device_destroy(struct class *class, dev_t devt)
參數主要是設備類和設備號。
同時也要釋放設備類。主要採用函數class_destroy()
- void class_destroy(struct class *cls)
參數是設備類。
/**
* 動態創建設備文件
* Lzy 2012\7\24
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include "hello.h"
/************************* 設備號 ******************************/
int mem_major = MEMDEV_MAJOR; /* 定義主設備號 */
module_param(mem_major, int, S_IRUGO); /* 主設備號設置爲模塊參數 */
int mem_minor = MEMDEV_MINOR; /* 定義次設備號 */
module_param(mem_minor, int, S_IRUGO); /* 次設備號設置爲模塊參數 */
struct mem_dev *my_devices;
static struct class *myclass;
struct file_operations my_fops =
{
.owner = THIS_MODULE,
};
/**
* 註冊字符設備
*/
static void mem_init_cdev(struct mem_dev *dev, int index)
{
int err, devno = MKDEV(mem_major, mem_minor + index);
cdev_init(&dev->cdev, &my_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_fops;
err = cdev_add (&dev->cdev, devno, 1); /* */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
static int mem_module_init(void)
{
int ret,i;
dev_t mem_dev; /* 聲明設備號 */
/************ 註冊設備號 ****************/
if(mem_major)
{
mem_dev = MKDEV(mem_major,mem_minor); /* 獲得設備號 */
ret = register_chrdev_region(mem_dev, MEMDEV_NR_DEVS, MEMDEV_NAME); /* 靜態註冊設備號 */
}
else
{
ret = alloc_chrdev_region(&mem_dev, mem_minor, MEMDEV_NR_DEVS, MEMDEV_NAME); /* 動態註冊設備號 */
mem_major = MAJOR(mem_dev); /* 獲得主設備號 */
}
if(ret < 0) /* 判斷設備號註冊是否成功 */
{
printk(KERN_WARNING "scull: can't get major %d\n", mem_major);
return ret;
}
/*在設備號申請完成以後可以爲設備創建一個設備類,用於設備文件的創建*/
myclass = class_create(THIS_MODULE,"memdev_class"); // 在/sys/class目錄下生成memdev_class目錄
/****************** 爲設備分配空間 *****************************/
my_devices = kmalloc((MEMDEV_NR_DEVS * sizeof(struct mem_dev)), GFP_KERNEL);
if (!my_devices)
{
ret = -ENOMEM;
goto fail;
}
memset(my_devices, 0, MEMDEV_NR_DEVS * sizeof(struct mem_dev));
/************************ 設備初始化 **************************************/
for(i=0; i<MEMDEV_NR_DEVS;i++)
{
my_devices[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
memset(my_devices[i].data, 0, MEMDEV_SIZE);
mem_init_cdev(&my_devices[i], i); /* 設備增加至系統 */
/**
* myclass爲設備類
* NULL 表示父設備爲空
* MKDEV(mem_major,i) 表示設備號
* NULL 表示設備數據爲空
* 後面的參數是用來設置 設備文件的名字
*/
device_create(myclass,NULL,MKDEV(mem_major,mem_minor+i),NULL,"memdev%d",i); // 創建設備文件
}
return ret;
fail:
unregister_chrdev_region(MKDEV(mem_major,mem_minor), MEMDEV_NR_DEVS); /* 註銷設備號 */
return ret;
}
static void mem_module_exit(void)
{
int i;
for(i=0; i<MEMDEV_NR_DEVS;i++)
{
cdev_del(&(my_devices[i].cdev)); /* 從系統去除字符設備 */
kfree(my_devices[i].data);
device_destroy(myclass,MKDEV(mem_major,mem_minor+i)); // 釋放分配好的設備文件
}
kfree(my_devices);
class_destroy(myclass); // 釋放設備類
unregister_chrdev_region(MKDEV(mem_major,mem_minor), MEMDEV_NR_DEVS); /* 註銷設備號 */
}
module_init(mem_module_init);
module_exit(mem_module_exit);
MODULE_LICENSE("GPL"); /* 模塊許可證 */
MODULE_AUTHOR("Lzy"); /* 作者聲明 */
MODULE_DESCRIPTION("memdev module"); /* 模塊描述 */
MODULE_VERSION("V1.0"); /* 模塊版本聲明 */