免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 807 | 回复: 0
打印 上一主题 下一主题

字符设备驱动程序 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-18 17:35 |只看该作者 |倒序浏览

  字符设备驱动程序
三个内核结构体: 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的成员,并建立cdevfile_operations之间的连接,其源代码如下:
void cdev_init(struct cdev *cdev,struct file_operations *fops)
{
    memset(cdev,0,sizeof *cdev);//
对字符设备进行清零操作
    INIT_LIST_HEAD(&cdev->list);  //
这个宏定义的功能是使得结构体list_headnextprev都指向自身
    cdev->kobj.ktype=&ktype_cdev_default;            
    kobject_init(&cdev->kobj);          //
进行kobject的初始化,在今后的驱动程序设计中,这是一个必不可少的工作。
    cdev->ops=fops;  //
将传入的文件操作结构体指针赋值给cdevops
}

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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP