字符设备驱动程序框架
设备号
一个字符设备或者块设备都有一个主设备号和次设备号。主设备和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序,次设备号用来表示使用该驱动程序的各设备。以32位机为例,其中高12位表示主设备号,低20位表示次设备号。
typedef u_long dev_t;
dev_t dev; /* 定义设备号 */
/* 设备号 */ #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
/* 主设备号 */ #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
/* 次设备号 */ #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
/* 查看设备号 */ cat /proc/devices
静态分配设备号和动态分配设备号
静态分配设备号是指驱动程序开发者,静态地指定一个设备号。
动态分配设备号是指有系统自动分配一个设备号供我们使用。
/* 静态申请设备号 */ int register_chrdev_region(dev_t from, unsigned count, const char *name)
/* 动态申请设备号 */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
/* 释放设备号 */ void unregister_chrdev_region(dev_t from, unsigned count)
字符设备结构体
字符设备结构体,该结构体是所有字符设备的抽象。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
字符设备文件操作集合
字符设备文件操作集合,该结构体是对设备进行操作的抽象结构体。
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 *);
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 (*mremap)(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 (*aio_fsync) (struct kiocb *, 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
};
inode结构体
内核中的文件表示使用inode结构体表示。
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino;
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink;
unsigned int __i_nlink;
};
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/* Misc */
unsigned long i_state;
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned long dirtied_time_when;
struct hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
u64 i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_marks;
#endif
void *i_private; /* fs or device private pointer */
};
file结构体
内核中打开的文件(文件实例)使用file结构体表示。
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
struct inode和struct file的区别在于,inode不跟踪文件的当前位置和当前模式,它只是帮助操作系统找到底层文件结构的内容(管道,目录,常规磁盘文件,字符设备/块设备文件等)。而struct file则是一个基本结构(它实际上持有一个指向struct inode的指针),它代表打开的文件,并且提供一组函数,它们与底层文件结构上执行的方法相关,这些方法包括open,write等。所有这一切都强化了UNIX系统的哲学:一切皆是文件。
换句话说,struct inode代表内核中的文件,struct file描述的是实际打开的文件。同一文件打开多次时,可能会有不同的文件描述符,但它们都指向同一个inode。
初始化字符设备
初始化字符设备,添加字符设备和删除字符设备
/* 初始化字符设备 */ void cdev_init(struct cdev *cdev, const struct file_operations *fops)
/* 添加字符设备 */ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
/* 删除字符设备 */ void cdev_del(struct cdev *p)
驱动程序与应用程序的数据交互函数
驱动程序与应用程序的数据交换函数
由于驱动程序与应用程序属于不同的地址空间,所以两者之间不能随意访问。必须使用内核提供的专用函数。
long copy_to_user(void __user *to, const void *from, long n)
long copy_from_user(void *to, const void __user *from, long n)
@x: Value to copy to user space.
@ptr: Destination address, in user space.
put_user(x, ptr)
get_user(x, ptr)