中断流程分析
- __irq_svc:执行irq_handler获取中断源
- 跳转到asm_do_IRQ,执行中断处理逻辑
__irq_svc: //1.中断统一入口函数
svc_entry //保存现场#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1 @ increment it
str r7, [tsk, #TI_PREEMPT]
#endifirq_handler //2.1执行irq_handler
/*
* Interrupt handling. Preserves r7, r8, r9
*/
.macro irq_handler // 2.2 irq_handler的宏定义
get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr //3.获取中断源
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b
bne asm_do_IRQ //4.执行asm_do_IRQ()函数
handle_edge_irq-------desc->chip->ack(irq)清中断----------handle_IRQ_event
按下按键---CPU进入异常模式---跳转到 b vector_irq + ........----调用到__irq_usr----调用irq_handler
中断的注册,处理,注销
- 1.注册:
int request_irq(unsigned int irq,
void (*handler)(int, void*, structpt_regs *),
unsigned long flags,
const char *devname,
void *dev_id)
unsigned int irq 中断号。
void (*handler)(int,void *) 中断处理函数。
unsigned long flags 与中断管理有关的各种选项。
const char * devname 设备名
void *dev_id 共享中断时使用。
- 注销
void free_irq(unsigned int irq, void *dev_id)
简单的demo:注册中断,中断处理,中断释放
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static struct class *irq_button_drv_class;
static struct class_device *irq_button_drv_class_dev;
//GPF端口
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
//GPG端口
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
//dev 就是request_irq的最后一个参数
static irqreturn_t buttons_irq(int irq, void *dev)
{
printk("irq = %d\n",irq);
return 0;
}
static int irq_button_drv_open(struct inode * inode, struct file * file)
{
// 配置GPF2,0为输入引脚 GPF2 = 00 GPF0 = 00
// 配置GPG3,11为输入引脚 GPG3 = 00 GPG11 = 00
// 中断源,中断处理函数,检测上升下降沿,中断设备名,...
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", 0);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", 0);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", 0);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", 0);
printk("open irq\n");
return 0;
}
ssize_t irq_button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
//返回四个引脚电平
unsigned char key_val[4];
int ret_val;
//读取GPF0 2
ret_val = *gpfdat;
key_val[0] = (ret_val & (1 << 0)) ? 1:0;
key_val[1] = (ret_val & (1 << 2)) ? 1:0;
//读取GPG3 11
ret_val = *gpgdat;
key_val[2] = (ret_val & (1 << 3)) ? 1:0;
key_val[3] = (ret_val & (1 << 11)) ? 1:0;
copy_to_user(buf,key_val,sizeof(key_val));
return sizeof(key_val);
}
int irq_button_close (struct inode * inode, struct file * file) {
//释放IRQ
free_irq(IRQ_EINT0,0);
free_irq(IRQ_EINT2,0);
free_irq(IRQ_EINT11,0);
free_irq(IRQ_EINT19,0);
printk("free irq\n");
return 0;
}
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE,
.open = irq_button_drv_open,
.read = irq_button_drv_read,
.release = irq_button_close,
};
int major;
static int irq_button_drv_init(void)
{
major = register_chrdev(0,"irq_button_drv", &button_drv_fops);
irq_button_drv_class = class_create(THIS_MODULE, "irq_button_drv");
irq_button_drv_class_dev = class_device_create(irq_button_drv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //地址映射:从0x56000050开始的16个字节的内存映射到gpfcon
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); //地址映射:从0x56000050开始的16个字节的内存映射到gpfcon
gpgdat = gpgcon + 1;
return 0;
}
static void irq_button_drv_exit(void)
{
unregister_chrdev(major,"irq_button_drv");
class_device_unregister(irq_button_drv_class_dev);
class_destroy(irq_button_drv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return 0;
}
module_init(irq_button_drv_init);
module_exit(irq_button_drv_exit);
MODULE_LICENSE("GPL");
具体的操作流程:
- request_irq();注册中断号,中断处理函数。。
- 检测到中断后,执行button_irq()打印irq值
- sudo make编译生成xxx.ko----insmod xxx.ko---lsmod
- ps(查看 -sh的PID 为772,用于接下来的命令)
- ls -l proc/772]/fd
- exec 5</dev/button(执行文件--即注册中断)---并且打印"open irq"
- 然后点击按键即可看到打印irq = ...,说明进入了中断处理函数
- 再ls -l proc/772即可看到设备文件/dev/button
- exec 5<&- 即可释放中断---并且打印“free irq”
- lsmod即可看到没有dev/button了
要使用软中断,必须先使用 open_softirq 函数注册对应的软中断处理函数