一、混杂设备驱动模型
混杂设备属于字符设备中的一种
在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采用向量中断的方式
当中断发生的时候,硬件自动判断到是那个中断产生,然后就会跳转到相应的中断寄存器里面,中断寄存器里面存放着这个中断的处理程序的地址,然后根据这个地址,就是我们的中断处理程序了,然后进行相应的处理。
2)linux中断分析
关于linux内核中断流程,下面做一个简单的分析:
arch/arm/kernel/entry-armv.S
1.统一的入口
2.在此下面,有一个标号
跳转到此标号处,可以看到经过一系列配置,又到了arch_irq_handler_default处
3.查找arch_irq_handler_default
arch_irq_handler_default是一个宏,在entry-macro-multi.s函数里面
get_irqnr_and_base的作用就是获取中断源,可以进到这个宏里面查看详细的代码,如下:
4.asm_do_IRQ
随后,拿到中断号,跳转到asm_do_IRQ 这个函数
如上图所示,unsigned int irq 为获取到的中断号,同时又调用generic_handle_irq这个函数
5.generic_handle_irq
在generic_handle_irq这个函数中,又调用generic_handle_irq_desc这个函数
在这个函数中,又调用desc->handle_irq(irq, desc);这个便是最终的中断处理函数,
将上面的流程进行综合便可以得到下面的这幅图。
根据上面的分析和上面的图片,我们可以得知,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);
菜鸟一枚,如有错误,多多指教。。。