APP调用open函数时会返回一个整数,我们通过这个整数找到对应的一个Struct file结构体,在open中的参数flag和mode也会传入结构体,结构体中还有一个struct file_operations *f_op结构体,这个是由内核提供的,我们可以在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 *);
unsigned int (*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 *);
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);
应用程序去打开某个设备节点时候,根据设备结点的主设备号,在内核的chrdevs数组中找到一个file_operations结构体这个结构体里就有open函数等等。当我们调用读写等函数的时候就会用到这里面的函数。
第一步我们应该确定主设备号,也可以让内核分配,在注册驱动程序函数中,majior传入0让可以内核分配。
第二步定义自己的file_operations结构体,在GCC中这个结构体有一个特定的语法。
static struct file_operations hello_drv = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
第三步,实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体。
第四步,此时我们需要去在内核中注册驱动程序,在misc.c中有入口出口函数。在入口函数中我们需要创建设备节点(需要定义一个类)。入口函数中需要在函数中我们需要使用到以下函数去注册驱动程序和创建销毁驱动程序。
(驱动注册进内核是用主设备号+次设备号标识的。 虚拟文件系统在创建设备节点时dev/xx时也会得到这个标识。 所以你打开文件时,就通过这个标识就能找到了对应的操作。 为了更方便应用,内核会为每个进程维护一个数据结构,里面存放文件描述符,每个文件描述符对应一个file结构体,当这个file结构体被创建后,就可以直接通过文件描述符来操作文件了。)
static int __init misc_init(void) //入口函数
static void __exit misc_init(void) //出口函数
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops) //返回主设备号
static inline void unregister_chrdev(unsigned int major, const char *name)
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
void device_destroy(struct class *class, dev_t devt)
static struct class *misc_class; //定义一个类
misc_class = class_create(THIS_MODULE, "misc");
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
第五步,提供设备信息,自动创建设备节点:
class_create();
device_create();
MODULE_LICENSE("GPL");