字符设备包含:设备号(dev_t),设备(cdev),file_operation 。
创建一个字符设备的流程:
0 创建一个字符设备,可以是静态定义或者动态申请;
1 首先要得到一个设备号,可以是静态定义或者动态申请;
2 把写好的file_operation 并保存到 cdev,实现cdev的初始化;
3 使用cdev_add()注册cdev,告诉内核;
4 创建设备节点,以便后面调用 file_operation接口;
static struct cdev chrdev; //第一种方式 静态定义
struct cdev *cdev_alloc(void); //第二种方式 动态申请 返回一个struct cdev类型的指针
//从内核中移除某个字符设备,
void cdev_del(struct cdev *p)
要得到一个设备号,也分为静态定义或者动态申请,但是涉及函数比较多。
有:register_chrdev_region,alloc_chrdev_region,register_chrdev。
//内核源码/fs/char_dev.c
int register_chrdev_region(dev_t from, unsigned count, const char *name);
参数:
//内核源码/fs/char_dev.c
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char name);
相较于静态定义,需要自己管理设备号,可能造成设备号冲突得缺点,动态分配由内核自己分配一个尚未使用得主设备号,可以解决这个问题。
参数:
我们可以通过命令“cat /proc/devices”查询内核分配的主设备号。
该函数是一个内联函数,它不仅支持静态申请设备号,也支持动态申请设备号,并将主设备号返回,函数原型如下所示:
//内核源码/include/linux/fs.h 文件
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
此函数的参数功能包含了,申请设备号,动态申请cdev,把file_operations保存到cdev。可以说,一个函数完成了这么多步骤。但是同一类字符设备(即主设备号相同),会在内核中申请了 256 个,通常情况下,我们不需要用到这么多个设备,这就造成了极大的资源浪费。
使用 register_chrdev 函数申请的设备号,则应该使用 unregister_chrdev 函数进行注销。
//内核源码/include/linux/fs.h
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
对于使用 register_chrdev_region 函数以及 alloc_chrdev_region 函数分配得到的设备编号,可以使用 unregister_chrdev_region 函数把分配的设备编号交还给内核。
编写一个字符设备最重要的是实现file_operations中的函数。
实现之后,将改结构体和字符设备关联起来。
//内核源码/fs/char_dev.c
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
函数参数和返回值如下:
参数:
//内核源码/fs/char_dev.c
//cdev_add 函数用于向内核的 cdev_map 散列表添加一个新的字符设备
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
函数参数和返回值如下:
参数:
从系统中删除 cdev,cdev 设备将无法再打开,但任何已经打开的 cdev 将保持不变,即使在 cdev_del 返回后,
它们的 FOP 仍然可以调用。
//内核源码/fs/char_dev.c
void cdev_del(struct cdev *p)
创建一个设备并将其注册到文件系统
//定义一个类指针
static struct class *chr_dev_class;
//内核源码/drivers/base/core.c
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
函数参数和返回值如下:
参数:
返回值:成功时返回 struct device 结构体指针, 错误时返回 ERR_PTR().
删除使用 device_create 函数创建的设备
//内核源码/drivers/base/core.c
void device_destroy(struct class *class, dev_t devt)
除了使用代码创建设备节点,还可以使用 mknod 命令创建设备节点。这里不展开。
如:mkmod /dev/test c 2 0
创建一个字符设备/dev/test,其主设备号为 2,次设备号为 0。
原文:https://www.cnblogs.com/Rainingday/p/14295943.html