cdev是linux用来管理字符设备的结构体,其在内核中采用数组结构设计,这样系统中有多少个主设备号就约定了数组大小,此设备号采用链表管理,同一主设备号下可以有多个子设备。设备即文件,上层应用要访问设备,必须通过文件,cdev中包含file_operations结构体,该结构体就是驱动的文件操作集合。
cdev.h位于 /usr/src/linux-headers-4.15.0-55/include/linux
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H
#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
#include <linux/device.h>
struct file_operations;
struct inode;
struct module;
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
} __randomize_layout;
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
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 *);
#endif
在开始编写驱动前,还需要学习一下驱动程序操作时一个重要的数据结构,file_operations。
在获取了一些设备编号后,我们还没有将任何驱动程序操作连接到这些编号,file_operations结构就是用来建立这种连接的。
位置: /usr/src/linux-headers-4.15.0-55/include/linux
struct iov_iter;
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 *);
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 (*setfl)(struct file *, unsigned long);
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;
struct file_operations {
struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES
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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作
int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
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 *, struct dentry *, int datasync); //刷新待处理的数据
int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据
int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化
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 **);
};
__user字符
它其实是一种形式的文档而已,表明指针是一个用户空间地址,因此不能被直接引用。对通常的编译来将,__user没有任何效果,但是可由外部检查软件使用,用来寻找对用户空间的错误使用。