创建设备文件的方法:
第一种:使用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"); // 描述模块的别名信息