Linux字符设备驱动(一) - 框架

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标、键盘、显示器、串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件。编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码。

一,关键数据结构

1,cdev

cdev可以理解为char device,用来抽象一个字符设备。

//kernel\include\linux\cdev.h

struct cdev {
    struct kobject kobj; //表示一个内核对象
    struct module *owner; //指向该模块的指针
    const struct file_operations *ops; //指向文件操作的指针,包括open、read、write等操作接口
    struct list_head list; //用于将该设备加入到内核模块链表中
    dev_t dev; //设备号,由主设备号和次设备号构成
    unsigned int count; //表示有多少个同类型设备,也间接表示设备号的范围
} __randomize_layout; //一个编译器指令,用于随机化结构体的布局,以增加安全性

2, file_operations

主要用来描述文件操作的各种接口,Linux一切接文件的思想,内核想要操作哪个文件,都需要通过这些接口来实现。

//kernel\include\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 (*iopoll)(struct kiocb *kiocb, bool spin);
    int (*iterate) (struct file *, struct dir_context *);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*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 *);
    unsigned long mmap_supported_flags;
    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 (*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
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
                   struct file *file_out, loff_t pos_out,
                   loff_t len, unsigned int remap_flags);
    int (*fadvise)(struct file *, loff_t, loff_t, int);

    ANDROID_KABI_RESERVE(1);
    ANDROID_KABI_RESERVE(2);
    ANDROID_KABI_RESERVE(3);
    ANDROID_KABI_RESERVE(4);
} __randomize_layout;

3, dev_t

表示字符设备对应的设备号,其中包括主设备号和次设备号。

//kernel\include\linux\types.h

typedef u32 __kernel_dev_t;
typedef __kernel_dev_t        dev_t;

4,数据结构之间的关系

字符设备驱动程序的数据结构以及API的关系图:

二,设备号管理

1,设备号的概念

每一类字符设备都有一个唯一的设备号,其中设备号又分为主设备号和次设备号,那么这两个分别作用是什么呢?

  • 主设备号:用于标识设备的类型,

  • 次设备号:用于区分同类型的不同设备

简单来说,主设备号用于区分是IIC设备还是SPI设备,而次设备号用于区分IIC设备下,具体哪一个设备,是MPU6050还是EEPROM。

2,设备号的分配

了解了设备号的概念,Linux中设备号有那么多,那么我们该如何去使用正确的设备号呢?

设备号的分配方式有两种,一种是动态分配,另一种是静态分配,也可以理解为一种是内核自动分配,一种是手动分配。

静态分配函数:

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

* from:表示已知的一个设备号
* count:表示连续设备编号的个数,(同类型的设备有多少个)
* name

猜你喜欢

转载自blog.csdn.net/u011456016/article/details/138504928
今日推荐