8. The interrupt support handler unmasks all interrupts, with the exception of the interrupt currently in processing. Keeping the current interrupt masked off explicitly prevents the current hardware device from triggering another interrupt while the IST runs.
9. The IST runs in response to the signaled event to perform and finish the interrupt handling without blocking other devices on the system.
10. The IST calls the InterruptDone function to inform the kernel's interrupt support handler that the IST has finished its processing and is ready for another interrupt event.
11. The interrupt support handler calls the OEMInterruptDone function in the OAL to complete the interrupt handling process and reenable the interrupt.
Interrupt Service Routines
In general, ISRs are small blocks of code that run in response to a hardware interrupt. Because the kernel exception handler masks off all interrupts of equal or lesser priority while this ISR runs, it is important to complete the ISR and return a SYSINTR value as quickly as possible so that the kernel can re-enable (unmask) all IRQs with minimal delay (except the currently processed interrupt). System performance can suffer significantly if too much time is spent in ISRs, leading to missed interrupts or overrun buffers on some devices. Another important aspect is that the ISR runs in kernel mode and does not have access to higher-level operating system APIs. For these reasons, ISRs usually perform no more than the most basic tasks, such as quickly copying data from hardware registers to memory buffers. On Windows Embedded CE, time-consuming interrupt processing is usually performed in an IST.
The primary task of the ISR is to determine the interrupt source, mask off or clear the interrupt at the device, and then return a SYSINTR value for the interrupt to notify the kernel about an IST to run. In the simplest case, the ISR returns SYSINTR_NOP to indicate that no further processing is necessary. Accordingly, the kernel does not signal an event for an IST to handle the interrupt. On the other hand, if the device driver uses an IST to handle the interrupt, the ISR passes the logical interrupt identifier to the kernel, the kernel determines and signals the interrupt event, and the IST typically resumes from a WaitForSingleObject call and executes the interrupt processing instructions in a loop. The latency between the ISR and the IST depends on the priority of the thread and other threads running in the system, as explained in Chapter 3, "Performing System Programming." Typically, ISTs run with a high thread priority.
Interrupt Service Threads
An IST is a regular thread that performs additional processing in response to an interrupt, after the ISR has completed. The IST function typically includes a loop and a WaitForSingleObject call to block the thread infinitely until the kernel signals the specified IST event, as illustrated in the following code snippet. However, before you can use the IST event, you must call InterruptInitialize with the SYSINTR value and an event handle as parameters so that the CE kernel can signal this event whenever an ISR returns the SYSINTR value. Chapter 3 provides detailed information about multithreaded programming and thread synchronization based on events and other kernel objects.
CeSetThreadPriority(GetCurrentThread(), 200);
// Loop until told to stop
while(!pIst->stop) {
// Wait for the IST event.
WaitForSingleObject(pIst->hevIrq, INFINITE)
// Handle the interrupt.
InterruptDone(pIst->sysIntr);
}
When the IST has completed processing an IRQ, it should call InterruptDone to inform the system that the interrupt was processed, that the IST is ready to handle the next IRQ, and that the interrupt can be reenabled by means of an OEMInterruptDone call. Table 6-6 lists the OAL functions that the system uses to interact with the interrupt controller to manage interrupts.
Table 6-6 OAL functions for interrupt management
Function | Description |
---|---|
OEMInterruptEnable | This function is called by the kernel in response to InterruptInitialize and enables the specified interrupt in the interrupt controller. |
OEMInterruptDone | This function is called by the kernel in response to InterruptDone and should unmask the interrupt and acknowledge the interrupt in the interrupt controller. |
OEMInterruptDisable | This function disables the interrupt in the interrupt controller and is called in response to the InterruptDisable function. |
OEMInterruptHandler | For ARM processors only, this function identifies the interrupt SYSINTR that occurs by looking at the status of the interrupt controller. |
HookInterrupt | For processors other than ARM, this function registers a callback function for a specified interrupt ID. This function must be called in the OEMInit function to register mandatory interrupts. |
OEMInterruptHandlerFIQ | For ARM processors, used to handle interrupts for the Fast Interrupt (FIQ) line. |
Do not use the WaitForMultipleObjects function to wait for an interrupt event. If you must wait for multiple interrupt events, you should create an IST for each interrupt.
Interrupt Identifiers (IRQ and SYSINTR)
Each hardware interrupt line corresponds to an IRQ value in the interrupt controller registers. Each IRQ value can be associated with only one ISR, but an ISR can map to multiple IRQs. The kernel does not need to maintain the IRQs. It just determines and signals events associated with the SYSINTR values returned from the ISR in response to the IRQ. The ability to return varying SYSINTR values from an ISR provides the basis to support multiple devices that use the same shared interrupt.
Target devices that only support a single IRQ, such as ARM-based systems, use the OEMInterruptHandler function as the ISR to identify the embedded peripheral that triggered the interrupt. Original equipment manufacturers (OEMs) must implement this function as part of the OAL. On platforms that support multiple IRQs, such as Intel x86-based systems, you can associate the IRQs with individual ISRs by calling HookInterrupt.
Static Interrupt Mappings
For the ISR to determine a correct SYSINTR return value there must be a mapping between the IRQ and the SYSINTR, which can be hardcoded into the OAL. The Bsp_cfg.h file for the Device Emulator BSP demonstrates how to define a SYSINTR value in the OAL for a target device relative to the SYSINTR_FIRMWARE value. If you want to define additional identifiers in your OAL for a custom target device, keep in mind that the kernel reserves all values below SYSINTR_FIRMWARE for future use and the maximum value should be less than SYSINTR_MAXIMUM.