ARM架构的CPU的异常向量基址可以是0x00000000,也可以是0xffff0000,Linux内核使用后者。
在trap_init()函数(代码在arch/arm/kernel/traps.c)中将异常向量复制到0xffff0000处。
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz); |
发生异常后先跳入异常向量vectors表中,异常向量表中存放的是一些跳转指令,根据不同的异常进行第二次跳转,
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start //重新定位跳转的位置 .globl __vectors_start __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_pabt + stubs_offset b vector_dabt + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end __vectors_end: |
其中vector_irq为一个宏:计算返回地址、保持环境变量
vector_stub irq, IRQ_MODE, 4 |
宏定义如下,可代入展开:
.macro vector_stub, name, mode, correction=0 .align 5 vector_\name: .if \correction sub lr, lr, #\correction .endif @ @ Save r0, lr_<exception> (parent PC) and spsr_<exception> @ (parent CPSR) @ stmia sp, {r0, lr} @ save r0, lr mrs lr, spsr str lr, [sp, #8] @ save spsr @ @ Prepare for SVC32 mode. IRQs remain disabled. @ mrs r0, cpsr eor r0, r0, #(\mode ^ SVC_MODE) msr spsr_cxsf, r0 @ @ the branch table must immediately follow this code @ and lr, lr, #0x0f mov r0, sp ldr lr, [pc, lr, lsl #2] movs pc, lr @ branch to handler in SVC mode .endm |
接着根据当前的模式进行第三次跳转:
.long __irq_usr @ 0 (USR_26 / USR_32) .long __irq_invalid @ 1 (FIQ_26 / FIQ_32) .long __irq_invalid @ 2 (IRQ_26 / IRQ_32) .long __irq_svc @ 3 (SVC_26 / SVC_32) .long __irq_invalid @ 4 .long __irq_invalid @ 5 .long __irq_invalid @ 6 .long __irq_invalid @ 7 .long __irq_invalid @ 8 .long __irq_invalid @ 9 .long __irq_invalid @ a .long __irq_invalid @ b .long __irq_invalid @ c .long __irq_invalid @ d .long __irq_invalid @ e .long __irq_invalid @ f |
以进入__irq_usr为例进行说明
__irq_usr: usr_entry #ifdef CONFIG_TRACE_IRQFLAGS bl trace_hardirqs_off #endif get_thread_info tsk #ifdef CONFIG_PREEMPT ldr r8, [tsk, #TI_PREEMPT] @ get preempt count add r7, r8, #1 @ increment it str r7, [tsk, #TI_PREEMPT] #endif irq_handler |
进入irq_handler函数继续执行
.macro irq_handler get_irqnr_preamble r5, lr 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ adrne lr, 1b bne asm_do_IRQ |
最后调用asm_do_IRQ函数。(C语言编写)
|
中断处理流程:
(1)发生中断时,CPU执行异常向量代码Vector_irq;
(2)在异常向量代码中,最终调用中断处理入口函数asm_do_IRQ;
(3)asm_do_IRQ函数根据中断号调用irq_desc数组中的handle_irq;
(4)handle_irq会使用chip成员函数中的函数来设置硬件,比如:清楚中断、禁止中断、重新使能中断;
(5)handle_irq逐个调用用户在action链表中注册的处理函数。
用户注册中断处理函数的过程:
调用request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);函数像内核注册中断处理函数。
Int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags, const char*devname,void *dev_id)
卸载中断处理函数
void free_irq(unsigned int irq,void *dev_id)
代码:
在Open函数中注册中断
static int third_drv_open(struct inode *inode, struct file *file) { request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } |
在中断处理函数中确定按键值并返回
static irqreturn_t buttons_irq(int irq, void *dev_id) { struct pin_desc * pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); //该系统函数可以读出引脚的值 if (pinval) { /* 按下 */ key_val = 0x80 | pindesc->key_val; } else { /* 松开 */ key_val = pindesc->key_val; } ev_press = 1; /* 表示按键发生了*/ wake_up_interruptible(&button_waitq); /*唤醒休眠的进程*/ return IRQ_RETVAL(IRQ_HANDLED); } |
读函数
ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if (size != 1) return -EINVAL; /* 未按下按键时,进程挂起 */ wait_event_interruptible(button_waitq, ev_press); /* 将键值拷贝给用户*/ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } |