字符设备驱动程序原理

版权声明 本文为博主随手笔记,欢迎评论和转载 https://mp.csdn.net/mdeditor/84987796

驱动是具有入口和出口的一组方法的集合,这一组方法才是驱动的核心内容。
对于字符设备驱动程序,最核心的就是 file_operation 结构,这个结构实际上是提供给虚拟文件系统 [ VFS ] 的文件接口,它的每一个成员函数一般都对应一个系统调用。用户进程利用系统调用对设备文件进行诸如操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序。

可根据这篇 https://blog.csdn.net/qq_41825384/article/details/84955103 字符设备驱动来理解本文的讲解,并根据本文的讲解来改用 cdev 结构进行写驱动。

看图来理清一下思路 :
字符设备驱动程序原理_第1张图片
字符设备驱动程序原理_第2张图片

理解 : 系统调用通过设备文件的主设备号找到相应的设备驱动程序
从下图中,/dev 目录下的设备文件可知,agpgart 的主设备号为 10 次设备号为 175
而且设备文件中,主设备号时可以相同的,次设备号的唯一的。
主设备号跟次设备号就可以用来识别一个设备文件。像通过身份证号码就可以识别一个人一样。
首字符c b 是标识符, c 表示字符设备, b 表示块设备
字符设备驱动程序原理_第3张图片

file_operations结构

摘自 Linxu 4.17.14,,在 /include/linux/fs.h中定义。用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的 事务的函数的地址。

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	int (*iterate_shared) (struct file *, struct dir_context *);
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
	ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,loff_t, size_t, unsigned int);
	int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,u64);
	ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,u64);
} __randomize_layout;

file_opration 简单的运用 :
应用层系统调用与驱动层 struct file_operation fops 的调用核心。

    struct file_operations fops = {
       .read = device_read,
       .write = device_write,
       .open = device_open,
       .release = device_release
    };

注册字符设备的机制

{

使用 register_chrdev 注册字符设备

注册字符设备函数 :int register_chrdev(unsigned int major, char *name, struct file_operations *fops);

参数 major 如果等于 0,则表示采用系统动态分配的主设备号。如果不等于 0,则需要通过查询未用的设备号来为函数传参数

使用 register_chrdev 注销字符设备

注销字符设备函数 : int unregister_chrdev(int major, const char *name);

}

使用 cdev_add 注册字符设备

{

在 Linux 内核中的字符设备用 cdev 结构来描述,其结构如下,摘自 Linxu 4.17.14

struct cdev {
	struct kobject kobj;
	struct module *owner;                //所属模块,一般为 THIS_MODULE
	const struct file_operations *ops;   //文件操作结构,即上文中的 file_operations 结构
	struct list_head list;
	dev_t dev;                           //设备号
	unsigned int count;
} __randomize_layout;

对 cdev 结构的操作,内核提供了一组函数

void cdev_init(struct cdev *, const struct file_operations *);    //初始化一个 cdev 结构
struct cdev *cdev_alloc(void);                                    //为 cdev 结构分配内存
void cdev_put(struct cdev *p);					   //减少使用计数
int cdev_add(struct cdev *, dev_t, unsigned);		  //注册设备,通常发生在驱动模块的加载函数中
void cdev_set_parent(struct cdev *p, struct kobject *kobj);	
int cdev_device_add(struct cdev *cdev, struct device *dev);
void cdev_device_del(struct cdev *cdev, struct device *dev);
void cdev_del(struct cdev *);				        //注销设备,通常发生在驱动模块的卸载函数中
void cd_forget(struct inode *);

使用 cdev 结构注册设备的一般过程如下。
以动态分配设备号为例:
申请设备号
分配设备结构
初始化设备结构
添加字符设备

无论是用 register_chrdev 还是用 cdev 注册字符设备都是可以的。 register_chrdev机制出现在 linux 2.6之前的内核版本中,而 cdev 出现在 linux 2.6及之后的内核版本中,不过 linux 2.6及之后的内核版本依然兼容 register_chrdev 机制。
}

字符设备的读写

{
在 Linux 有个最核心的思想,一切皆为文件。字符设备依然不例外。字符设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。
字符设备驱动程序原理_第4张图片
最后根据 xxx_read(),xxx_write() 内容进行硬件操作。’
}

你可能感兴趣的:(Linux驱动编程,Linux,字符驱动原理,驱动原理讲解)