Interrupt Service Threads (Windows CE 5.0)

Send Feedback

The interrupt service thread (IST) is a thread that does most of the interrupt processing. The OS wakes the IST when the OS has an interrupt to process. Otherwise, the IST is idle. ISTs for a keyboard, touch screen, mouse, and display driver must call SetKMode(TRUE) to allow GWES to write to the shared memory heap.

For the OS to wake the IST, the IST must associate an event object with an interrupt identifier. Use the CreateEvent function to create an event object. After an interrupt is processed, the IST should wait for the next interrupt signal. This call is usually inside a loop.

When the hardware interrupt occurs, the kernel signals the event on behalf of the ISR, and then the IST performs necessary I/O operations in the device to collect the data and process it. When the interrupt processing completes, the IST should inform the kernel to re-enable the hardware interrupt.

Usually, IST threads run at above-normal priority. They can boost themselves to a higher priority before registering their event with the kernel by calling CeSetThreadPriority.

The following table shows the functions that the IST commonly uses.

Function Description
InterruptInitialize Links the event with the interrupt identifier of the ISR.
WaitForSingleObject Returns when the specified object is in the signaled state or when the time-out interval elapses.
InterruptDone Instructs the kernel to re-enable the hardware interrupt related to this thread.

The following lists shows examples of what an IST might do at startup:

  • Create a structure for storing interrupt values.
  • Use CreateEvent as the IST trigger.
  • Read the IRQ and SysIntr values from the registry and allow the OAL to map the IRQ to the SYSINTR value before the driver loads.
  • Store the handle for the thread you create.

The following code example from %_WINCEROOT%\Public\Common\OAK\Drivers\Keybd\Ps2_8042\Ps2keybd.cpp shows a typical IST.

BOOL
Ps2Keybd::
IsrThreadProc(
  void
  )
{
  DWORD dwTransferred = 0;
  HKEY hk;
  DWORD dwStatus, dwSize, dwValue, dwType;
  int iPriority = 145;

  // look for our priority in the registry
  dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\KEYBD"), 0, 0, &hk);
  if(dwStatus == ERROR_SUCCESS) {
  // See if you have to enable your interrupt to wake the sustem from suspend.
    dwSize = sizeof(dwValue);
    dwStatus = RegQueryValueEx(hk, _T("EnableWake"), NULL, &dwType, (LPBYTE) &dwValue, &dwSize);
    if(dwStatus == ERROR_SUCCESS && dwType == REG_DWORD) {
      if (dwValue != 0) {
        m_pp2p->SetWake(TRUE);
      }
    }

    // get interrupt thread priority
    dwSize = sizeof(dwValue);
    dwStatus = RegQueryValueEx(hk, _T("Priority256"), NULL, &dwType, (LPBYTE) &dwValue, &dwSize);
    if(dwStatus == ERROR_SUCCESS && dwType == REG_DWORD) {
      iPriority = (int) dwValue;
    }

    // read our sysintr
    dwSize = sizeof(dwValue);
    dwStatus = RegQueryValueEx(hk, _T("SysIntr"), NULL, &dwType, (LPBYTE) &dwValue, &dwSize);
    if(dwStatus == ERROR_SUCCESS) {
      if(dwType == REG_DWORD) {
        dwSysIntr_Keybd = dwValue;
      } else {
      dwStatus = ERROR_INVALID_PARAMETER;
      }
    }
    RegCloseKey(hk);
  }

  if(dwStatus != ERROR_SUCCESS) {
    goto leave;
  }

  // set the thread priority
  CeSetThreadPriority(GetCurrentThread(), iPriority);

  m_hevInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL);
  if ( m_hevInterrupt == NULL)
  {
    goto leave;
  }

  if ( !InterruptInitialize(dwSysIntr_Keybd, m_hevInterrupt, NULL, 0) )
  {
    goto leave;
  }

  if (m_pp2p->WillWake()) {
  // Ask the OAL to enable your interrupt to wake the system from suspend.
    DEBUGMSG(ZONE_INIT, (TEXT("Keyboard: Enabling wake from suspend\r\n")));
    BOOL fErr = KernelIoControl(IOCTL_HAL_ENABLE_WAKE, &dwSysIntr_Keybd,  
    sizeof(dwSysIntr_Keybd), NULL, 0, &dwTransferred);
    DEBUGCHK(fErr); // KernelIoControl should always succeed.
  }

  m_pp2p -> KeybdInterruptEnable();

  extern UINT v_uiPddId;
  extern PFN_KEYBD_EVENT v_pfnKeybdEvent;

  KEYBD_IST keybdIst;
  keybdIst.hevInterrupt = m_hevInterrupt;
  keybdIst.dwSysIntr_Keybd = dwSysIntr_Keybd;
  keybdIst.uiPddId = v_uiPddId;
  keybdIst.pfnGetKeybdEvent = KeybdPdd_GetEventEx2;
  keybdIst.pfnKeybdEvent = v_pfnKeybdEvent;

    KeybdIstLoop(&keybdIst);

leave:
    return 0;
}

The following code sample shows the implementation of the KeybdIstLoop function.

BOOL
 KeybdIstLoop (
    PKEYBD_IST pKeybdIst
    )
{
    SETFNAME(_T("KeybdIstLoop"));

    UINT32  rguiScanCode[16];
    BOOL    rgfKeyUp[16];
    UINT    cEvents;

    DEBUGCHK(pKeybdIst->hevInterrupt != NULL);
    DEBUGCHK(pKeybdIst->pfnGetKeybdEvent != NULL);
    DEBUGCHK(pKeybdIst->pfnKeybdEvent != NULL);

    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

wait_for_keybd_interrupt:
    if (WaitForSingleObject(pKeybdIst->hevInterrupt, INFINITE) == WAIT_OBJECT_0)
    {
        cEvents = (*pKeybdIst->pfnGetKeybdEvent)
            (pKeybdIst->uiPddId, rguiScanCode, rgfKeyUp);
            
        for (UINT iEvent = 0; iEvent < cEvents; ++iEvent) {
            (*pKeybdIst->pfnKeybdEvent)(pKeybdIst->uiPddId, 
                rguiScanCode[iEvent], rgfKeyUp[iEvent]);
        }
        // cEvents could be 0 if this was a partial scan code, such as 0xE0.

        InterruptDone(pKeybdIst->dwSysIntr_Keybd);
    }

    goto wait_for_keybd_interrupt;

    ERRORMSG(1, (TEXT("KeybdIstLoop: Keyboard driver thread terminating.\r\n")));
    return TRUE;
}

See Also

Defining an Interrupt Identifier | Implementing an ISR | Loader | PCI Bus Driver | Real-Time Priority System

Send Feedback on this topic to the authors

Feedback FAQs

© 2006 Microsoft Corporation. All rights reserved.