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:
NOTE
Replace "KP_" in the function names below with the name of your Kernel PlugIn
driver. For example, the Kernel PlugIn interrupt handler functions for the
sample KP_PCI driver are
KP_PCI_IntAtIrql and
KP_PCI_IntAtDpc.
KP_IntAtIrql:
This function is executed in the kernel at high IRQ (Interrupt Request)
level immediately upon the detection of an interrupt. The 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 — KP_PCI
(WinDriver/pci_diag/kp_pci/kp_pci.c) in v7.0.0
and above / KPTEST
(WinDriver/kerplug/kermode/kptest.c) in v6.2.3
and below — as the basis for your Kernel PlugIn
project. If you are developing a driver for a Xilinx PCI Express card
with Bus Master DMA (BMD) design, you can use the sample
Kernel PlugIn driver for this design — KP_BMD
(xilinx/bmd_design/kp/kp_bmd.c) in v10.3.1
and above / KP_VRTX for Virtex 5 cards
(xilinx/virtex5/bmd/kp/kp_vrtx.c) in v10.3.0
and below.
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 — refer to 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.2.3 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 (XXX_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;
}
}
|