linux device drivers 读书笔记(第三章)

scull的design

(举了一个具体的scull字符设备的例子)

主从设备号

字符设备是通过文件系统中的名字进入的,
而在相应目录下使用ls -l可以看到有两个数字,分别是主从号
主号表明设备相应的驱动,而从号表示具体哪个设备被指向.

设备号的内部表示

dev_t类型定义于

MAJOR(dev_t dev);
MINOR(dev_t dev);
//分别得到主从设备号
MKDEV(int major, int minor);
//通过主从设备号得到一个dev_t

分配和释放一个设备号

驱动需要做的第一件事就是获得一个或多个设备号来使用
相应的函数定义在

int register_chrdev_region(dev_t first, unsigned int count, char *name);

first是逆向分配的设备号数字的范围的第一个设备号,一般从设备号部分是0.count是你请求的连续设备号的总数,name是和分配的设备号范围相应的设备名,分配成功返回0,否则返回负数错误代码.

这个函数用于你知道你想分配什么设备号,否则使用另外一个函数进行动态分配

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

firstminor是第一个需要用的从设备号,一般是0.dev_t是输出,如果分配成功,里边即保存的分配后的dev_t.

取消分配是用另外一个函数

voidunregistered_传入的_region(dev_t first, unsigned int count);

动态分配主设备号

由于一些设备号可能已经被使用,所以我们一般使用动态分配的方法,不过这样意味着我们无法提前创建设备节点,因为主设备号是可变的.所以动态分配后我们需要从/proc/devices中获取信息,创建特殊的文件

一些重要的数据结构

File operations 文件操作

用来指明驱动的操作,在linux/fs.h中定义,每一个域指明一个操作,指向一个函数.

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 (*setfl)(struct file *, unsigned long);
    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
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
            u64);
    ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
            u64);
};

file结构

同样在linux/fs.h中定义,表示一个打开的文件,注意和用户空间FILE区分.在open时被创建,然后在其他操作时被传入

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 */

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

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
    struct fscrypt_info *i_crypt_info;
#endif

    void            *i_private; /* fs or device private pointer */
};

字符设备注册

内核使用结构体cdev来表示字符设备,在内核调用你的设备的操作之前,你必须分配和注册一个或多个这些结构.在

struct cdev *my_cdev = cdev_alloc();
my_cdev -> ops = &my_ops;

也可以用已经有的自己的cdev来存储一个分配的cdev

void cdev_init(struct cdev *cdev, struct file_operations *fops);

最后需要

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

count是设备关联的设备号的数量

移除一个字符设备

void cdev_del(struct cdev *dev);

open和release

open方法

原型

int (*open)(struct inode *inode, struct file *filep);

用来进行初始化操作,表明打开了一个设备

release方法

做一些结束之后的操作

read和write

read和write负责和应用程序交互数据,
原型

ssize_t read(struct file *filep, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filep, const char __user *buff, size_t count, loff_t *offp);

filep是文件指针,count是数量,offp表明文件位置.

对于和用户空间内存的交互,我们需要使用一些特殊的函数,定义于

unsigned long copy_to_user(void __user *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);

它们和memcpy用途一样,不过都保证了用户空间和内核空间的内存交互是安全的.当地址空间无效的时候,copy将不会发生

readv和writev

是read和write的两个vector版本,用来依次处理一定数量的read和write
原型

ssize_t (*readv) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);

iovec是在

struct iovec 
{
void __user *iov_base;//需要传输的数据的开始,在用户空间
__kernel_size_t iov_len;//长度
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值