When adapting Windows Embedded CE to a custom target device, it is important to keep the OAL as generic as possible. Otherwise, you have to modify the code every time you add a new component to the system. To provide flexibility and adaptability, Windows Embedded CE supports installable ISRs (IISR), which a device driver can load into kernel space on demand, such as when new peripheral devices are connected in a Plug and Play fashion. Installable ISRs also provide a solution to process interrupts when multiple hardware devices share the same interrupt line. The ISR architecture relies on lean DLLs that contain the code of the installable ISR and export the entry points summarized in Table 6-7.
Table 6-7 Exported installable ISR DLL functions
Function | Description |
---|---|
ISRHandler | This function contains the installable interrupt handler. The return value is the SYSINTR value for the IST that you want to run in response to the IRQ registered for the installable ISR in the call to the LoadIntChainHandler function. The OAL must support chaining on at least that IRQ, which means that an unhandled interrupt can be chained to another handler (which is the installed ISR in this case) when an interrupt occurs. |
CreateInstance | This function is called when an installable ISR is loaded by using the LoadIntChainHandler function. It returns an instance identifier for the ISR. |
DestroyInstance | This function is called when an installable ISR is unloaded by using the FreeIntChainHandler function. |
IOControl | This function supports communication from the IST to the ISR. |
To help you implement installable ISRs, Microsoft provides a generic installable ISR sample that covers the most typical needs for many devices. You can find the source code in the following folder: %_WINCEROOT% PublicCommonOakDriversGiisr.
Registering an IISR
The LoadIntChainHandler function expects three parameters that you must specify to load and register an installable ISR. The first parameter (lpFilename) specifies the filename of the ISR DLL to load. The second parameter (lpszFunctionName) identifies the name of the interrupt handler function, and the third parameter (bIRQ) defines the IRQ number for which you want to register the installable ISR. In response to hardware disconnect, a device driver can also unload an installable ISR by calling the FreeIntChainHandler function.
External Dependencies and Installable ISRs
It is important to keep in mind that LoadIntChainHandler loads ISR DLLs into kernel space, which means that the installable ISR cannot call high-level operating system APIs and cannot import or implicitly link to other DLLs. If the DLL has explicit or implicit links to other DLLs, or if it uses the C run-time library, the DLL will not be able to load. The installable ISR must be completely self-sufficient.
To ensure that an installable ISR does not link to the C run-time libraries or any DLLs, you must add the following lines to the Sources file in your DLL subproject:
NOMIPS16CODE=1
NOLIBC=1
The NOLIBC=1 directive ensures that the C run-time libraries are not linked and the NOMIPS16CODE=1 option enables the compiler option /QRimplicit-import, which prevents implicit links to other DLLs. Note that this directive has absolutely no relationship to the Microprocessor without Interlocked Pipeline Stages (MIPS) CPU.
Lesson Summary
Windows Embedded CE relies on ISRs and ISTs to respond to interrupt requests triggered by internal and external hardware components that require the attention of the CPU outside the normal code execution path. ISRs are typically compiled directly into the kernel or implemented in device drivers loaded at boot time and registered with corresponding IRQs through HookInterrupt calls. You can also implement installable ISRs in ISR DLLs, which device drivers can load on demand and associate with an IRQ by calling LoadIntChainHandler. Installable ISRs also enable you to support interrupt sharing. For example, on a system with only a single IRQ, such as an ARM-based device, you can modify the OEMInterruptHandler function, which is a static ISR that loads further installable ISRs depending on the hardware component that triggered the interrupt.
Apart from the fact that ISR DLLs must not have any dependencies on external code, ISRs and installable ISRs have many similarities. The primary task of an interrupt handler is to determine the interrupt source, mask off or clear the interrupt at the device, and then return a SYSINTR value for the IRQ to notify the kernel about an IST to run. Windows Embedded CE maintains interrupt mapping tables that associate IRQs with SYSINTR values. You can define static SYSINTR values in the source code or use dynamic SYSINTR values that you can request from the kernel at run time. By using dynamic SYSINTR values, you can increase the portability of your solutions.
According to the SYSINTR value, the kernel can signal an IST event which enables the corresponding interrupt service thread to resume from a WaitForSingleObject call. By performing most of the work to handle the IRQ in the IST instead of the ISR, you can achieve optimal system performance because the system blocks interrupt sources with lower or equal priority only during ISR execution. The kernel unmasks all interrupts when the ISR finishes, with the exception of the interrupt currently in processing. The current interrupt source remains blocked so that a new interrupt from the same device cannot interfere with the current interrupt handling procedure. When the IST has finished its work, the IST must call InterruptDone to inform the kernel that it is ready for a new interrupt so that the kernel's interrupt support handler can reenable the IRQ in the interrupt controller.