First, locate the slot to which your card is connected, using
WD_PciScanCards(). Then get the card's information by
calling WD_PciGetCardInfo(). This information includes the
IRQ chosen for the card by the Plug and Play system. Now call
WD_CardRegister() to install the interrupt.
Since PCI interrupts are level sensitive, you should verify that the
INTERRUPT_LEVEL_SENSITIVE flag is set in
cardReg.Card.Item[i].I.Int.dwOptions — where 'i' is the
index number of the Interrupt item in the Item array. You would
normally not need to set this flag explicitly in your code, since
WD_PciGetCardInfo() automatically sets this flag for PCI cards and
you can normaly pass the same WD_CARD structure, which was returned from
WD_PciGetCardInfo(), to WD_CardRegister() (in
cardReg.Card).
Level sensitive interrupts must be acknowledged (i.e., cleared) in the kernel
immediately when they are received. To enable this acknowledgment of the
interrupts, call InterruptEnable() (/
InterruptThreadEnable() — in versions 4.30–5.22), or
the lower level WD_IntEnable() function (which is called by
InterruptEnable() (/ InterruptThreadEnable())),
passing to it a WD_INTERRUPT structure that holds the interrupt
handle returned by the previous call to WD_CardRegister() and the
relevant information for clearing the interrupt in the kernel. You should set
up this information (i.e., the location of the interrupt status register that
needs to be written to/read, the acknowledgment command to be performed —
of type enum WD_TRANSFER_COMMAND, defined in windrvr.h
— and a buffer with the data to be written\read to\from the register) in
the array of WD_TRANSFER structures, which is pointed at by the
Cmd member of the WD_INTERRUPT structure. Please note
that the information for acknowledging and clearing the interrupt is
hardware specific.
NOTE: You can use the DriverWizard to define the relevant
register/s for the interrupt acknowledgment (from the "Registers" tab select
"New" and define the register) and then assign the register/s to the interrupt
(in the 'Interrupt" tab), before generating the code. (Beginning with version
5.2.1 you can assign more than one register to the interrupt.) The generated
code would then include transfer commands for acknowledging the interrupt,
which comply with the information you have defined in your DriverWizard
project.
After passing the interrupt acknowledgment information to WinDriver, you need
to physically enable the interrupts. You can do this from your code by
writing the relevant information to the interrupt register (this information is
also hardware specific).
InterruptEnable() (/ InterruptThreadEnable()
— in versions 4.30–5.22) enables the interrupts by calling
WD_IntEnable(), and then creates and runs an interrupt
handler thread. The interrupt thread calls WD_IntWait() to
wait on the interrupt and then activates your interrupt handler routine when
the interrupt occurs and WD_IntWait() returns. You can refer to
the WinDriver\src\windrvr_int_thread.c file to view the implementation
of InterruptEnable() (For version 4.3.0–5.22, refer to the
implementation of InterruptThreadEnable() in
WinDriver\include\windrvr_int_thread.h).
(If you select to use the lower-level WD_IntEnable() and
WD_IntWait() functions directly from your code, instead of using
the InterruptEnable() (/ InterruptThreadEnable)
convenience function, you will need to spawn the thread that calls
WD_IntWait() yourself.) When an interrupt occurs, the interrupt
acknowledgment commands that you have set up in the WD_INTERRUPT
structure, before enabling the interrupts, will be executed by WinDriver at the
kernel level, before WD_IntWait() returns and your handler
routine is activated.
At the end, remember to call InterruptDisable() (/
InterruptThreadDisable() — in versions 4.30–5.22),
or the lower-level WD_IntDisable() function (depending on
how you enabled the interrupts), in order to disable the interrupts
Please note that the int.Cmd array can include more than a
single WD_TRANSFER command. You can set int.Cmd to
point to an array of as many WD_TRANSFER structures as you wish.
You can set, for example, the first WD_TRANSFER structure in the
array with a read transfer command, in order to read from the relevant register
in the kernel before acknowledging the interrupt. The read result will be
stored in the Data union field of the WD_TRANSFER
structure, and you will be able to access it later from your user mode
interrupt handler routine (e.g.:
DWORD data = int.Cmd[0].Data.Dword).
It is important to note, that in order to save the data that has been
read in the kernel, before acknowledging the interrupt, you will need to set
the INTERRUPT_CMD_COPY flag in the dwOptions
field of the WD_INTERRUPT structure
(int.dwOptions |= INTERRUPT_CMD_COPY;).
The generated DriverWizard code for your card will already implement
the relevant WinDriver API calls for scanning the PCI bus, registering your
card's resources (including the interrupt) and enabling and handling the
interrupt. However, since the interrupt acknowledgment mechanism is hardware
specific, you will need to modify the code somewhat, according to the
guidelines above, in order to set up the correct acknowledgment information for
the interrupt (in accordance with your hardware's specification) and to
implement your desired interrupt handler routine.
You can also refer to the WinDriver special library functions for specific
PCI chip-sets (such as PLX, Altera, etc.) for examples of specific
WinDriver interrupt handling code for these chips. Please note that the
interrupt acknowledgment commands in the samples disable all further
interrupts. You should therefore re-enable the interrupts from your
interrupt handler routine. (You can do this by adding at the end of your
XXX_IntHandler() or XXX_IntHandlerRoutine() routine a
similar line to that used for enabling the interrupts from
XXX_IntEnable()).
For a detailed explanation of the WinDriver interrupt handling mechanism and
relevant code samples, please refer to the "Handling Interrupts" section of the
WinDriver User's Manual
(see specifically the sub-section regarding PCI Interrupts). Please also refer
to the "Function Reference" chapter of the manual for a better understanding of
the relevant functions and structures and their usage.
To increase the flexibility of your interrupt code and improve the interrupt
handler rate, consider using WinDriver's
Kernel PlugIn feature in order to
acknowledge and handle the interrupts directly in the kernel.
|