PCI interrupts are normally handled at the kernel level. Therefore, the best
way to handle PCI interrupts with WinDriver is using the
Kernel PlugIn
feature, which enables you to implement code directly at the kernel level. This is specifically true when handling interrupts that are shared
between different hardware devices, which is very common for PCI interrupts, since these interrupts are by definition sharable (For information on how to set the sharable state of the interrupt with WinDriver, see Technical Document #21.)
The Kernel PlugIn consists of two interrupt handler functions:
-
KP_IntAtIrql:
This function is executed in the kernel at high IRQ (Interrupt Request) level
immediately upon the detection of an interrupt.
This function should contain the write/read commands for clearing the source
of the interrupt (acknowledging the interrupt) and any additional high-priority
interrupt handling code. If additional deferred kernel or user-mode interrupt
processing is required, KP_IntAtIrql
should return TRUE, in which case the deferred
KP_IntAtDpc() routine (see below) will be executed once the
high-level processing is completed; Otherwise, the function should return
FALSE and KP_IntAtDpc() (as well as any
existing user-mode interrupt handler routine) will not be executed. The
generated and sample WinDriver Kernel PlugIn projects, for
example, schedule deferred interrupt processing once every five interrupts by
implementing code that returns TRUE only for every fifth
KP_IntAtIrql() call.
-
KP_IntAtDpc: This function is
executed in the kernel at raised execution level, provided
KP_IntAtIrql() returned TRUE (see above),
and should contain any lower-priority kernel interrupt handling that you wish to perform.
The return value of this function determines how many times, if at all, the user-mode interrupt handler routine (which can be set in the call to InterruptEnable() from the user-mode) will be executed once the control returns to the user-mode.
To handle shared PCI interrupts with WinDriver, perform the following steps:
-
Generate a Kernel PlugIn project with WinDriver's DriverWizard utility.
(When generating C code with the wizard you will be given the option to generate
Kernel PlugIn code - simply check the relevant check-box and proceed to generate
the code.) Alternatively, you can also use the generic WinDriver Kernel PlugIn
sample - KPTEST - as the basis for your Kernel PlugIn project.
The advantage of using the wizard is that the generated code will utilize the
specific device configuration information detected for your device, as well as any hardware-specific information that you define with the wizard. When generating Kernel PlugIn code for handling PCI interrupts, define the registers that you wish to access upon the detection of an interrupt in the wizard's Registers tab, and then from the Interrupts tab assign the registers read/write commands that you wish to perform at high IRQ level (in KP_IntAtIrql()) to the interrupt. The exact commands for acknowledging the interrupt are hardware-specific and you should therefore consult your hardware specification to determine the correct commands to set.
For more information on how to define the registers and interrupt commands in the wizard, see Technical Document #105.
-
The correct way to handle PCI interrupts with the Kernel PlugIn, and
shared interrupts in particular, is to include a command in
KP_IntAtIrql() that reads information from the hardware (normally you would read from the interrupt status register - INTCSR) in order to determine if your hardware generated the interrupt. [You can define a relevant read command in the DriverWizard before generating your
Kernel PlugIn code (see step #1 above) or manually modify the generated/sample
code to add such a command.] If the interrupt was indeed generated by your hardware, the function can set the value of the fIsMyInterrupt parameter to TRUE in order to accept control of the interrupt, and then proceed to write/read the relevant hardware register(s) in order to acknowledge the interrupt, and either return TRUE to perform additional deferred processing or return FALSE if no such processing is required (see above); If, however, you determine that the interrupt was not generated by your hardware, fIsMyInterrupt should be set to FALSE, in order to ensure that the interrupt will be passed on to other drivers in the system, and
KP_IntAtIrql() should return FALSE. (Note that there is no real harm in setting fIsMyInterrupt to FALSE even if the interrupt belongs to you, as done by default in the generated and sample WinDriver code, since other drivers in the system, assuming they were implemented correctly, should not attempt to handle the interrupt if it was not generated by their hardware.)
The portion of the code that performs the check whether your hardware
generated the interrupt is based on hardware-specific logic that cannot be
defined in the wizard. You will therefore need to modify the generated/sample
KP_IntAtIrql() implementation and add a
relevant "if" clause to ensure that you do not accept control of an
interrupt that was not generated by your hardware and do not attempt to clear
the source of such an interrupt in the hardware.
Following is a sample KP_IntAtIrql()
implementation, based on the v6.23 generated WinDriver Kernel PlugIn code. The code reads the INTCSR memory register (defined elsewhere in the code) and only proceeds to accept control of the interrupt and acknowledge it if the value read from the INTCSR is 0xFF (which serves as an indication in this sample that our hardware generated the interrupt). The interrupt in this sample is acknowledged by writing back to the INTCSR the value that was read from it:
BOOL __cdecl KP_XXX_IntAtIrql(PVOID pIntContext, BOOL *pfIsMyInterrupt)
{
XXX_HANDLE hXXX = (XXX_HANDLE) pIntContext;
DWORD data = 0;
PVOID pData = NULL;
DWORD addrSpace;
WD_ITEMS *pItem;
addrSpace = XXX_INTCSR_SPACE;
pItem = &hXXX->cardReg.Card.Item[hXXX->addrDesc[addrSpace].index];
pData = (DWORD*)pItem->I.Mem.dwTransAddr;
(DWORD)pData += XXX_INTCSR_OFFSET;
data = dtoh32(*((DWORD*)pData));
if (data == 0xFF) // The interrupt was generated by our hardware
{
// Write 0x0 to INTCSR to acknowledge the interrupt
*((DWORD*)pData) = dtoh32(data);
// Accept control of the interrupt
*pfIsMyInterrupt = TRUE;
// Schedule deferred interrupt processing (KP_IntAtDpc())
return TRUE;
}
else
{
// (Do not acknowledge the interrupt)
// Do not accept control of the interrupt
*pfIsMyInterrupt = FALSE;
// Do not schedule deferred interrupt processing
return FALSE;
}
}
Back to Top
|