字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构
中断方式获取按键值(单片机)
1、有按键按下
2、CPU发生中断
强制调到异常向量入口执行(中断是异常的一种)
3、入口函数 跳转指令:b 函数
a.保存被中断的现场(各种寄存器的值)
b.执行中断处理函数
c.恢复被中断的现场
Linux异常向量:
ARM架构CPU的异常向量基址可以是0x00000000,也可以是0xffff0000,Linux内核使用后者。(这个地址0xffff0000并不对应于实际的内存,是一个虚拟地址)。trap_init函数将异常向量复制到0xffff0000处。
使用memcpy把__vectors_start开始,大小为__vectors_end - __vectors_start,拷贝到vectors里。
vectors=CONFIG_VECTORS_BASE,是一个配置项。内核代码里vi .config文件:
CONFIG_VECTORS_BASE=0xffff0000。
__vector_start:(arch\arm\kernel\entry-armv.s)
也是一些跳转指令。
看一个例子,对于中断,发生中断就会跳到b vector_irq + stubs_offset:
vector_stub irq, IRQ_MODE, 4 把它展开。
vector_irq:
sub lr, lr, #4 //计算返回地址
@
@ 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:
usr_entry:保存寄存器。
irq_handler:最终会调用asm_do_IRQ(用C语言来写的)。
总结:
异常向量最终怎么调用asm_do_irq?
在trap_init构造异常向量,异常向量是把__vectors_start开始,大小为__vectors_end - __vectors_start,拷贝到vectors0xffff0000里。
异常向量:(也是某些跳转)
比如 b vector_irq+subs_offset(vector_irq:链接地址,subs_offset:偏移地址)
vector_irq是用一个宏来实现的vector_stub irq, IRQ_MODE, 4 ,计算返回地址,保存寄存器,调用处理函数。
处理函数:usr_entry保存环境变量、寄存器。调用irq_handler函数。
irq_handler函数:asm_do_IRQ(C函数)。
恢复。
ARM架构Linux内核的异常处理体系结构: