嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

时间:2022-08-03 17:54:28

一、混杂设备驱动模型

混杂设备属于字符设备中的一种

在Linux驱动中把无法归类的一些的设备定义为混杂设备(miscdevice)。他们共享相同的主设备号MISC_MAJOR(即10),但次设备号不同。
所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。miscdevice的API实现在drivers/char/misc.c中。

关于混杂设备的主要概念有:

1).设备描述

Linux中使用struct miscdevice来描述一个混杂设备。

struct miscdevice {
int minor; /* 次设备号*/
const char *name; /* 设备名*/
const struct file_operations *fops; /*文件操作*/
struct list_head list; /*misc_list的链表头*/
struct device *parent; /*父设备*/
struct device *this_device; /*当前设备,是device_create的返回值*/
};

2).设备注册

Linux中使用misc_register函数来注册一个混杂设备驱动。

int misc_register(struct miscdevice * misc)

3).设备卸载

misc_deregister

4)混杂设备驱动模型的主要流程

1.初始化struct miscdevice

这部分主要是定义struct miscdevice结构,然后struct miscdevice结构里面的内容根据自己的需要进行填充,如:

struct miscdevice misc = {
.minor = 200, /*次设备号*/
.name = "key",
.fops = &key_ops,
};

2.初始化操作函数集

在上面struct miscdevice结构中已经定义了fops 结构,因此我们接下来需要将其初始化,根据需要填充内容。

struct file_operations key_ops = {
.open = key_open,
};

然后对自己定义的函数进行扩展。

int key_open(struct inode *node ,struct file *filp)
{

return 0;
}

3.注册miscdevice

将此混杂设备注册到内核中去。

    /*注册混杂设备*/
misc_register(&misc);

4.注销miscdevic

在此驱动卸载的时候,需要将其注销,回收资源。

    /*注销混杂设备*/
misc_deregister(&misc);

贴上代码:

/*********************************************
*File name :key.c
*Author :stone
*Date :2016/07/28
*Function :混杂设备驱动模型
*********************************************/


#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>

int key_open(struct inode *node ,struct file *filp)
{

return 0;
}

struct file_operations key_ops = {
.open = key_open,

};

struct miscdevice misc = {
.minor = 200, /*次设备号*/
.name = "key",
.fops = &key_ops,
};

static int key_init()
{
/*注册混杂设备*/
misc_register(&misc);

}

static void key_exit()
{
/*注销混杂设备*/
misc_deregister(&misc);

}

MODULE_LICENSE("GPL");
module_init(key_init);
module_exit(key_exit);

二、linux中断处理流程

1)裸机中断回顾

裸机的中断在”按键中断“一课中有详细的介绍。6410采用向量中断的方式
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
当中断发生的时候,硬件自动判断到是那个中断产生,然后就会跳转到相应的中断寄存器里面,中断寄存器里面存放着这个中断的处理程序的地址,然后根据这个地址,就是我们的中断处理程序了,然后进行相应的处理。

2)linux中断分析

关于linux内核中断流程,下面做一个简单的分析:

arch/arm/kernel/entry-armv.S

1.统一的入口
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

2.在此下面,有一个标号
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

跳转到此标号处,可以看到经过一系列配置,又到了arch_irq_handler_default处
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

3.查找arch_irq_handler_default
arch_irq_handler_default是一个宏,在entry-macro-multi.s函数里面
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
get_irqnr_and_base的作用就是获取中断源,可以进到这个宏里面查看详细的代码,如下:
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
4.asm_do_IRQ
随后,拿到中断号,跳转到asm_do_IRQ 这个函数
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

如上图所示,unsigned int irq 为获取到的中断号,同时又调用generic_handle_irq这个函数

5.generic_handle_irq
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
在generic_handle_irq这个函数中,又调用generic_handle_irq_desc这个函数
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程
在这个函数中,又调用desc->handle_irq(irq, desc);这个便是最终的中断处理函数,
将上面的流程进行综合便可以得到下面的这幅图。
嵌入式学习-驱动开发-lesson3-混杂设备驱动模型与linux中断处理流程

根据上面的分析和上面的图片,我们可以得知,linux内核中断处理流程和裸机有一定的相似之处,其主要流程如下:
1.irq_svc中断的入口
2.获取中断号
3.根据中断号找到相应的中断
4.到相应的中断处取出事先注册好的中断处理函数

因此,我们的驱动需要做的是:
1.实现中断处理程序
2.注册中断,使内核识别

3)中断处理相关函数

1、中断注册

request_irq函数用于注册中断。
函数原型:

int request_irq(unsigned int irq, void (*handler)(int, void*, structpt_regs *),unsigned long flags,const char *devname,void *dev_id)

函数返回值:
返回0表示成功,或者返回一个错误码

参数:
unsigned int irq
中断号

void (handler)(int,void )
自定义的中断处理函数

* unsigned long flags*
与中断管理有关的各种选项,具体可以查看内核相关
如:
• IRQF_DISABLED(SA_INTERRUPT)
如果设置该位,表示是一个“快速”中断处理程序;
如果没有设置这位,那么是一个“慢速”中断处理程序。
• IRQF_SHARED(SA_SHIRQ)
该位表明该中断号是多个设备共享的。

快/慢速中断的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是“开启中断”标志位(处理器IF)在运行快速中断处理程序时是关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其它类型的中断仍可以得到服务。

const char * devname
设备名

void *dev_id
共享中断时使用。

2 中断处理程序

中断处理程序的特别之处是在中断上下文中运行的,它的行为受到某些限制:
①.不能使用可能引起阻塞的函数(如果引起阻塞,则整个系统的中断都无法处理)
②.不能使用可能引起调度的函数

其处理流程如下所示:
①检查设备是否产生了中断
②清除中断产生标志
③进行相应的硬件操作

3 注销中断

当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们注销, 使用函数:

void free_irq(unsigned int irq, void *dev_id)

贴上代码:

/*********************************************
*File name :key.c
*Author :stone
*Date :2016/07/28
*Function :混杂设备驱动加上中断处理流程
*********************************************/


#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>

irqreturn_t key_handle(int irq,void *dev_id)
{
/*1.检测是否发生按键中断*/

/*2.清除已经发生的按键中断*/

/*3.打印按键值*/
printk(KERN_WARNING"key down !\n");

return 0;
}
int key_open(struct inode *node ,struct file *filp)
{

return 0;
}

struct file_operations key_ops = {
.open = key_open,

};

struct miscdevice misc = {
.minor = 200, /*次设备号*/
.name = "key",
.fops = &key_ops,
};

static int key_init()
{
/*注册混杂设备*/
misc_register(&misc);

/*中断初始化*/
request_irq(irqno,key_handle,IRQF_TRIGGER_FALLING,"key",0);
//IRQF_TRIGGER_FALLING 从高电平到低电平产生中断 下降沿

return 0;
}

static void key_exit()
{
/*注销混杂设备*/
misc_deregister(&misc);

/*注销中断*/
free_irq(irqno,0);
}


MODULE_LICENSE("GPL");

module_init(key_init);
module_exit(key_exit);

菜鸟一枚,如有错误,多多指教。。。