Windows CE 中断管理
操作系统对外设的请求都是通过中断来处理的。大多数情况,操作系统都不主动去查看外围设备的请求,只有当中断发生的情况下,操作系统才会通过中断来向外围设备提供服务。首先需要解释几个概念:
IRQ(Interrupt Request):物理中断请求,这是外部设备通过CPU的中断引脚向CPU发送的中断信号。
SYSINTR:逻辑中断,这是操作系统或者驱动程序直接调用的中断号,它是通过OAL把物理中断信号映射成为了OEM定义的逻辑中断号。
ISR(Interrupt Service Routine):中断服务例程,处于内核模式,ISR的主要职责就是判断输入的物理中断号,转化为相应的逻辑中断号。
IST(Interrupt Service Thread):中断服务线程,处于用户模式,IST是真正的中断处理程序,处理相关的中断请求就是在IST中实现的。
Windows CE 的中断处理模型如下图所示。简单来说,外围设备向内核发出了一个物理中断,ISR捕捉到这个物理中断,并且转化为相应的逻辑中断,将逻辑事件与一个事件关联,IST通过事件响应,进入中断处理程序,一次中断结束。
一. 中断处理过程
当一个中断发生时,处理器将控制内核中的一个异常操作,然后调用ISR注册到当前的中断。ISR负责把当前的物理中断转化为一个逻辑中断,同时给内核返回一个逻辑中断号。内核将设置一个事件与该逻辑中断相关联。IST将等到这个事件,直到该事件触发,IST处理中断请求。
下图是一个中断处理内部结构图:
上图的整个流程为:
(1). 如果内核的捕捉异常代码接收到一个硬件中断,那么内核接着就会识别一个异常,并且提交相应的硬件中断。
(2). 内核的中断管理器通知ISR禁用当前中断,直到中断处理完成,才能再重新启动当前中断。这个阶段过程中,允许中断嵌套,也就是允许高优先级的中断触发。
(3). 异常管理器调用ISR来响应这个中断。
(4). 内核接收到ISR的返回值后,依据该返回值来决定如何处理中断。
(5). 内核触发中断支持处理器来唤醒IST并激活该线程。
(6). IST响应相应的中断,如果有需要,IST调用个中I/O函数来访问响应的硬件来完成操作。
(7). 当IST完成中断处理工作后,调用InterruptDone()函数来通知内核。
(8). 内核调用OAL中的函数OEMInterruptDone()函数来宣告所有中断处理工作已经完成。OAL通知硬件重新打开该中断。至此,一次中断处理结束。
二. 中断服务例程ISR
ISR是运行在内核当中的一段代码,通常通过OEM实现。ISR主要是用于相应物理中断,并决定如何处理该中断。如果中断需要被内核和IST进一步处理,那么ISR会返回一个逻辑中断号SYSINTR_XXX;如果该中断不需要被进一步的处理,那么ISR只需要返回SYSINTR_NOP给内核。一个ISR程序必须非常的高效,从而避免对硬件中断相应的延迟。如果一个ISR响应太慢,从用户来看,就会认为这个设备死机了。
从上面看来,我们也可以在ISR中直接完成对中断的请求,并且完成相应的操作。但是这不是很好的办法,我们应该让IST来完成大多数的工作。
下面以一个SMDK2410的例子,该函数位于%WINCEROOT% /PLATFORM/COMMON/SRC/ARM/SAMSUNG/S3C2410X/INTR下,在SMDK2410种,OEMInterruptHandler()函数充当了ISR的角色:
ULONG OEMInterruptHandler(ULONG ra)
{
//得到正在处理的物理中断号
irq = INREG32(&g_pIntrRegs->INTOFFSET);
//系统时钟处理
if (irq == IRQ_TIMER4) {
// Clear the interrupt
OUTREG32(&g_pIntrRegs->SRCPND, 1 << IRQ_TIMER4);
OUTREG32(&g_pIntrRegs->INTPND, 1 << IRQ_TIMER4);
// Rest is on timer interrupt handler
sysIntr = OALTimerIntrHandler();
}
else {
//禁止同类的中断
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
}
//清空中断寄存器
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
//可挂载ISR
sysIntr = NKCallIntChain((UCHAR)irq);
if (sysIntr == SYSINTR_CHAIN || !NKIsSysIntrValid(sysIntr)) {
// IRQ wasn't claimed, use static mapping
sysIntr = OALIntrTranslateIrq(irq);
}
// unmask interrupts in case it's NOP or invalid
if (SYSINTR_NOP == sysIntr) {
if (OAL_INTR_IRQ_UNDEFINED == irq2) {
// Unmask the primary interrupt
CLRREG32(&g_pIntrRegs->INTMSK, mask);
} else {
// Unmask the external interrupt
mask = 1 << (irq2 - IRQ_EINT4 + 4);
CLRREG32(&g_pPortRegs->EINTMASK, mask);
}
}
}
//返回逻辑中断号
return sysIntr;
}
三. 中断服务线程IST
IST其实就是一个普通的用户态线程,它负责处理相应中断的大多数操作。前面的内容中,我们提到过IST线程是在等待到相应事件才会进行处理的,所以在大多数情况下,IST都是空闲的。实现IST的常见函数如下:
InterruputInitialize():该函数负责把某个逻辑中断与一个Event内核对象关联起来。
WaitForSingleObject():该函数阻塞当前进程,等待某个EVENT内核对象事件发生。
InterruptDone():该函数用来告诉操作系统,对该中断已经处理完成,操作系统可以重新开启该中断。
实现IST必须的步骤:
(1).创建一个结构体用来保存中断处理的相关数据。
(2).当IST触发时使用CreateEvent()函数。
(3).通过注册表读取物理中断号和逻辑中断号,在驱动加载前允许OAL把物理中断号映射成逻辑中断号。
(4).保存创建线程句柄。
具体例子就不在这里列举出来了。
参考书籍:
Windows CE 嵌入式操作系统。
Windows CE 设备驱动及BSP开发指南。