An Interrupt Service Thread (IST) is the way that drivers in Windows CE service an interrupt. Typically, a driver will contain a thread function that handles the interrupt, and does nothing else. A typical IST will do the following:
1.       Create an Event
2.       Register with the kernel
3.       Set the thread priority
4.       Wait for the event to be signaled
5.       Service the hardware
6.       Tell the kernel that the interrupt has been serviced
7.       Repeat steps 3, 4 and 5
There are certainly variations in how ISTs are written and in what they actually do. These steps are the basic things that an IST needs to do though. The following will look into these steps further.
Create an Event
The kernel will signal an event to tell the IST that the interrupt that it is responsible for has been triggered. The event does not need to be named and it can be manual or automatically reset. Example:
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
This creates an unnamed event that is not signaled on creation and will be automatically reset when the IST sees it signaled. More on that in Wait for the Event to be Signaled below.
Quite often the event is created in the driver’s Init function so that it can be cleanly closed in the Deinit function. It can just as easily be created and closed in the IST itself. This is just one of the many differences between ISTs.
Register with the Kernel
The IST registers with the kernel by calling InterruptInitialize(). InterruptInitialize() tells the kernel which sysintr value that the IST will service and passes the event handle that the kernel will signal. Example:
DWORD RetVal = InterruptInitialize( SysintrValue, hEvent, NULL, 0 );
The first parameter is a sysintr value. A sysintr value is an number that is mapped to the interrupt’s IRQ in the kernel. The mapping of sysintr to IRQ is BSP specific, which means that the OEM decides how to map them.
The real question then is how does the driver obtain a sysintr value to use for this call? There are several ways, including:
·         The OEM hard codes the value in the driver and the kernel. This is not tremendously flexible, but Windows CE is an embedded OS so in many cases this works very well.
·         The OEM hard codes the value in the kernel and puts the value in the registry for the driver to read.
·         The OEM provides support for IOCTL_HAL_REQUEST_SYSINTR. The driver then calls KernelIoControl with an IRQ to request a sysintr value.  The IRQ value is also BSP specific and determined by the OEM. Obtaining an IRQ value is very similar to obtaining a sysintr value. These same options are possible sources of the IRQ value, but the IOCTL is IOCTL_HAL_REQUEST_IRQ. Example request for a sysintr value:
DWORD IRQ = MYDRIVER_IRQ;
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &IRQ, sizeof(DWORD), &SysintrValue, sizeof(DWORD), NULL)
This passes in the IRQ value and receives the sysintr value in SysintrValue.
Set the Thread Priority
Setting the thread priority is actually an optional step. Without setting the thread priority, the IST will not be able to handle the interrupt in a timely manner. At the default thread priority, the IST will share time with applications and may even be blocked by applications. Example:
CeSetThreadPriority(GetCurrentThread(), 150);
This gets the thread ID for the current thread by calling GetCurrentThread() and passes in a new thread priority for the thread. This example uses a hardcoded priority value, but I recommend that the driver read the priority from the registry which makes it easier to adjust priorities to fine tune the system when needed.
Wait for the Event to be Signaled
At this point the driver has initialized the interrupt handling and is ready to wait for an interrupt to be triggered. The way to wait for an interrupt is to call WaitForSingleObject(). Note: The only way to wait is to call WaitForSingleObect(). WaitForMultipleObjects() is documented that it should not be used for an interrupt event. From personal experience, I can tell you that WaitForMultipleObjects() will not work. I tried it before reading that documentation.
Example:
RetVal = WaitForSingleObject( hEvent, 2000 );
This waits for the interrupt event, hEvent, to be set. It times out after 2 seconds, which can be useful for checking to see if the driver is being unloaded.
Service the Hardware
This is the hard part of the IST. It is up to the driver developer to read the datasheets for the hardware and write code to service the interrupt. In some cases this involves reading data from a chip and can be complex, in other cases this could simply count the interrupts. That is up to the requirements of the driver and the hardware.
Tell the Kernel that the Interrupt has been Serviced
Finally, the IST must tell the kernel that it has finished servicing the interrupt. The IST does this by calling InterruptDone(). The kernel responds to this call by re-enabling the interrupt. Example:
InterruptDone( SysintrValue );
The parameter passed into InterruptDone() is the sysint value used when calling InterruptInitialize().
Tags: Drivers
Copyright © 2009 – Bruce Eitman
All Rights Reserved