WinDriver provides you with API, DriverWizard code generation, and samples, to simplify the task of handling interrupts from your driver.
If you are developing a driver for a device based on one of the enhanced-support WinDriver chipsets [7], we recommend that you use the custom WinDriver interrupt APIs for your specific chip in order to handle the interrupts, since these routines are implemented specifically for the target hardware.
For other chips, we recommend that you use the DriverWizard to detect/define the relevant information regarding the device interrupt (such as the interrupt request (IRQ) number, its type and its shared state), define commands to be executed in the kernel when an interrupt occurs (if required), and then generate skeletal diagnostics code, which includes interrupt routines that demonstrate how to use WinDriver's API to handle your device's interrupts, based on the information that you defined in the wizard.
The following sections provide a general overview of PCI/PCMCIA/ISA interrupt handling and explain how to handle interrupts using WinDriver's API. Use this information to understand the sample and generated DriverWizard interrupt code or to write your own interrupt handler.
PCI, PCMIA and ISA hardware uses interrupts to signal the host.
There are two main methods of PCI interrupt handling:
Legacy Interrupts: The traditional interrupt handling, which uses a line-based mechanism. In this method, interrupts are signaled by using one or more external pins that are wired ''out-of-band'', i.e. separately from the main bus lines.
Legacy interrupts are divided into two groups:
Level-sensitive interrupts: These interrupts are generated as long as the physical interrupt signal is high. If the interrupt signal is not lowered by the end of the interrupt handling in the kernel, the operating system will call the kernel interrupt handler repeatedly causing the host platform to hang. To prevent such a situation, the interrupt must be acknowledged (cleared) by the kernel interrupt handler immediately when it is received.
Legacy PCI interrupts are level sensitive.
Edge-triggered interrupts: These are interrupts that are generated once, when the physical interrupt signal goes from low to high. Therefore, exactly one interrupt is generated. No special action is required in order to acknowledge this type of interrupt.
ISA/EISA interrupts are edge triggered.
MSI/MSI-X: Newer PCI bus technologies, available beginning with v2.2
of the PCI bus and in PCI Express, support Message-Signaled Interrupts (MSI). This
method uses "in-band" messages instead of pins and can target addresses
in the host bridge. A PCI function can request up to 32 MSI messages.
Note: MSI and MSI-X are edge triggered and do not require acknowledgement
in the kernel.
Among the advantages of MSIs:
Extended Message-Signaled Interrupts (MSI-X) are available beginning with version 3.0 of the PCI bus. This method provides an enhanced version of the MSI mechanism, which includes the following advantages:
The newer PCI buses, which support MSI/MSI-X, maintain software compatibility with the legacy line-based interrupts mecahnism by emulating legacy interrupts through in-band mechanisms. These emulated interrupts are treated as legacy interrupts by the host operating system.
WinDriver supports legacy line-based interrupts, both edge triggered and level sensitive, on all supported operating systems: Windows, Windows CE, and Linux. (For Windows CE, see specific information in section 9.2.8).
WinDriver also supports PCI MSI/MSI-X interrupts (when supported by the hardware) on Linux and Windows Vista and higher (earlier versions of Windows do not support MSI/MSI-X), as detailed in section 9.2.6.
WinDriver provides a single set of APIs for handling both legacy and MSI/MSI-X interrupts, as described in this manual.
![]() | |
|
This section describes how to use WinDriver to handle interrupts from a user-mode
application. Since interrupt handling is a performance-critical task, it is
very likely that you may want to handle the interrupts directly in the kernel.
WinDriver's Kernel PlugIn [11] enables
you to implement kernel-mode interrupt routines. To find out how to handle interrupts from the Kernel PlugIn, please refer to section 11.6.5 of the manual. |
The interrupt handling sequence using WinDriver is as follows:
WDC_IntEnable [B.3.46] or the
low-level InterruptEnable or WD_IntEnable
functions, described in the WinDriver PCI Low-Level API Reference – to enable interrupts
on the device.NOTE:
When WDC_IntEnable [B.3.46] or
the lower-level InterruptEnable function is called, WinDriver
spawns a thread
for handling incoming interrupts.
When using the low-level WD_IntEnable function you need to
spawn the thread yourself.
![]() | |
WinDriver must be registered with the OS as the driver of the device before
enabling interrupts. For Plug-and-Play hardware (PCI/PCI Express/PCMCIA) on
Windows platforms, this association is made by installing an INF file for the
device [15.1].
If the INF file is not installed, the interrupt enable function
will fail with a WD_NO_DEVICE_OBJECT error [B.9].
|
WDC_IntEnable or
InterruptEnable) is called.
WDC_IntDisable [B.3.47] or the low-level
InterruptDisable or WD_IntDisable functions,
described in the WinDriver PCI Low-Level API Reference (depending on the function used to
enable the interrupts).
![]() | |
|
When retrieving resources information for a Plug-and-Play device using
WDC_PciGetDeviceInfo [B.3.8] (PCI) or
WDC_PcmciaGetDeviceInfo [B.3.9] (PCMCIA), or the low-level
WD_PciGetCardInfo or WD_PcmciaGetCardInfo
function (described in the WinDriver PCI Low-Level API Reference), the function returns information
regarding the interrupt types supported by the hardware. This information is
returned within the dwOptions field of the returned interrupt
resource (pDeviceInfo->Card.Item[i].I.Int.dwOptions for the WDC
functions pPciCard->Card.Item[i].I.Int.dwOptions for the low-level
functions). The interrupt options bit-mask can contain a combination of any of
the following interrupt type flags:
INTERRUPT_MESSAGE_X : Extended Message-Signaled Interrupts (MSI-X).*INTERRUPT_MESSAGE : Message-Signaled Interrupts (MSI).*INTERRUPT_LEVEL_SENSITIVE : Legacy level-sensitive
interrupts.
INTERRUPT_LATCHED : Legacy edge-triggered interrupts.
The value of this flag is zero and it is applicable only when no other
interrupt flag is set.
The WDC_GET_INT_OPTIONS macro returns a WDC device's interrupt
options bit-mask [B.4.9]. You can pass the
returned bit-mask to the WDC_INT_IS_MSI macro to check whether
the bit-mask contains the MSI or MSI-X flags [B.4.10].
![]() | |
|
When attempting to enable interrupts for a PCI card on Linux or Windows Vista and higher, WinDriver first
tries to use MSI-X or MSI, if supported by the card. If this fails, WinDriver
attempts to enable legacy level-sensitive interrupts.
WinDriver's interrupt enable functions return information regarding the interrupt
type that was enabled for the card. This information is returned within the
dwEnabledIntType field of the WD_INTERRUPT
structure that was passed to the function. When using the high-level
WDC_IntEnable function, the information is stored within the
Int field of the WDC device structure referred to by the
function's hDev parameter [B.3.46],
and can be retrieved using the WDC_GET_ENABLED_INT_TYPE
low-level WDC macro [B.4.8].
When handling interrupts you may find the need to perform high-priority tasks at the kernel-mode level immediately when an interrupt occurs. For example, when handling level-sensitive interrupts, such as legacy PCI interrupts [9.2.1], the interrupt line must be lowered (i.e. the interrupt must be acknowledged) in the kernel, otherwise the operating system will repeatedly call WinDriver's kernel interrupt handler, causing the host platform to hang. Acknowledgment of the interrupt is hardware-specific and typically involves writing or reading from specific run-time registers on the device. PCMCIA interrupts also require hardware-specific kernel-mode interrupt handling.
WinDriver's interrupt enable functions receive an optional pointer to an array
of WD_TRANSFER structures [B.5.15],
which can be used to set up read/write transfer command from/to memory or I/O
addresses on the device.
The WDC_IntEnable function [B.3.46]
accepts this pointer and the number of commands in the array as direct
parameters (pTransCmds and dwNumCmds).
The low-level InterruptEnableand WDC_IntEnable
functions receive this information within the Cmd and
dwCmds fields of the WD_INTERRUPT structure that is
passed to them (see the WinDriver PCI Low-Level API Reference).
When you need to execute performance-critical transfers to/from your device
upon receiving an interrupt – e.g. when handling level-sensitive
interrupts – you should prepare an array of WD_TRANSFER
structures that contain the required information regarding the read/write
operations to perform in the kernel upon arrival of an interrupt, and pass this
array to WinDriver's interrupt enable functions. As explained in
section 9.2.2 (step #3), WinDriver's kernel-mode interrupt handler
will execute the transfer commands passed to it within the interrupt enable
function for each interrupt that it handles, before returning the control to
the user mode. Note: Memory allocated for the transfer commands must remain available
until the interrupts are disabled
.
The interrupt transfer commands array that you pass to WinDriver can also
contain an interrupt mask structure, which will be used to verify the source of
the interrupt. This is done by setting the transfer structure's
cmdTrans field, which defines the type of the transfer command, to
CMD_MASK, and setting the relevant mask in the transfer
structure's Data field [B.5.15]. Note
that interrupt mask commands must be set directly after a read transfer command
in the transfer commands array.
When WinDriver's kernel interrupt handler encounters a mask interrupt command, it masks the value that was read from the device in the preceding read transfer command in the array, with the mask set in the interrupt mask command. If the mask is successful, WinDriver will claim control of the interrupt, execute the rest of the transfer commands in the array, and invoke your user-mode interrupt handler routine when the control returns to the user mode. However, if the mask fails, WinDriver will reject control of the interrupt, the rest of the interrupt transfer commands will not be executed, and your user-mode interrupt handler routine will not be invoked. (Note: acceptance and rejection of the interrupt is relevant only when handling legacy interrupts; since MSI/MSI-X interrupts are not shared, WinDriver will always accept control of such interrupts.)
![]() | |
|
This section provides sample code for setting up interrupt transfer commands
using the WinDriver Card (WDC) library API [B.2].
The sample code is provided for the following scenario: Assume you have a PCI
card that generates level-sensitive interrupts. When an interrupt occurs you
expect the value of your card's interrupt command-status register
(INTCSR), which is mapped to an I/O port address
(dwAddr), to be intrMask.
In order to clear and acknowledge the interrupt you need to write 0 to the
INTCSR.
The code below demonstrates how to define an array of transfer commands that instructs WinDriver's kernel-mode interrupt handler to do the following:
INTCSR register and save its value.
INTCSR value against the given mask
(intrMask) to verify the source of the interrupt.
INTCSR to
acknowledge the interrupt.
Note: all commands in the example are performed in modes of DWORD.
WD_TRANSFER trans[3]; /* Array of 3 WinDriver transfer command structures */ BZERO(trans); /* 1st command: Read a DWORD from the INTCSR I/O port */ trans[0].cmdTrans = RP_DWORD; /* Set address of IO port to read from: */ trans[0].dwPort = dwAddr; /* Assume dwAddr holds the address of the INTCSR */ /* 2nd command: Mask the interrupt to verify its source */ trans[1].cmdTrans = CMD_MASK; trans[1].Data.Dword = intrMask; /* Assume intrMask holds your interrupt mask */ /* 3rd command: Write DWORD to the INTCSR I/O port. This command will only be executed if the value read from INTCSR in the 1st command matches the interrupt mask set in the 2nd command. */ trans[2].cmdTrans = WP_DWORD; /* Set the address of IO port to write to: */ trans[2].dwPort = dwAddr; /* Assume dwAddr holds the address of INTCSR */ /* Set the data to write to the INTCSR IO port: */ trans[2].Data.Dword = 0;
After defining the transfer commands, you can proceed to enable the
interrupts.
Note that memory allocated for the transfer commands must remain available
until the interrupts are disabled
, as explained above.
The following code demonstrates how to use the WDC_IntEnable
function to enable the interrupts using the transfer commands prepared above:
/* Enable the interrupts:
hDev: WDC_DEVICE_HANDLE received from a previous call to WDC_PciDeviceOpen().
INTERRUPT_CMD_COPY: Used to save the read data - see WDC_IntEnable().
interrupt_handler: Your user-mode interrupt handler routine.
pData: The data to pass to the interrupt handler routine. */
WDC_IntEnable(hDev, &trans, 3, INTERRUPT_CMD_COPY, interrupt_handler,
pData, FALSE);
As indicated in section 9.2.1, WinDriver
supports PCI Message-Signaled Interrupts (MSI) and Extended Message-Signaled Interrupts (MSI-X) on Linux and Windows Vista and higher (earlier versions of Windows do not
support MSI/MSI-X).
The same APIs are used for handling both legacy and MSI/MSI-X interrupts, and
these APIs return information regarding interrupt types supported by your
hardware [9.2.3] and the interrupt type
that was enabled for it [9.2.4].
When using WinDriver on Windows Vista and higher, WinDriver's kernel-mode interrupt
handler sets the interrupt message data in the dwLastMessage
field of the WD_INTERRUPT structure that was passed to the
interrupt enable/wait function. If you pass the same interrupt structure as
part of the data to your user-mode interrupt handler routine, as demonstrated
in the sample and generated DriverWizard interrupt code, you will be able to
access this information from your interrupt handler. When using a Kernel PlugIn
driver [11], the last message data
is passed to your kerne-mode KP_IntAtIrqlMSI [B.6.10] and KP_IntAtDpcMSI [B.6.11] handlers.
You can use the low-level WDC_GET_ENABLED_INT_LAST_MSG macro to
retrieve the last message data for a given WDC device [B.4.11].
![]() | |
| The information in this section is relevant only when working on Windows. |
To successfully handle PCI interrupts with WinDriver on Windows, you must first install an INF file that registers your PCI card to work with WinDriver's kernel driver, as explained in section 15.1.
To use MSI/MSI-X on Windows, the card's INF file must contain specific
[Install.NT.HW] MSI information, as demonstrated below:
[Install.NT.HW] AddReg = Install.NT.HW.AddReg [Install.NT.HW.AddReg] HKR, "Interrupt Management", 0x00000010 HKR, "Interrupt Management\MessageSignaledInterruptProperties", 0x00000010 HKR, "Interrupt Management\MessageSignaledInterruptProperties", MSISupported, 0x10001, 1
Therefore, to use MSI/MSI-X on Windows Vista and higher with WinDriver – provided your hardware supports MSI/MSI-X – you need to install an appropriate INF file.
When using DriverWizard on Windows Vista and higher to generate an INF file for a PCI device that supports MSI/MSI-X, the INF generation dialogue allows you to select to generate an INF file that supports MSI/MSI-X (see step #3 of the DriverWizard Walkthrough in section 4.2 of the manual).
In addition, the WinDriver Xilinx Virtex 5 BMD sample, which demonstrates MSI handling, includes a sample MSI INF file for this chip – WinDriver/xilinx/virtex5/bmd/~ml555_bmd.inf.
![]() | |
| If your card's INF file does not include MSI/MSI-X information, as detailed above, WinDriver will attempt to handle your card's interrupts using the legacy level-sensitive interrupt handling method, even if your hardware supports MSI/MSI-X. |
The sample code below demonstrates how you can use the WDC
library's [B.2] interrupt APIs (described in
sections B.3.46 – B.3.48 of the manual) to implement a simple user-mode
interrupt handler.
For complete interrupt handler source code that uses the WDC interrupt
functions, refer, for example, to the WinDriver
pci_diag
(WinDriver/samples/pci_diag/),
pcmcia_diag
(WinDriver/samples/pcmcia_diag/), and PLX
(WinDriver/plx/) samples and to the generated DriverWizard
PCI/PCMCIA/ISA code. For a sample of MSI interrupt handling, using the same
APIs, refer to the Xilinx Virtex 5 BMD sample (WinDriver/xilinx/virtex5/bmd/), or to the
code generated by DriverWizard for PCI hardware that supports MSI/MSI-X on the
supported operating sytsems (Linux or Windows Vista and higher).
![]() | |
|
VOID DLLCALLCONV interrupt_handler (PVOID pData)
{
PWDC_DEVICE pDev = (PWDC_DEVICE)pData;
/* Implement your interrupt handler routine here */
printf("Got interrupt %d\n", pDev->Int.dwCounter);
}
...
int main()
{
DWORD dwStatus;
WDC_DEVICE_HANDLE hDev;
...
WDC_DriverOpen(WDC_DRV_OPEN_DEFAULT, NULL);
...
hDev = WDC_IsaDeviceOpen(...);
...
/* Enable interrupts. This sample passes the WDC device handle as the data
for the interrupt handler routine */
dwStatus = WDC_IntEnable(hDev, NULL, 0, 0,
interrupt_handler, (PVOID)hDev, FALSE);
/* WDC_IntEnable() allocates and initializes the required WD_INTERRUPT
structure, stores it in the WDC_DEVICE structure, then calls
InterruptEnable(), which calls WD_IntEnable() and creates an interrupt
handler thread */
if (WD_STATUS_SUCCESS != dwStatus)
{
printf ("Failed enabling interrupt. Error: 0x%x - %s\n",
dwStatus, Stat2Str(dwStatus));
}
else
{
printf("Press Enter to uninstall interrupt\n");
fgets(line, sizeof(line), stdin);
WDC_IntDisable(hDev);
/* WDC_IntDisable() calls InterruptDisable(), which calls WD_IntDisable() */
}
...
WDC_IsaDeviceClose(hDev);
...
WDC_DriverClose();
}Windows CE uses a logical interrupt scheme rather than the physical interrupt number. It maintains an internal kernel table that maps the physical IRQ number to the logical IRQ number. Device drivers are expected to use the logical interrupt number when requesting interrupts from Windows CE. In this context, there are three approaches to interrupt mapping:
For an example how to register your device with the PCI bus driver, refer to section 5.3.
logical interrupt = SYSINTR_FIRMWARE + physical interruptWhen the device is not registered with Windows CE Plug-and-Play, WinDriver will follow this mapping.
![]() | |
| This option can only be performed by the Platform Builder. |
Provide the device's mapped logical interrupt value. If unavailable,
statically map the physical IRQ to a logical interrupt. Then call
WD_CardRegister with the logical interrupt and with the
INTERRUPT_CE_INT_ID flag set. The static interrupt map is in
the file CFWPC.C (located in the
\%_TARGETPLATROOT\%\ KERNEL\ HAL directory).
You will then need to rebuild the Windows CE image NK.BIN and download the new executable onto your target platform.
Static mapping is helpful also in the case of using reserved interrupt mapping. Suppose your platform static mapping is:
An attempt to initialize and use any of these interrupts will fail.
However, you may want to use one or more of these interrupts on
occasion, such as when you do not want to use the PPSH, but you want to
reclaim the parallel port for some other purpose. To solve this
problem, simply modify the file CFWPC.C
(located in the \%_TARGETPLATROOT\%\ KERNEL\ HAL
directory) to include code, as shown below, that sets up a value for
interrupt 7 in the interrupt mapping table:
SETUP_INTERRUPT_MAP(SYSINTR_FIRMWARE+7,7);
Suppose you have a PCI card which was assigned IRQ9. Since Windows CE
does not map this interrupt by default, you will not be able to receive
interrupts from this card. In this case, you will need to insert a
similar entry for IRQ9:
SETUP_INTERRUPT_MAP(SYSINTR_FIRMWARE+9,9);
You can reduce the interrupt latency on Windows CE for PCI devices by making slight changes in the registry and in your code:
"WdIntEnh"=dword:0"WdIntEnh"=dword:1If you exclude this line, or leave the value 0, the interrupt latency will not be reduced.
WD_xxx API (described in the
WinDriver PCI Low-Level API Reference), call WD_InterruptDoneCe immediately
after calling WD_IntEnable.
![]() | |
When using WinDriver's WDC APIs [B.2]
to handle the interrupts, or when enabling interrupts using the lower-level
InterruptEnable function (described in the
WinDriver PCI Low-Level API Reference), you do not need to call
WD_InterruptDoneCe, since
WDC_IntEnable [B.3.46] /
InterruptEnable automatically call
WD_InterruptDoneCe.
|
WD_InterruptDoneCe receives two parameters:
void WD_InterruptDoneCe(HANDLE hWD, WD_INTERRUPT pInt);
hWD: Handle to WinDriver's kernel-mode driver as
received from WD_Open (see description of
WD_Open in the WinDriver PCI Low-Level API Reference)
pInt: Pointer to a WD_INTERRUPT structure
returned from WD_IntEnable