字符设备驱动程序

    技术2022-05-11  18

      字符设备驱动程序

    三个内核结构体: 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)