1、申请设备号
#include <linux/fs.h> int register_chrdev_region(dev_t first, unsigned int count, char *name);
静态分配字符设备号,从fist开始的count个,name为设备名称(name会出现在/proc/devices和sysfs中),成功返回0,失败返回一个负的错误码 int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
动态分配字符设备号,主设备号动态分配,次设备号从firstminor开始的count个,name为设备名称(动态分配的主设备号可以在/proc/devices中获取) void unregister_chrdev_region(dev_t first, unsigned int count);
注销字符设备号,从first开始的count个
2、初始化字符设备
#include <linux/cdev.h> 动态创建
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops; 静态创建
void cdev_init(struct cdev *cdev, struct file_operations *fops);
3、添加和删除字符设备
#include <linux/cdev.h> 添加字符设备
int cdev_add(struct cdev *dev, dev_t num, unsigned int count); 删除字符设备
void cdev_del(struct cdev *dev);
4、关键数据结构
4.1、file_operations结构体
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 (*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
};
4.2、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())); /* lest something weird decides that 2 is OK */
4.3、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_io_list; /* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
struct bdi_writeback *i_wb; /* the associated cgroup wb */ /* foreign inode detection, see wbc_detach_inode() */
int i_wb_frn_winner;
u16 i_wb_frn_avg_time;
u16 i_wb_frn_history;
#endif
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;
char *i_link;
}; __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 */
};
Linux中一切皆文件,对于字符设备驱动而言,都是通过设备文件进行交互的。Linux内核中,针对每一个文件有一个唯一的inode结构体描述,但是一个文件可以打开多次,所以内核使用file结构体作为文件描述符,每次打开同一个文件,均生成一个新的文件描述符,但是所有的文件描述符都指向同一个inode,表示打开的是同一个文件。inode和file结构体中均有file_operation结构体成员,它就是指向驱动实现的file_operations结构体,为文件操作提供具体实现函数。
5、container_of宏
#include <linux/kernel.h> container_of(pointer, container_type, container_field);
6、open和release
6.1、int open(struct inode *inode, struct file *filp);
open函数中应当进行以下工作:
1)检查设备特定的错误(如设备未准备好,或者硬件错误)
2)如果第一次打开,初始化设备
3)如果需要,更新f_op指针
4)分配并填充要放入filp_private_data的任何数据结构
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* contain cdev member */ dev = contailer_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; return ;
}
6.2、int release(struct inode *inode, struct file *filp);
release函数应当进行以下工作:
1)释放open分配在filp->private_data中的任何东西
2)在最后close关闭设备
int scull_release(struct inode *inode, struct file *filp)
{
return ;
}
7、read和write
#include <linux/fs.h> ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
8、ioctl
#include <linux/ioctl.h> long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
long (*compat_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg); cmd - 操作码(四字节,type|nr|direction|size)
类型:魔数,通常为一个字符,表示特定设备,自定义
序号:命令序号
方向:__IOC_NONE|__IOC_READ|__IOC_WRITE
大小:数据结构大小 根据方向,可以使用_IO(type,nr)
_IOR(type,nr,datatype)
_IOW(type,nr,datatype)
组合生成cmd操作码,size成员通过sizeof(datatype)获取 反之,可以通过宏解析出cmd操作码的不同字段:
类型:_IOC_TYPE(cmd)
方向:_IOC_DIR(cmd)
序号:_IOC_NR(cmd)
大小:_IOC_SIZE(cmd)
arg - 可选参数(可以是一个整数或者一个指针) 返回值:失败返回-ENOTTY或者-ENIVAL给用户 ioctl函数注册时,需要注意按照以下形式注册:
struct file_operations globalvar_fops = {
.owner = THIS_MODULE,
.open = globalvar_open,
.release = globalvar_release,
.read = globalvar_read,
.write = globalvar_write,
.unlocked_ioctl = globalvar_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = globalvar_ioctl,
#endif
};
9、access_ok()
#include <asm/uaccess.h> 检查用户空间地址是否可以存取,成功返回1,失败返回0,失败时,应该给调用者返回-EFAULT错误码
int access_ok(int type, const void *addr, unsigned long size); type: VERIFY_READ|VERIFY_WRITE
addr: 用户空间地址
size: 大小