前言
什么是新字符设备呢?那我还是先说说什么是老的字符设备驱动吧。传统的字符设备驱动开发就是使用函数 register_chrdev注册字符设备,注册完成把驱动模块加载进内核后,还需要手动使用 mknod 命令创建设备节点。这就非常地不方便,这需要我们知道哪些设备号可以使用,还会浪费掉次设备号。(因为一个设备只用一个主设备号,则其下的所有此设备号都为它使用了)。于是就出现了新字符设备驱动了,这个提供的api可以让系统自动分配设备号,这样子在我们加载完成模块驱动后,自动就会在/dev上面挂着设备节点以供我们操作了。
在这里我介绍一下使用新字符设备的几个步骤:
1.定义设备结构体
struct newchrled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
首先定义好设备结构体用于描述设备信息。
2.定义字符设备结构
在内核中已经定义好了如下结构体
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
编写字符设备驱动之前需要定义一个 cdev 结构体变量:struct cdev test_cdev;
3.初始化的 cdev 结构体变量
函数原型是:void cdev_init(struct cdev *cdev, const struct file_operations *fops)
struct cdev testcdev;
/* 设备操作函数 */
static struct file_operations test_fops = {
.owner = THIS_MODULE,
/* 其他具体的初始项 */
};
testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops); /* 初始化 cdev 结构体变量*/
使用cdev_init函数把设备文件操作函数file_operations 和testcdev初始化。
4.向 Linux 系统添加字符设备
函数原型:int cdev_add(struct cdev *p, dev_t dev, unsigned count)
cdev_init(&testcdev, &test_fops); /* 初始化 cdev 结构体变量 */
cdev_add(&testcdev, devid, 1); /* 添加字符设备 */
在init结构体之后,需要向内核里添加字符设备。
cdev_del(&testcdev);
5.创建类
自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在 cdev_add 函数后面添加自动创建设备节点相关代码。首先要创建一个 class 类, class 是个结构体,定义在文件include/linux/device.h 里面。
函数原型:struct class *class_create (struct module *owner, const char *name)
class = class_create(THIS_MODULE, "xxx");
6.创建设备
创建好类以后还不能实现自动创建设备节点,我们还需要在这个类下创建一个设备。使用 device_create 函数在类下面创建设备,函数原型:
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, …)
device = device_create(class, NULL, devid, NULL, "xxx");
7.在出口函数注销掉上面全部创建。
void cdev_del(struct cdev *p)
void class_destroy(struct class *cls);
void device_destroy(struct class *class, dev_t devt)
使用如上函数注销,参数与注册时一致。