自动创建设备节点

创建设备文件的方法:

第一种:使用mknod手工创建,mknod filename type major minor

第二种:自动创建设备节点,利用udev来实现设备文件的自动创建

 

udev介绍

udev 运行在用户模式,而非内核中。udev 的初始化脚本在系统启动时创建设备节点,并且当插入新设备——加入驱动模块——在sysfs上注册新的数据后,udev会创建新的设备节点。

udev 是一个工作在用户空间的工具,它能根据系统中硬件设备的状态动态的更新设备文件,包括设备文件的创建,删除,权限等。这些文件通常都定义在/dev 目录下,但也可以在配置文件中指定。udev 必须内核中的sysfs和tmpfs支持,sysfs 为udev 提供设备入口和uevent 通道,tmpfs 为udev 设备文件提供存放空间。

注意,udev 是通过对内核产生的设备文件修改,或增加别名的方式来达到自定义设备文件的目的。但是,udev 是用户模式程序,其不会更改内核行为。也就是说,内核仍然会创建sda,sdb等设备文件,而udev可根据设备的唯一信息来区分不同的设备,并产生新的设备文件(或链接)。而在用户的应用中,只要使用新产生的设备文件

自动创建设备节点:

在驱动用加入对udev 的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)创建对应的设备。

内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。

这样,加载模块的时候,用户空间中的udev会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点。

 

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
 
//#define MYMAJOR	200
#define MYCNT		1
#define MYNAME		"testchar"
 
static dev_t mydev;
static struct cdev test_cdev;
static struct class *test_class;

static int test_chrdev_open(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_open...\n");
	
	return 0;
}
 
static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release...\n");
	
	return 0;
}
 
ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	printk(KERN_INFO "test_chrdev_read...\n");
	
	return 0;
}
 
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
	printk(KERN_INFO "test_chrdev_write...\n");
 
	return 0;
}
 
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,
	
	.open		= test_chrdev_open,
	.release	= test_chrdev_release,
	.write 		= test_chrdev_write,
	.read		= test_chrdev_read,
};
 
static int __init chrdev_init(void)
{	
	int retval;
	
	printk(KERN_INFO "chrdev_init...\n");
 
	/* 分配主次设备号 */
/*
	mydev = MKDEV(MYMAJOR, 0);
	retval = register_chrdev_region(mydev, MYCNT, MYNAME);
	if (retval) {
		printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
		return -EINVAL;
	}
*/
	retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);
	if (retval < 0) 
	{
		printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);
		goto flag1;
	}
	printk(KERN_INFO "alloc_chrdev_region success\n");
	printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));
 
	/* 注册字符设备驱动 */
	cdev_init(&test_cdev, &test_fops);
	retval = cdev_add(&test_cdev, mydev, MYCNT);
	if (retval) {
		printk(KERN_ERR "Unable to cdev_add\n");
		goto flag2;
	}
	printk(KERN_INFO "cdev_add success\n");
 
	/* 创建设备类 */
	test_class = class_create(THIS_MODULE, "test_class");
	if (IS_ERR(test_class)) {
		printk(KERN_ERR "Unable to class_create\n");
		goto flag3;
	}
	
	/* 创建设备节点 */
	device_create(test_class, NULL, mydev, NULL, "test");

	return 0;
 
flag3:
	cdev_del(&test_cdev);

flag2:
	unregister_chrdev_region(mydev, MYCNT);
flag1:	
	return -EINVAL;
}
 
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit...\n");
 
	/* 销毁设备类节点 */
	device_destroy(test_class, mydev);
	class_destroy(test_class);

	/* 注销字符设备驱动 */
	cdev_del(&test_cdev);
	/* 注销申请的主次设备号 */
	unregister_chrdev_region(mydev, MYCNT);
}
 
module_init(chrdev_init);
module_exit(chrdev_exit);
 
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("lsm");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/86180163
今日推荐