学习wince中断时将资料整理成一篇文章(一)
1.总体了解流程
首先描述wince5.0 (2440BSP)的中断流程:
流程1.创建事件aà创建线程ISTà用InterruptInitialize〈系统中断号绑定线程IST〉à线程IST进入等待事件a状态(挂起状态)。
流程2.外部引发中断à OEMInterruptHandler<屏蔽中断à把物理中断转换成系统中断,其他à重新使能中断。(ISR过程)>à操作系统根据系统中断号触发事件a。
流程3.挂起的IST线程等待到事件a进入就绪状态,得到执行时间后开始执行中断服务代码, 最后调用InterruptDone重新使能当前的中断。
你需要为你的设备驱动写好中断处理请求(ISR)和中断服务线程(IST),并牢记这些事件的顺序:
1).当一个中断发生,处理器跳转到核心的中断处理程序(exception handler );
2).这个中断处理程序禁止所有同级或低优先级的其他中断,然后为当前的IRQ调用对应的ISR;
3).ISR中会按照中断标识的形式,返回一个逻辑中断号给中断处理程序,并会置位板级设备中断;
4).中断处理程序重新使能所有的中断,而目前的中断已经在上一步中置位了,然后就触发对应的IST事件;
5).IST就绪,服务于中断设备,然后完成对中断的处理;
6).IST调用InterruptDone函数,该函数将顺序调用OAL层的OEMInterruptDone函数,它将重新使能当前的中断。
1.1物理中断和逻辑中断的对应关系如何建立
这个函数用将物理中断号来获取逻辑中断号:
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &g_PwrButtonIrq, sizeof(UINT32), &g_PwrButtonSysIntr, sizeof(UINT32), NULL))
其中:UINT32 g_PwrButtonIrq = IRQ_EINT0;
从help里面查出,KernelIoControl函数最终是调用OEMIoControl函数。
在D:\WINCE500\PLATFORM\COMMON\SRC\COMMON\IOCTL里找到它的定义了,关键代码:
// Execute the handler
rc = g_oalIoCtlTable.pfnHandler(
code, pInBuffer, inSize, pOutBuffer, outSize, pOutSize
);在SMDK2440\Src\Kernel\Oal\ioctl.c中可以找到:
const OAL_IOCTL_HANDLER g_oalIoCtlTable[] = {
#include "ioctl_tab.h"
};
在SMDK2440\Src\Inc\ioctl_tab.h文件中,找到这个表的定义。这个命令对应的函数是OALIoCtlHalRequestSysIntr。
PLATFORM\COMMON\SRC\COMMON\IOCTL\ioctl.c找到这个函数定义:
// Find if it is new or old call type
if (inpSize > sizeof(UINT32) && pInpData[0] == -1) {
// Second UINT32 contains flags, third and subsequents IRQs
sysIntr = OALIntrRequestSysIntr(inpSize/sizeof(UINT32) - 2, &pInpData[2], pInpData[1]); } else {
// This is legacy call, first UINT32 contains IRQ
sysIntr = OALIntrRequestSysIntr(1, pInpData, 0);
}
在WINCE500\PLATFORM\COMMON\SRC\COMMON\INTR\BASE\map.c找到OALIntrRequestSysIntr定义:
irq = pIrqs[0];
sysIntr = g_oalIrq2SysIntr[irq];
在同一个文件中定义:static UINT32 g_oalIrq2SysIntr[OAL_INTR_IRQ_MAXIMUM];
对这个表格赋值仅有两个地方:VOID OALIntrStaticTranslate(UINT32 sysIntr, UINT32 irq)
{
OALMSG(OAL_FUNC&&OAL_INTR, (
L"+OALIntrStaticTranslate(%d, %d)\r\n", sysIntr, irq
));
if (irq < OAL_INTR_IRQ_MAXIMUM && sysIntr < SYSINTR_MAXIMUM) {
g_oalSysIntr2Irq[sysIntr] = irq;
g_oalIrq2SysIntr[irq] = sysIntr;
}
OALMSG(OAL_FUNC&&OAL_INTR, (L"-OALIntrStaticTranslate\r\n"));
}
OALIntrStaticTranslate和OALIntrRequestSysIntr本身两个函数负责建立对应表。后者如果在现有的中断表中找不到已经建立的对应关系,就会分配一个未定义的Sysintr逻辑中断号给这个物理中断号。因此逻辑中断和物理中断的对应,可以说是随机的,只要保证两者是一一对应就好了,不必要硬性建立一个中断号表格(像WINCE4.2那样)。代码中只找到OALIntrStaticTranslate(SYSINTR_OHCI, IRQ_USBH);是静态对应。当中断处理程序获得了逻辑中断号,那么就会触发该中断号关联着的事件。
1.2核心部分的中断处理程序如何获得物理中断号
这个问题的目的是:如何添加一个原来系统中没有的物理中断。Physical interrupts (IRQs) are hardware lines over which devices can send interrupt signals to the microprocessor. Logical interrupts (SYSINTRs) are a mapping of the IRQ, which the OAL specifies.一般情况下将ISR与中断处理程序相关联的注册在系统启动的时候进行。在启动过程中,在OAL层kernel调用OEMInit函数。然后,OEMInit调用HookInterrupt 函数来通知中断处理程序,哪些ISR对应到某个物理中断。WINCE500\PUBLIC\COMMON\OAK\INC\nkintr.h,定义了某些逻辑中断号,声明了hookInterrupt等函数。除此之外,再没有hookInterrupt的定义。看看WINCE500\PLATFORM\SMDK2440A\src\kernel\oal\init.c里面的OEMinit函数做了些什么:
// Initialize interrupts
if (!OALIntrInit()) {
OALMSG(OAL_ERROR, (
L"ERROR: OEMInit: failed to initialize interrupts\r\n"
));
}
OALIntrInit函数在WINCE500\PLATFORM\COMMON\SRC\ARM\SAMSUNG\S3C2440A\Intr\intr.c文件中定义:调用OALIntrMapInit()函数初始化寄存器;最后调用:
#ifdef OAL_BSP_CALLBACKS
// Give BSP change to initialize subordinate controller
rc = BSPIntrInit();
#else
rc = TRUE;
#endif
OALIntrMapInit()函数里面对两个中断表做了初始化:
for (i = 0; i < SYSINTR_MAXIMUM; i++) {
g_oalSysIntr2Irq = OAL_INTR_IRQ_UNDEFINED;
}
for (i = 0; i < OAL_INTR_IRQ_MAXIMUM; i++) {
g_oalIrq2SysIntr = SYSINTR_UNDEFINED;
}
WINCE500\PLATFORM\COMMON\SRC\ARM\SAMSUNG\S3C2440A\Intr\sources:
TARGETNAME=oal_intr_s3c2440a
TARGETTYPE=LIBRARY
SYNCHRONIZE_DRAIN=1
NOMIPS16CODE=1 CDEFINES=$(CDEFINES) -DCEDDK_USEDDKMACRO -DOAL_BSP_CALLBACKS
----------------------------------------------------------------------------------
CDEFINES=-DSomeDef : This sets one or more preprocessor definitions. You must include the -D switch on each define you add. You can add new defines by using this syntax: "CDEFINES=$(CDEFINES) -DAnotherDef", or you can ignore existing settings with this syntax: "CDEFINES=-DOnlyDef".
----------------------------------------------------------------------------------
因此BSPIntrInit会被执行。
在WINCE500\PLATFORM\SMDK2440A\Src\Kernel\Oal\intr.c有这个函数的定义:
// Set GPG1 as EINT9
// Add static mapping for Built-In OHCI
OALIntrStaticTranslate(SYSINTR_OHCI, IRQ_USBH);
到这里,无法了解如何添加一个物理中断。在CPU接收到中断后,对中断的处理是在 OEMInterruptHandler()中,该函数的首先屏蔽该中断,最后得到实际中断IRQ所对应的sysintr的值”OEMInterruptHandler函数在WINCE500\PLATFORM\COMMON\SRC\ARM\SAMSUNG\S3C2440A\Intr\intr.c文件中,感觉到它实际上就是上面中断序列中谈到的“中断处理程序”和ISR。就是说,中断发生之后,CPU并不知道到底是哪个中断发生了,实际上WINCE中也没有建立中断矢量表,而是直接跳转到OEMInterruptHandler函数,然后在其中查看g_pIntrRegs->INTOFFSET寄存器,来查看到底发生了什么中断。
在s3c2440a_intr.h文件里面有中断号宏定义:
#define IRQ_EINT0 0 // Arbiter 0
#define IRQ_EINT1 1
#define IRQ_EINT2 2
#define IRQ_EINT3 3......
INTOFFSET寄存器的值与这个宏定义是完全一一对应。这样,也就搞清楚了物理中断号如何获得,又如何对应到逻辑中断号,最后,触发了IST,整个中断处理就结束了。2440全部的中断源都已经被纳入了,添加一个采用某个中断源的设备驱动,只需要用kernelIOControl函数通过物理中断产生一个逻辑中断号就可以了!这样的话,只要在0x18位置有一个跳转指令就可以了(但还没有找到这条跳转指令)。
1.3 其他中断相关函数了解
关注WINCE500\PLATFORM\COMMON\SRC\ARM\SAMSUNG\S3C2440A\Intr\intr.c中的其他函数:
OEMInterruptHandler包含了对以下中断的判断和处理:
IRQ_TIMER4,这个是系统节拍;
IRQ_TIMER2,作用未知(Profiling timer);
IRQ_EINT4_7,EINT8_23,外部中断;
任何一个中断发生后,先mask该中断(禁止中断),然后再清除中断请求:
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
其它中断获取逻辑中断号:
// First find if IRQ is claimed by chain
sysIntr = NKCallIntChain((UCHAR)irq);
if (sysIntr == SYSINTR_CHAIN || !NKIsSysIntrValid(sysIntr)) {
// IRQ wasn't claimed, use static mapping
sysIntr = OALIntrTranslateIrq(irq);
}
关于NKCallIntChain的说明:
如果没有与ISR关联的IRQ事件,返回SYSINTR_CHAIN ;
除此之外,将返回IRQ对应的SYSINTR值。