通过powerButton驱动分析WINCE的中断实现

前两天有一个朋友问了我s3c2410 WINCE挂起/唤醒的实现,这两天我研究了一下s3c2410的PowerButton驱动,对WINCE的中断流程又有了进一步的了解,下面我就写一下我的心得和大家交流一下。
首先和PowerButton驱动有关的文件为:
1)$(_WINCEROOT)\PLATFORM\smdk2410\DRIVERS\PWRBTN文件夹下面的三个文件pwrbtn2410.c、 pwrbtn2410.h和pwrbtn2410.def,这个PowerButton驱动是通过流驱动实现的,实现的过程很简单,下面会详细说明。
2)$(_WINCEROOT)\PLATFORM\smdk2410\INC\oalintr.h,定义非内核模式的中断号( non-kernel interrupt identifiers )
3)$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL\cfw.c,这里面主要实现了 OEMInitInterrupts、OEMInterruptEnable、OEMInterruptDisable、 OEMInterruptDone这几个重要的中断函数。
4)$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL\ARM\armint.c,这里主要实现了OEMInterruptHandler这个中断处理函数。
下面我简单分析一下中断处理过程。
a)   首先你为自己的硬件(键盘,按键等需要使用的中断)定义一个中断名称,比如这个电源按键就起了一个中断名称SYSINTR_POWER,然后在oalintr.h里面把它定义成SYSINTR_FIRMWARE+n的形式
比如:     #define SYSINTR_POWER         (SYSINTR_FIRMWARE+13)
n必须小于SYSINTR_MAXUMUM or SYSINTR_FIRMWARE+23
b)在cfw.c的OEMInitInterrupts中进行一些中断初始化工作,主要就是屏蔽所有中断,清除中断挂起等工作,代码我就不详细说明了,比 较简单。在OEMInterruptEnable(这个函数会被InterruptInitialize函数调用)函数中主要进行中断开启工作,当驱动使 用InterruptInitialize的时候(比如InterruptInitialize(SYSINTR_POWER, gPwrButtonIntrEvent, 0, 0))就会把SYSINTR_POWER中断号传入,然后开启EIN0这个中断,并且把SYSINTR_POWER中断和事件 gPwrButtonIntrEvent连接起来,代码如下:
INTERRUPTS_OFF(); //关闭所有中断
 switch (idInt)
 {......
case SYSINTR_POWER:
  s2410INT->rSRCPND  = BIT_EINT0;
  // S3C2410X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
  if (s2410INT->rINTPND & BIT_EINT0) s2410INT->rINTPND = BIT_EINT0;
  s2410INT->rINTMSK &= ~BIT_EINT0;
  s2410INT->rSRCPND  = BIT_EINT2;
  // S3C2410X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
  if (s2410INT->rINTPND & BIT_EINT2) s2410INT->rINTPND = BIT_EINT2;
  s2410INT->rINTMSK &= ~BIT_EINT2;
  break;
.....
}
INTERRUPTS_ON();//开启所有中断
 
这个文件里面还实现了OEMInterruptDisable函数用来禁止中断,与PowerButton相关的函数如下:
INTERRUPTS_OFF();
switch (idInt)
 {....
 case SYSINTR_POWER:
  s2410INT->rINTMSK |= BIT_EINT0;
  s2410INT->rINTMSK |= BIT_EINT2;
  break;        
  .....
 }
 INTERRUPTS_ON();
在这个文件中,还实现了OEMInterruptDone函数,做一些中断处理结束后的事情,当驱动调用InterruptDone时会把中断号传到这个函数来使用这个函数,与PowerButton相关的函数如下:
 INTERRUPTS_OFF(); 
 switch (idInt)
 {...
 case SYSINTR_POWER:
  s2410INT->rSRCPND = BIT_EINT0;
  if (s2410INT->rINTPND & BIT_EINT0) s2410INT->rINTPND = BIT_EINT0;
  s2410INT->rINTMSK &= ~BIT_EINT0;
  s2410INT->rSRCPND = BIT_EINT2;
  if (s2410INT->rINTPND & BIT_EINT2) s2410INT->rINTPND = BIT_EINT2;
  s2410INT->rINTMSK &= ~BIT_EINT2;
  break;
 }
 INTERRUPTS_ON();
以上几个中断函数都相当重要,而且功能我也讲得很清楚了,大家应该理解了吧^_^。
c)在armint.c中主要实现了OEMInterruptHandler这个中断处理函数,当有硬件中断来的时候会进入这个处理函数,我们看看与PowerButton有关的代码:
 else if (IntPendVal == INTSRC_EINT0)  { // POWER BUTTON中断
  s2410INT->rINTMSK |= BIT_EINT0;
  s2410INT->rSRCPND  = BIT_EINT0; /* Interrupt Clear    */
  if (s2410INT->rINTPND & BIT_EINT0) s2410INT->rINTPND  = BIT_EINT0;
  return(SYSINTR_POWER); //返回一个中断号通知系统发生了什么中断,系统通过这个中断产生一个事件//给IST使用。
在这里,我们把PowerButton和EINT0联系起来了,并且如果EINT0来了中断,就会返回系统一个中断号SYSINTR_POWER。
d)我们下面再来看看PowerButton驱动的实现。在pbut2410.c文件里,我们首先看看动态链接库的init实现:
PUBLIC DWORD
DSK_Init(DWORD dwContext)
{
 do
 {
  gPwrButtonIntrThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) PBT_IntrThread, 0, 0, &IDThread);//创建了一个PBT_IntrThread线程,这个就是PowerButton的IST
  if (gPwrButtonIntrThread == 0)
  {
   break;
  }
 } while (0);
}
下面我们看看PBT_IntrThread的实现:
DWORD
PBT_IntrThread(PVOID pArg)
{
 PBT_InitializeAddresses();//得到EINI0口的虚拟地址
 PBT_EnableInterrupt(); //使能EINI0口中断
 gPwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//创建一个事件//PowerButton事件
 if (!(InterruptInitialize(SYSINTR_POWER, gPwrButtonIntrEvent, 0, 0)))
 //通知系统使能SYSINTR_POWER这个中断,并且当这个中断产生时产生一个gPwrButtonIntrEvent事件,//第一个参数为与这个IST连接的中断ID,第二个参数为中断产生是产生的事件
 {
  RETAILMSG(1, (TEXT("::: SYSINTR_POWER Init Fail\r\n")));
 }
 while (1)
 {
  WaitForSingleObject(gPwrButtonIntrEvent, INFINITE);//等待中断发生
     if (gOffFlag == FALSE)
  {
   if (PBT_IsPushed())   /* To Filter Noise    *///判断是否是噪声
   {
    Sleep(200);
    if (PBT_IsPushed())
    { 
    }
    else
    {//如果不是噪声则:
                                    #if (WINCEOSVER >= 400)
                                        if(gpfnSetSystemPowerState != NULL)
                                        {
                                            gpfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE);
                                        }
                                        else
                                        {
                                            PowerOffSystem();//调用PowerOffSystem函数,在这//个函数里面又会调用OEMPowerOff函数,这个函数在power.c里
                                        }
                                    #else
                                        PowerOffSystem();
                                    #endif
                                    DriverSleep(0, FALSE);
    }
   }
   InterruptDone(SYSINTR_POWER);//通知系统调用OEMInterruptDone
     }
    }         
}
在看看一些函数
PRIVATE VOID
PBT_EnableInterrupt(VOID)
{
     v_pIOPregs->rGPFCON  &= ~(0x3 << 0);      /* 设置GPF0) 为 EINT0                     */
     v_pIOPregs->rGPFCON  |=  (0x2 << 0);
    v_pIOPregs->rEXTINT0 &= ~(0x7 << 0);       /* 配置EINT0为下降沿模式                 */
    v_pIOPregs->rEXTINT0 |=  (0x2 << 0);
}
 
PRIVATE BOOL
PBT_IsPushed(VOID)
{//判断GPF0是否被按下
     return ((v_pIOPregs->rGPFDAT & (1 << 0)) ? FALSE : TRUE);
}
 
PRIVATE BOOL
PBT_InitializeAddresses(VOID)
{//分配EINT0的虚拟地址供驱动使用
     BOOL RetValue = TRUE;      
     /* IO Register Allocation */
     v_pIOPregs = (volatile IOPreg *)VirtualAlloc(0, sizeof(IOPreg), MEM_RESERVE, PAGE_NOACCESS);
     if (v_pIOPregs == NULL)
     {
         ERRORMSG(1,(TEXT("For IOPregs : VirtualAlloc failed!\r\n")));
         RetValue = FALSE;
     }
     else
     {
         if (!VirtualCopy((PVOID)v_pIOPregs, (PVOID)(IOP_BASE), sizeof(IOPreg), PAGE_READWRITE | PAGE_NOCACHE))
         {
              ERRORMSG(1,(TEXT("For IOPregs: VirtualCopy failed!\r\n")));
              RetValue = FALSE;
         }
     }
    
     if (!RetValue)
     {
//       RETAILMSG (1, (TEXT("::: PBT_InitializeAddresses - Fail!!\r\n") ));
 
         if (v_pIOPregs)
         {
              VirtualFree((PVOID) v_pIOPregs, 0, MEM_RELEASE);
         }
 
         v_pIOPregs = NULL;
     }
     else RETAILMSG (1, (TEXT("::: PBT_InitializeAddresses - Success\r\n") ));
 
     return(RetValue);
}
 
总结:
从上面PowerButton这个驱动我们就能把WINCE的中断处理过程了解清楚,基本的过程是首先在oalinitr.h中把中断ID define成SYSINTR_FIRMWARE+N的形式,然后
OEMInitInterrupts(cfw.c) -> OEMInterruptEnable(cfw.c) -> 硬件中断到达 -> OEMInterruptHandler(armint.c) -> 自己写的中断服务线程(IST)-> OEMInterruptDone(cfw.c)
其中IST的流程一般为:
创建一个事件CreateEvent -> InterruptInitialize(SYSINTR_XXXX, XXXXXEvent, 0, 0) 把中断ID和IST联系起来,并且把中断ID和事件XXXXXEvent联系起来 -> WaitForSingleObject(XXXXXEvent, INFINITE)等待这个事件的产生,由于事件和中断联系起来了,实际就是等待中断的产生 -> 实际的中断处理过程 -> InterruptDone(SYSINTR_ XXXX) 通过这个函数调用OEMInterruptDone

你可能感兴趣的:(职场,WinCE,驱动,休闲,powerButton)