设备文件是非常重要的文件,是应用程序与设备驱动交换数据,控制硬件的桥梁。在驱动程序中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"); /* 模块版本声明 */