欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

3.1【Linux驱动基础之一】:字符设备驱动知识点

程序员文章站 2022-07-14 15:18:29
...




一、字符设备驱动概念

字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的串口、SPI、LCD、键盘等等都是字符设备,这些设备的驱动就叫做字符设备驱动。




二、驱动工作模式层级流程图

Linux驱动:应用层到驱动调用流程
3.1【Linux驱动基础之一】:字符设备驱动知识点


三、设备驱动基础知识点

3.1 【设备】和【驱动】

Linux学习中,会碰到两个概念【device 】和【driver】。

【device 】:表示设备,按照个人理解指的是硬件板卡芯片对应的所有外设,如GPIO、串口、spi、磁盘等等。

【driver】:表示驱动,按照个人理解,驱动就是让【device(设备,即各种外设)】能够正常工作的文件。

所以,驱动开发的过程就是:程序员撰写驱动代码(driver),使硬件外设设备(device)正常启动,正常工作。

3.2 【设备名】和【设备号】

Linux中,所有的【设备】都有自己的【设备名】和【设备号】。

【设备名】 是指:一个代表该设备的字符串名称,方便开发人员识别。

【设备号】 是指:一个代表该设备的数字序号,计算机是通过该数字序号来识别是哪个设备。

备注:

设备名与设备号信息在在文件 /proc/devices 中保存。

输入命令“cat /proc/devices”可以查看当前硬件外设的设备信息,包括设备名、设备号。

设备号数据类型为:dev_t,原型是一个32位无符号整形类型(unsigned int )。

定义位置在:include/linux/types.h

3.3 【主设备号】和【次设备号】

dev_t原型是一个32位无符号整形类型的值(unsigned int ),其中高12位表示主设备号,低20位表示次设备号,如下图。

备注:因为主设备号总共占12bit,所以范围为0 ~ 2^12,即:0~4095

图:【设备号、主设备号、次设备号】结构
3.1【Linux驱动基础之一】:字符设备驱动知识点

设备号、主设备号、次设备号操作宏

合成设备号:MKDEV(ma,mi)       /* 已经知道主设备号ma和次设备号mi,使用该宏可以合成为完成的设备号 */
主设备号:MAJOR(xxx.devid)
次设备号:MINOR(xxx.devid)

3.4 【file_operations结构体】:设备操作函数集合

驱动中实现对设备操作的具体函数,基本全都以函数指针的形式,在内核中的file_operations这个结构体中定义好了。

当我们需要什么功能或者说需要对外设进行什么操作,只需要实现具体对应的函数内容即可。

file_operations结构体在内核源码的【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 (*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 (*mremap)(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
};

3.5 【cdev结构体】:字符设备描述结构体

Linux中,使用cdev结构体描述一个字符设备。

cdev结构体在内核源码的【include/linux/cdev.h】文件中定义。

cdev结构体原型如下:

struct cdev { 
	struct kobject kobj;                  //内嵌的内核对象.
	struct module *owner;                 //该字符设备所在的内核模块的对象指针.
	const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
	struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.
	dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.
	unsigned int count;                   //隶属于同一主设备号的次设备号的个数.
}

内核提供cdev使用的函数有如下几个:(具体含义此处不进行详述)

void cdev_init(struct cdev *, const struct file_operations *); 
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *); 
void cd_forget(struct inode *);

3.6 【文件私有数据结构体】: 该结构体由用户自定义实现代码封装

备注:该结构体非内核中定义好的结构体,而是为了保持代码整洁,设置设备文件私有属性时候自定义的结构体。内容可以根据实际业务场景随时进行增减。

此处给一示例如下:

/* 抽象设备结构体 */
typedef struct {
    dev_t devid;            /* 设备号 */
    int major;              /* 主设备号 */
    int minor;              /* 次设备号 */
    struct cdev cdev;       /* cdev */
    struct class *class;    /* 类 */
    struct device *device;  /* 设备 */
} led_device_struct;
led_device_struct led; /* 实例化设备 */



四、设备驱动基础知识点思维导图


3.1【Linux驱动基础之一】:字符设备驱动知识点