- 论坛徽章:
- 0
|
字符设备驱动程序
一 三个内核结构体: file_operations、file、inode。
定义在
A 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 (*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);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
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 *, struct dentry *, 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 **);
};
初始化:struct file_operations scull_fops={
.owner=THIS_MODULE,
.read=scull_read,
.llseek=scull_llseek,
.open=scull_open.
.release=scull_release,
.ioctl=scull_ioctl,
};
B file结构
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
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;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
C inode结构
struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino;
atomic_t i_count;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
u64 i_version;
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
blkcnt_t i_blocks;
unsigned int i_blkbits;
unsigned short i_bytes;
umode_t i_mode;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
cdev结构体:表示字符设备的内核的内部结构。即内核内部使用该结构体表示字符设备。
struct cdev
{
struct kobject kobj; //内嵌的kobject对象
struct module *owner; //所属模块
struct file_operations *ops; //文件操作结构体
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
字符设备的注册:
法一:
Void cdev_init(struct cdev *cdev,struct file_operations *fops)
cdev_init()函数用来初始化cdev的成员,并建立cdev和file_operations之间的连接,其源代码如下:
void cdev_init(struct cdev *cdev,struct file_operations *fops)
{
memset(cdev,0,sizeof *cdev);//对字符设备进行清零操作
INIT_LIST_HEAD(&cdev->list); //这个宏定义的功能是使得结构体list_head的next和prev都指向自身
cdev->kobj.ktype=&ktype_cdev_default;
kobject_init(&cdev->kobj); //进行kobject的初始化,在今后的驱动程序设计中,这是一个必不可少的工作。
cdev->ops=fops; //将传入的文件操作结构体指针赋值给cdev的ops
}
Struct cdev 某些字段的初始化
Int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
Void cdev_del(struct cdev *dev)
cdev_add()函数和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销。
对cdev_add()函数的调用通常发生在字符设备驱动模块加载函数中,而对cdev_del()函数的调用则通常发生在字符设备驱动模块卸载函数中。
下面是cdev_add()函数的定义:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
458{
459 p->dev = dev;
460 p->count = count;
461 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);//这个函数实现的是添加一个对应的cdev对象.
462}
下面死cdev_del()函数的定义:
476void cdev_del(struct cdev *p)
477{
478 cdev_unmap(p->dev, p->count); //这个函数主要实现的是删除一个cdev的功能
479 kobject_put(&p->kobj); //减少计数量
480}
struct cdev *cdev_alloc(void); //初始化cdev的成员
下面cdev_alloc()函数用于动态申请一个cdev内存,其源代码如下:
struct cdev *cdev_alloc(void)
{
struct cdev *p=kmalloc(sizeof(struct cdev),GFP_KERNEL);//分配cdev的内存;给 kmalloc 的第一个参数是要分配的块的大小.
//第 2 个参数, 分配标志, 非常有趣, 因为它以几个方式控制 kmalloc 的行为.
if(p){
memset(p,0,sizeof(struct cdev));
p->kobj.ktype=&ktype_cdev_dynamic;
/*
static struct kobj_type ktype_cdev_dynamic = {
.release = cdev_dynamic_release,
//这里的函数使用的是动态操作来释放存储空间。
//这个函数同上面的函数cdev_default_release有着微弱的区别,前者使用的静态操作,后者使用的是动态操作。
};
void cdev_put(struct cdev *p); //减少计数
法二
Int register_chrdev(unsigned int major,const char *name,struct file_operations *fops);
Int unregister_chrdev(unsigned int major,const char *name);
二 主次设备编号
主设备号标志设备对应的驱动程序;
次设备号用于正确确定设备文件所指的设备。
MAJOR(dev_t dev);
MINOR(dev_t dev);
MKDEV(int major,int minor);
分配和释放设备编号:
Int register_chrdev_region(dev_t first,unsigned int count,char *name );
成功返回0,失败返回负的错误码。
动态分配Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
Void unregister_chrdev_region(dev_t first,unsigned int count);
从inode结构获得主次设备号:
Unsigned int iminor(struct inode *inode);
Unsigned int imajor(struct inode *inode);
三 方法
Open方法:
1 检查设备特定的错误
2 如设备是首次打开,则对其初始化
3 如有必要,更新f_op指针
4 分配并置于filp->private_date你的数据结构
Struct scull_dev *dev;
Dev=container_of(inode->i_cdev,struct scull_dev,cdev);
Filp->private_date=dev;
如果利用register_chrdev注册的设备号,则:iminor(struct inode *inode);
Release方法:
1 释放由open分配的、保存在filp->private_data中的所有内容。
2 在最后一次关闭操作时关闭设备。
Read方法:
Write方法:
Ssize_t read(struct file *filp,char __user *buff,size_t count,,loff_t *offp);
Buff参数是用户空间的指针。内核代码不能直接应用其中的内容。
1 在内核模式中运行时,用户空间的指针可能是无效的。该地址可能根本无法被映射到内核空间,或者指向某些随机数据
2 用户空间的内存是分页的,而在系统调用被调用是,涉及到的内存可能根本就不在RAM中。对用户空间内存的直接引用将导致页错误。
3 指针可能由存在缺陷或恶意的用户程序提供。
在用户地址空间和内核地址空间之间进行整段数据的拷贝,可以由下列内核函数提供:
Unsigned long copy_to_user(void __user *to,const void *from,unsigned long count);
Unsigned long copy_from_user(void __user *to,const void *from,unsigned long count);
它们还检查指针是否有效,若无效,就不会进行拷贝。
若在拷贝过程中遇到无效地址,则仅仅会复制部分数据。
此时返回值是还需要拷贝的内存数量值,给用户返回-EFAULT。
向量操作的函数原型:
Ssize_t (*readv) (struct file *filp,const struct iovec *iov,unsigned long count,loff_t *paos);
Ssize_t (*writev) (struct file *filp,const struct iovec *iov,unsigned long count,loff_t *paos);
Count 是要操作的iovec结构的个数。
Sruct iovec(定义在)
{
Void __user *iov_base;
__kernel_size_t iov_len;
}
四 高级字符驱动
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/104179/showart_2055499.html |
|