3.1【Linux驱动基础之一】:字符设备驱动知识点
文章目录
一、字符设备驱动概念
字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的串口、SPI、LCD、键盘等等都是字符设备,这些设备的驱动就叫做字符设备驱动。
二、驱动工作模式层级流程图
三、设备驱动基础知识点
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
设备号、主设备号、次设备号操作宏
合成设备号: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.3【Linux驱动基础之三】:设备号、主设备号、次设备号
下一篇: 搭建hadoop伪分布式环境