知识储备。
基本的框架
在linux系统中,App无法操作硬件的,不具备相应的权限。在mmu(内存管理单元)作了划分的,应用层只能操作属于它的mmu部分。操作硬件只能是通过驱动程序。应用程序与驱动程序之间有严格的界限。驱动程序在内核中。
app如何调用驱动机制
如App使用open函数打开设备时,会先设置某个寄存器,会触发一个异常swi指令(此时由用户态切换到内核态才有更高的权限),此时会有中断服务程序被调用。然后根据先前设置的寄存器判断是什么操作;然后去读写硬件。
字符设备驱动编写步骤
1. 实现入口函数 XXX_init()和卸载函数 XXX_exit()
2. 申请设备号 register_chrdev_region(与内核相关)
静态分配设备号:在事先知道设备主设备号的情况下通过参数函数指定第一个设备号而向系统申请分配一定数目的设备号。
动态分配:alloc_chrdev_region():通过参数仅设置第一个次设备号(通常为0,事先不会知道主设备号)和要分配的设备数目而系统动态分配所需的设备号
3. 注册字符设备驱动 cdev_alloc / cdev_init /cdev_add(与内核相关)
4. 利用udev/mdev机制创建设备文件(节点)class_create,device_create(与内核相关)
5. 硬件部分初始化
io资源映射 ioremao,内核提供gpio库函数(与硬件相关)
注册中断(与硬件相关)
初始化等待队列(与内核相关)
初始化定时器(与内核相关)
6. 构建file_operation结构(与内核相关)
7. 实现硬件操作方法 XXX_open,XXX_read,XXX_write…(与硬件相关)
通用GPIO驱动框架的问题及理解
setup_timer()函数
原型:#define setup_timer(timer, fn, data) \ __setup_timer((timer), (fn), (data), 0)
第一个参数:是struct timer_list类型的变量,此变量用于存放动态定时器,是即将要被初始化的对象,其定义及详细解释参考
第二个参数是定时器到期时将要执行的函数,用于给定时器变量的function字段赋值。
第三个参数用于给定时器变量的data字段赋值
add_timer()函数
将定时器加入到定时器链表,并激活定时器
gpio_to_irq()函数或gpiod_to_irq()
int gpio_to_irq(unsigned int gpio);//返回值:gpio对应的中断号
request_irq()函数
原型:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) { return request_threaded_irq(irq, handler, NULL, flags, name, dev); }
irq:要申请的中断号。
handler:中断处理函数,当中断发生时,执行该函数。
flags:中断标志。
name:中断名字,设置后在/proc/interrupts目录下可以查看。
dev:如果flags设置为IRQF_SHARED,dev用来区分不同的中断,一般情况下,dev设置为设备结构体,它会传给中断处理函数irq_handler_t第二个参数,本例中置为NULL。
register_chrdev()
原型:int register_chrdev(unsigned int major, const char *name,struct file_operations *fops)
major:设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号
name:设备名
fops:对各个调用的入口点
class_create()函数
原型:#define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ })
owner:一个struct module结构体类型的指针,指向函数__class_create()即将创建的、“拥有”这个struct class的模块。一般赋值为THIS_MODULE,此结构体的详细定义见文件include/linux/module.h。
name:char类型的指针,代表即将创建的struct class变量的名字,用于给struct class的name字段赋值。通俗地说,就是指向struct class名称的字符串的指针。
效果:此函数的执行效果就是在/sys/class/目录下创建一个新的文件夹
device_create()函数
原型:struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata,const char *fmt, ...);
class:该设备依附的类
parent:父设备
devt:设备号(此处的设备号为主次设备号)
drvdata:私有数据
fmt:设备名。
device_create能自动创建设备文件是依赖于udev这个应用程序。udev是一种工具,它能够