字符设备驱动(一)
目录
前言
我们前两天提到了模块的编写,知道了模块是我们驱动代码的承载物,那么设备又是什么东西呢,顾名思义,它是我们在内核注册的对于硬件的一种抽象,如果把模块比作流水线,那么设备就是生产的产品,所以呢,一个模块可以注册多个设备,但是一个设备只能来自一个模块。本节我们还会用到之前我们Linux应用那几个函数open,read,write函数,以理解Linux内这些函数与c库函数的不同之处。
一、open 函数的流程
我们在应用的那节知道,open函数可以根据flag,和设定权限打开文件,返回fd,当时可能大家都以为这个open跟普通库函数没两样,其实不然,在驱动中,由于不同硬件的差异性,所以这些应用函数肯定不尽相同,就比如一个简简单单的read,不同传感器,不同设备它读取数据肯定不一样吧,所以这个read函数也不一样,那怎么才能做到用户态用一个函数做出不同的效果呢,我们就以open为例来讲一讲。
首先,用户调用open("/dev/xxx"), 这时,就要靠一个sys_open函数让我们从用户态陷入内核态,这时,VFS会根据我们要打开的设备名,查找目录项,找到对应的inode号,里面有我们的设备号等信息,这时会分配一个struct file结构体,就是我们熟知空的文件表,里面存着inode指针,fops函数表这些信息,不过这时候还是默认的,这时候调用的就是默认函数表中的open(chrdev_open),此时内核会给你一个fd进程号,接着内核会调用这个默认open函数,它会路径追踪从inode表里面找到设备号,去/dev里面找到cdev表,读这个哈希表,读到该设备驱动的函数表,并讲file结构体的函数表替换成该函数表(这些函数我们称为回调函数),然后尝试执行这个函数表中的open函数,如果没有open则会返回fd,就跟我们平时的open一样,这样我们就可以在设备驱动这个函数表里添加一些我们想要的功能了。
值得一提的是,open我们就算不写回调函数它也能执行打开文件分配fd的操作,这是内核帮你完成的,是比较特殊的存在。但是呢read,write就不一样了,在设备驱动中,read,write在不同设备中的特异性,必须确保每个read,write函数都有回调函数,否则内核如果找不到这俩回调函数,则会返回错误码。
那么知道了流程,我们就可以反推一下进而设计驱动代码了,这个我们下节再讲,接下来讲讲我们刚刚提到的这一些结构体,它们的成员包含的信息都有哪些。
二、一些结构体
file_operations结构体cdev结构体inode结构体device结构体class结构体
一些结构体
file_operations结构体
struct file_operations { struct module *owner; int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); };该结构体定义了字符设备驱动中文件操作的函数指针,用于实现对设备的读写、打开、关闭等操作。
cdev结构体
struct cdev { struct kobject kobj; dev_t dev; /* 设备号 */ unsigned int count; /* 设备数量 */ struct file_operations *ops; /* 文件操作结构体 */ };cdev是字符设备的核心结构体,用于管理字符设备的注册和注销过程。
inode结构体
struct inode { umode_t i_mode; /* 文件权限 */ uid_t i_uid; /* 文件所有者 */ gid_t i_gid; /* 文件所属组 */ dev_t i_rdev; /* 字符设备的设备号 */ struct file_operations *i_fop; /* 文件操作结构体 */ };inode是 Linux 文件系统中表示文件或目录的元数据结构,包含文件的权限、大小、时间戳等信息。
device结构体
struct device { struct class *class; /* 设备所属的类 */ dev_t devt; /* 设备号 */ struct device_driver *driver; /* 关联的驱动程序 */ };device结构体用于描述设备的属性和行为,是设备模型中的核心结构之一。
class结构体
struct class { struct kobject *kobj; /* 内核对象 */ char name[20]; /* 类名 */ };class结构体用于表示设备类,是设备模型中用于组织设备的一种方式。