Share via


Interrupt Service Threads (Windows Embedded CE 6.0)

1/6/2010

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.

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 system 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

Concepts

Interrupts

Other Resources

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