如何实现2440软件重启/software reset(作者:wogoyixikexie@gliet)
上周,把4.2下的BSP 的串口驱动(包括16C552的并口转换成串口的驱动)移植到5.0BSP下,经过一番周折,已经把所有异常排除了,但是用到外部中断2(EINT2)的注册表加载的时候导致系统重启。后来发现这个EINT2是系统默认做软件重启/software reset的中断的。找了一小时,把一些中断相关的东西改变一些了,但是还是照样加载重启。刚才无意中想到了一个函数OEMInterruptHandler,这个可以对中断做一些处理的,我想玄机就在这里了,果然是。下面来看看这个函数。
第一处:
C:\WINCE500\PLATFORM\SMDK2440A\Src\Common\Intr\intr.c(251):// Function: OEMInterruptHandler
//------------------------------------------------------------------------------
//
// Function: OEMInterruptHandler
//
ULONG OEMInterruptHandler(ULONG ra)
{
UINT32 sysIntr = SYSINTR_NOP;
UINT32 irq, irq2, mask;
// Get pending interrupt(s)
irq = INREG32(&g_pIntrRegs->INTOFFSET);
g_oalLastSysIntr = TRUE; //c ksk 20060404
// System timer interrupt?
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();
}
// Profiling timer interrupt?
else if (irq == IRQ_TIMER2)
{
// Mask and Clear the interrupt.
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// The rest is up to the profiling interrupt handler (if profiling
// is enabled).
//
if (g_pProfilerISR)
{
sysIntr = g_pProfilerISR(ra);
}
}
else if (irq == IRQ_EINT2) // Softreset
{
RETAILMSG(1, (TEXT("OEMInterruptHandler: irq = %d \r\n"), irq));
OALIoCtlHalReboot(IOCTL_HAL_REBOOT,NULL,0,NULL,0,NULL);
sysIntr = SYSINTR_NOP;
}
else
{
#ifdef OAL_ILTIMING
if (g_oalILT.active) {
g_oalILT.isrTime1 = OALTimerCountsSinceSysTick();
g_oalILT.savedPC = 0;
g_oalILT.interrupts++;
}
#endif
if (irq == IRQ_EINT4_7 || irq == IRQ_EINT8_23) { // 4 or 5
// Find external interrupt number
mask = INREG32(&g_pPortRegs->EINTPEND);
mask &= ~INREG32(&g_pPortRegs->EINTMASK);
mask = (mask ^ (mask - 1)) >> 5;
irq2 = IRQ_EINT4;
while (mask != 0) {
mask >>= 1;
irq2++;
}
// Mask and clear interrupt
mask = 1 << (irq2 - IRQ_EINT4 + 4);
SETREG32(&g_pPortRegs->EINTMASK, mask);
OUTREG32(&g_pPortRegs->EINTPEND, mask);
// Clear primary interrupt
mask = 1 << irq;
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// From now we care about this irq
irq = irq2;
}
else if(irq == IRQ_CAM)
{
if(INREG32(&g_pIntrRegs->SUBSRCPND) & (1<<IRQ_SUB_CAM_C))
{
SETREG32(&g_pIntrRegs->INTSUBMSK, (1<<IRQ_SUB_CAM_C));
SETREG32(&g_pIntrRegs->INTMSK, (1<<IRQ_CAM));
OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_CAM_C));
OUTREG32(&g_pIntrRegs->SRCPND, (1<<IRQ_CAM));
OUTREG32(&g_pIntrRegs->INTPND,(1<<IRQ_CAM));
//RETAILMSG(1,(TEXT("IRQ_CAM Codec\r\n")));
}
else if(INREG32(&g_pIntrRegs->SUBSRCPND) & (1<<IRQ_SUB_CAM_P))
{
SETREG32(&g_pIntrRegs->INTSUBMSK, (1<<IRQ_SUB_CAM_P));
SETREG32(&g_pIntrRegs->INTMSK, (1<<IRQ_CAM));
OUTREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_CAM_P));
OUTREG32(&g_pIntrRegs->SRCPND, (1<<IRQ_CAM));
OUTREG32(&g_pIntrRegs->INTPND,(1<<IRQ_CAM));
//RETAILMSG(1,(TEXT("PreView\r\n")));
}
else
{
SETREG32(&g_pIntrRegs->INTSUBMSK, (1<<IRQ_SUB_CAM_C)|(1<<IRQ_SUB_CAM_P));
SETREG32(&g_pIntrRegs->INTMSK, (1<<IRQ_CAM));
SETREG32(&g_pIntrRegs->SUBSRCPND, (1<<IRQ_SUB_CAM_C)|(1<<IRQ_SUB_CAM_P));
OUTREG32(&g_pIntrRegs->SRCPND, (1<<IRQ_CAM));
OUTREG32(&g_pIntrRegs->INTPND,(1<<IRQ_CAM));
//RETAILMSG(1,(TEXT("nop\r\n")));
return SYSINTR_NOP;
}
}
else {
// Mask and clear interrupt
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);
}
}
//g_oalLastSysIntr = sysIntr;
return sysIntr;
}
//------------------------------------------------------------------------------
第二处:
C:\WINCE500\PLATFORM\SMDK2440A\Src\Common\Intr_dvs\intr.c(284):// Function: OEMInterruptHandler
//------------------------------------------------------------------------------
//
// Function: OEMInterruptHandler
//
ULONG OEMInterruptHandler(ULONG ra)
{
UINT32 sysIntr = SYSINTR_NOP;
UINT32 irq, irq2, mask;
#ifdef DVS_EN
#if (DVS_METHOD == 1 || DVS_METHOD == 3)
unsigned int clkval_calc;
unsigned int i;
#endif //(DVS_METHOD == 1 || DVS_METHOD == 3)
#endif //DVS_EN
b_oalInterruptFlag = TRUE;
// Get pending interrupt(s)
irq = INREG32(&g_pIntrRegs->INTOFFSET);
#ifdef DVS_EN
#if (DVS_METHOD == 2)
#if ( USESWPWSAVING == 1)
if ( bPowerSaving != TRUE ) // do not power up, if cpu load is low.
#endif //( USESWPWSAVING == 1)
{
int i;
if ( GetCurrentVoltage() != V130 )
{
ChangeVoltage(HIGHVOLTAGE);
for(i=0;i<VOLTAGEDELAY;i++)
{
INREG32(&g_pPortRegs->GPFDAT); // for loop operation, just read.
}
DVS_OFF();
}
}
#elif (DVS_METHOD == 3)
if ( CurrentState == Active )
{
if ( GetCurrentVoltage() != V130 )
{
ChangeVoltage(HIGHVOLTAGE);
for(i=0;i<VOLTAGEDELAY;i++)
{
INREG32(&g_pPortRegs->GPFDAT); // for loop operation, just read.
}
DVS_OFF();
}
}
#endif //(DVS_METHOD == 3)
#endif //DVS_EN
// System timer interrupt?
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();
}
// Profiling timer interrupt?
else if (irq == IRQ_TIMER2)
{
// Mask and Clear the interrupt.
mask = 1 << irq;
SETREG32(&g_pIntrRegs->INTMSK, mask);
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// The rest is up to the profiling interrupt handler (if profiling
// is enabled).
//
if (g_pProfilerISR)
{
sysIntr = g_pProfilerISR(ra);
}
}
else if (irq == IRQ_EINT2) // Softreset
{
// RETAILMSG(1, (TEXT("g_pIntrRegs->INTMSK = 0x%x \r\n"), g_pIntrRegs->INTMSK));
// RETAILMSG(1, (TEXT("OEMInterruptHandler: irq = %d \r\n"), irq));
#ifdef DVS_EN
#if (DVS_METHOD == 1 || DVS_METHOD == 3)
ChangeVoltage(HIGHVOLTAGE);
for(i=0;i<VOLTAGEDELAY;i++)
{
INREG32(&g_pPortRegs->GPFDAT); // for loop operation, just read.
}
DVS_OFF();
//switch ( HCLKDIV )
switch ( 4 )
{
case 4:
CLKDIV124();
break;
case 6:
CLKDIV136();
break;
case 8:
CLKDIV148();
break;
}
#endif //(DVS_METHOD == 1 || DVS_METHOD == 3)
#endif //DVS_EN
OALIoCtlHalReboot(IOCTL_HAL_REBOOT,NULL,0,NULL,0,NULL);
sysIntr = SYSINTR_NOP;
}
#ifdef DVS_EN
#if (DVS_METHOD == 1)
else if(irq == IRQ_LCD)
{
VSYNCINTR = TRUE;
if ( IDLEflag == FALSE )
{
// 4.1 Mask LCD VSYNC Interrupt
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK ) | (1 << IRQ_LCD ));
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
if (INREG32(&g_pLCDRegs->LCDCON5) & (0x3 << 15) ) // Check VSync Area.
{
}
else
{
CurrStateIdle = FALSE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
// 4.2 Return Voltage
ChangeVoltage(HIGHVOLTAGE);
// 4.3 Delay
for(i=0;i<VOLTAGEDELAY;i++)
{
INREG32(&g_pPortRegs->GPFDAT); // for loop operation, just read.
}
// 4.4 DVS_ON = 0 (6:6:6->1:6:6)
DVS_OFF();
// 4.5 Return HCLK
// (1:6:6) -> (1:6:12) -> (1:3:6)
CLKDIV136();
clkval_calc = (WORD)((float)(S3C2440A_HCLK)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
}
}
else
{
// 2.1 Mask LCD VSYNC Interrupt
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK) | (1 << IRQ_LCD)); // disable LCD interrupt
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
if (INREG32(&g_pLCDRegs->LCDCON5) & (0x3 << 15) ) // Check VSync Area.
{
}
else
{
CurrStateIdle = TRUE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
// 2.2 Change HCLK Freq by half
// (1:3:6) -> (1:6:12) -> (1:6:6)
CLKDIV166();
// 2.3 DVS_ON = 1 (6:6:6)
DVS_ON();
// 2.4 Drop Voltage
ChangeVoltage(LOWVOLTAGE);
clkval_calc = (WORD)((float)(S3C2440A_HCLK/2)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
}
}
sysIntr = SYSINTR_NOP;
}
#elif (DVS_METHOD == 3)
else if( irq == IRQ_LCD )
{
VSYNCINTR = TRUE;
if ( NextState == Active )
{
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK) | (1 << IRQ_LCD)); // Disable LCD interrupt
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
CurrStateIdle = FALSE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
ChangeVoltage(HIGHVOLTAGE);
for(i=0;i<VOLTAGEDELAY;i++)
{
INREG32(&g_pPortRegs->GPFDAT); // for loop operation, just read.
}
DVS_OFF();
CLKDIV136();
clkval_calc = (WORD)((float)(S3C2440A_HCLK)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
CurrentState = Active;
NextState = 0;
}
else if ( NextState == SlowActive )
{
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK) | (1 << IRQ_LCD)); // Disable LCD interrupt
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
CurrStateIdle = FALSE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
CLKDIV136();
clkval_calc = (WORD)((float)(S3C2440A_HCLK)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
CurrentState = SlowActive;
NextState = 0;
}
else if ( NextState == LazyActive )
{
// 2.1 Mask LCD VSYNC Interrupt
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK) | (1 << IRQ_LCD)); // Disable LCD interrupt
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
CurrStateIdle = TRUE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
CLKDIV166();
DVS_ON();
ChangeVoltage(LOWVOLTAGE);
clkval_calc = (WORD)((float)(S3C2440A_HCLK/2)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
CurrentState = LazyActive;
NextState = 0;
}
else if ( NextState == DeepIdle )
{
// 2.1 Mask LCD VSYNC Interrupt
OUTREG32(&g_pIntrRegs->INTMSK, INREG32(&g_pIntrRegs->INTMSK) | (1 << IRQ_LCD)); // Disable LCD interrupt
if( INREG32(&g_pLCDRegs->LCDSRCPND) & 2) OUTREG32(&g_pLCDRegs->LCDSRCPND, 0x2);
if( INREG32(&g_pLCDRegs->LCDINTPND) & 2) OUTREG32(&g_pLCDRegs->LCDINTPND, 0x2);
if( INREG32(&g_pIntrRegs->SRCPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->SRCPND, 0x1 << IRQ_LCD);
if( INREG32(&g_pIntrRegs->INTPND) & ( 0x1 << IRQ_LCD)) OUTREG32(&g_pIntrRegs->INTPND, 0x1 << IRQ_LCD);
CurrStateIdle = TRUE;
OUTREG32(&g_pLCDRegs->LCDCON1, INREG32(&g_pLCDRegs->LCDCON1) & ~(0x1)); //Disable LCD Output
CLKDIV166();
DVS_ON();
ChangeVoltage(LOWVOLTAGE);
clkval_calc = (WORD)((float)(S3C2440A_HCLK/2)/(2.0*5000000)+0.5)-1;
OUTREG32(&g_pLCDRegs->LCDCON1, (clkval_calc << 8)|(MVAL_USED << 7)|(3 << 5)|(12 << 1)|(1 << 0));
NextState = 0;
}
sysIntr = SYSINTR_NOP;
}
#endif //(DVS_METHOD == 3)
#endif //DVS_EN
else
{
#ifdef OAL_ILTIMING
if (g_oalILT.active) {
g_oalILT.isrTime1 = OALTimerCountsSinceSysTick();
g_oalILT.savedPC = 0;
g_oalILT.interrupts++;
}
#endif
if (irq == IRQ_EINT4_7 || irq == IRQ_EINT8_23) { // 4 or 5
// Find external interrupt number
mask = INREG32(&g_pPortRegs->EINTPEND);
mask &= ~INREG32(&g_pPortRegs->EINTMASK);
mask = (mask ^ (mask - 1)) >> 5;
irq2 = IRQ_EINT4;
while (mask != 0) {
mask >>= 1;
irq2++;
}
// Mask and clear interrupt
mask = 1 << (irq2 - IRQ_EINT4 + 4);
SETREG32(&g_pPortRegs->EINTMASK, mask);
OUTREG32(&g_pPortRegs->EINTPEND, mask);
// Clear primary interrupt
mask = 1 << irq;
OUTREG32(&g_pIntrRegs->SRCPND, mask);
OUTREG32(&g_pIntrRegs->INTPND, mask);
// From now we care about this irq
irq = irq2;
} else {
// Mask and clear interrupt
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);
}
}
return sysIntr;
}
//------------------------------------------------------------------------------
很明显,这个OALIoCtlHalReboot(IOCTL_HAL_REBOOT,NULL,0,NULL,0,NULL);函数实现了软件复位。现在两个地方出现了这个函数,只要把这个去掉就可以了。哦,不行不能直接屏蔽的。还要做一些处理,因为在外部串口中还是需用到这个中断的。暂时不管先,先看看这个OALIoCtlHalReboot是怎么实现重启的吧。
在C:\WINCE500\PLATFORM\SMDK2440A\Src\Inc\ioctl_tab.h(43):{ IOCTL_HAL_REBOOT, 0, OALIoCtlHalReboot }, 从这个可以看出在wince起来之后可以使用KernelIOControl函数来实现软件复位的。找到源码所在C:\WINCE500\PLATFORM\SMDK2440A\Src\Common\Ioctl\reboot.c(27):BOOL OALIoCtlHalReboot(UINT32 code, VOID *pInpBuffer,
//------------------------------------------------------------------------------
// it only uses the watchdog timer to assert reset 看门狗复位?狗没有喂就会重新启动?貌似在单片机中实现过。哈哈,厉害。
// Function: OALIoCtlHalReboot
//
//
BOOL OALIoCtlHalReboot(UINT32 code, VOID *pInpBuffer,
UINT32 inpSize, VOID *pOutBuffer,
UINT32 outSize, UINT32 *pOutSize)
{
//
// If the board design supports software-controllable hardware reset logic, it should be
// used. Because this routine is specific to the S3C2440A CPU, it only uses the watchdog
// timer to assert reset. One downside to this approach is that nRSTOUT isn't asserted
// so any board-level logic isn't reset via this method. This routine can be overidden in
// the specific platform code to control board-level reset logic, should it exist.
//
volatile S3C2440A_WATCHDOG_REG *pWDRegs = (volatile S3C2440A_WATCHDOG_REG *)
OALPAtoVA(S3C2440A_BASE_REG_PA_WATCHDOG, FALSE);
OALMSG(OAL_IOCTL&&OAL_FUNC, (L"+OALIoCtlHalReboot\r\n"));
// Setup the watchdog.
//
pWDRegs->WTDAT = 0;
pWDRegs->WTCNT = 5; // Load count with low value.
pWDRegs->WTCON = 0x8021; // Enable watchdog timer...
// Wait for watchdog reset...
//
while(TRUE);
// Should never get to this point...
//
OALMSG(OAL_IOCTL&&OAL_FUNC, (L"-OALIoCtlHalReboot\r\n"));
return(TRUE);
}
//------------------------------------------------------------------------------
关于中断的详细过程,请看这个帖子http://www.cnblogs.com/wogoyixikexie/archive/2009/02/04/1383849.html