Linux驱动开发2——字符设备驱动

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(4)));  /* 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 0;
}

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

 

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: 大小

 

posted on 2019-03-29 10:23  者旨於陽  阅读(302)  评论(0编辑  收藏  举报

导航