网站建议:179001057@qq.com

字符设备驱动程序

技术2022-05-11  1

  字符设备驱动程序

三个内核结构体: file_operationsfileinode

定义在<linux/fs.h>

 

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;};

字符设备的注册:<linux/cdev.h>

法一:

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);

主次设备编号

主设备号标志设备对应的驱动程序;

次设备号用于正确确定设备文件所指的设备。

<linux/types.h> <linux/kdev_t.h>

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注册的设备号,则:iminorstruct 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(定义在<linux/uio.h>)

{

Void __user *iov_base;

__kernel_size_t iov_len;

}

高级字符驱动


最新回复(0)