在Linux中有两种创建字符设备的方法,一种是通过mknod手动进行设备文件创建,第二种是通过device_create函数进行设备文件创建。在驱动开发中常用第二种方式进行设备文件的创建。
- class_create和device_create
先来了解一下跟设备文件创建相关的两个函数。
class_create:在调用device_create前要先用class_create创建一个类。类这个概念在Linux中被抽象成一种设备的集合。类在/sys/class目录中。
Linux内核中有各种类,比如gpio、rtc、led等。
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
class_create这个函数使用非常简单,在内核中是一个宏定义。其中参数owner通常为THIS_MODULE,name为类名。
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;
}
device_create用于创建设备。
class:该设备依附的类
parent:父设备
devt:设备号(此处的设备号为主次设备号)
drvdata:私有数据
fmt:设备名。
device_create能自动创建设备文件是依赖于udev这个应用程序。udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。
下面就来写一个驱动程序并使用这两个函数来创建设备。
程序:
#define CHRDEV_MAJOR 240 // 主设备号
#define CHRDEV_MAION 0 // 次设备号
#define CHRDEV_COUNT 1 // 次设备号个数
#define CHRDEV_NAME "testchrdev"
struct led_cdev
{
struct cdev chrdevcdev;
int major;
dev_t dev;
struct class *led_dev_class;
};
static struct led_cdev leddev;
ssize_t chrdev_read (struct file *file, char __user *usr, size_t size, loff_t *loff)
{
printk("%s\r\n",__func__);
return 0;
}
int chrdev_open (struct inode *inode, struct file *file)
{
printk("%s\r\n",__func__);
return 0;
}
int chrdev_release (struct inode *inode, struct file *file)
{
printk("%s\r\n",__func__);
return 0;
}
struct file_operations fops =
{
.open = chrdev_open,
.read = chrdev_read,
.release = chrdev_release,
};
static int __init chrdev_init(void)
{
int ret = 0,error = 0;
struct device *devices;
error = alloc_chrdev_region(&leddev.dev,CHRDEV_MAION,CHRDEV_COUNT,CHRDEV_NAME); // 注册设备号
printk("MAJOR = %d MINOR = %d\r\n",MAJOR(leddev.dev),MINOR(leddev.dev));
if(error < 0){
printk("alloc_chrdev_region error\r\n");
ret = -EBUSY;
goto fail;
}
leddev.major = MAJOR(leddev.dev);
cdev_init(&leddev.chrdevcdev, &fops); // 绑定字符设备操作函数集
error = cdev_add(&leddev.chrdevcdev,leddev.dev,CHRDEV_COUNT); // 添加字符设备
if(error < 0){
printk("cdev_add error\r\n");
ret = -EBUSY;
goto fail1;
}
// 创建类,类名为testledclass
leddev.led_dev_class = class_create(THIS_MODULE, "testledclass");
if (IS_ERR(leddev.led_dev_class)){
printk("class_create error\r\n");
ret = -EBUSY;
goto fail2;
}
// 创建设备
devices = device_create(leddev.led_dev_class, NULL, MKDEV(leddev.major,0), NULL, "testled");
if(NULL == devices){
printk("device_create error\r\n");
ret = -EBUSY;
goto fail3;
}
return 0;
fail3:
class_destroy(leddev.led_dev_class);/* 删除类 */
fail2:
cdev_del(&leddev.chrdevcdev);/* 删除cdev */
fail1:
unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
fail:
return ret;
}
static void __exit chrdev_exit(void)
{
device_destroy(leddev.led_dev_class,MKDEV(leddev.major,0));/* 卸载设备 */
class_destroy(leddev.led_dev_class);/* 删除类 */
cdev_del(&leddev.chrdevcdev);/* 删除cdev */
unregister_chrdev_region(leddev.dev,CHRDEV_COUNT);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_DESCRIPTION("xxxxxx");
MODULE_AUTHOR("xxxxxx");
MODULE_LICENSE("GPL");
在程序中调用class_create创建一个类,类名为testledclass。然后在调用device_create自动创建设备文件,设备名为testled。加载驱动结果如下。
加载驱动后进入/sys/class目录中可以看到有一个名为testledclass的类。在/dev目录下可以找到一个名为testled的设备文件。然后我们也可以操作这个设备文件进行读写操作。