The information in this article applies to:
- Microsoft Win32 Device Driver Kit (DDK), versions 3.5, 3.51
SUMMARY
This article describes how to create a PCI Device driver for Windows NT. A
device driver must perform many functions, such as creating a device
object, etc. In addition, there are many more functions that a driver must
perform during driver initialization to support a PCI device. Note that
some of the functions mentioned below are also used in non-PCI drivers.
MORE INFORMATION
Following are the steps necessary to accomplish this task:
- Find the PCI device with HalGetBusData:
Scan all the buses and slots to find the location of your PCI device. In
the buffer returned by HalGetBusData, look for a match to your device's
PCI VendorId and DeviceId.
If your device driver requires additional information from the PCI
configuration space, use HalGetBusData or HalGetBusDataByOffset. Fields
in the PCI configuration space can be set with HalSetBusData or
HalSetBusDataByOffset.
IMPORTANT: Do not set the base address registers (BARs) or the interrupt
information in the PCI configuration space. Also, do not expect the BARs
original settings to be those that will be used for your device. Since
PCI is dynamically configurable, the operating system reserves the right
to move the BARs as it sees fit.
Find the PCI device with 3 FOR loops: one for the bus, one for the
device number, and one for the function number. Note that the structure
PCI_SLOT_NUMBER contains the device number and function number
information.
- Claim the device resources with HalAssignSlotResources:
Once the PCI device is located, pass the bus and slot information to
HalAssignSlotResources. This API will ensure that there are no conflicts
with the resources specified in the configuration space. Note that the
HAL may move your memory or port ranges, so don't use the values
directly from the configuration space. Instead, use the values returned
in the resource list and translate them as indicated below.
- Parse the resource list for the device resources:
HalAssignSlotResources will claim the PCI device resources and return
the claimed resources in a CM_RESOURCE_LIST structure. The driver will
have to parse this resource list to get the interrupt, memory range, and
I/O range information. Note that a single PCI device can have multiple
memory and I/O ranges. If necessary, save this information in the
driver's device extension for later translation. Do not use the raw
resource information to access the I/O or memory spaces.
At this point, if the driver were to examine the PCI configuration
space, the BARs should be the same as those returned in the
CM_RESOURCE_LIST structure. However, use the information returned in the
resource list.
- For each memory range claimed, call HalTranslateBusAddress and
MmMapIoSpace:
HalTranslateBusAddress translates a bus-specific address into the
corresponding system-logical address. After the call to this API, check
the fourth parameter. On entry to the API, zero indicates memory space.
On exit from the API, if the value is still zero, as is the normal case
for memory translation, the driver must also call MmMapIoSpace.
Save the translated memory range information in a driver-accessible area
like the device extension. Use the translated range to access the memory
space.
- For each I/O range claimed, call HalTranslateBusAddress, and if needed,
MmMapIoSpace:
Mapping is similar to that for memory space. However, on entry to
HalTranslateBusAddress, the fourth parameter is one, which indicates I/O
space. On exit, the API may have changed this parameter to zero, in
which case the driver must also call MmMapIoSpace.
Save the translated I/O range information in a driver-accessible area
like the device extension. Use the translated range to access the I/O
space.
On some RISC-based systems, there is no I/O space: I/O space is mapped
to memory space. In this situation, the fourth parameter will change to
indicate that the I/O space has been mapped to memory space. On an x86-
based system, translating an I/O space will use HalTranslateBusAddress,
but not MmMapIoSpace. On some RISC-based systems, both
HalTranslateBusAddress and MmMapIoSpace might be called. It is important
to check the fourth parameter of HalTranslateBusAddress after the call
to this API.
This is a common problem when porting from x86-based machines to RISC
platforms. Driver writers sometimes assume that if they are translating
I/O ranges, they will never need to call MmMapIoSpace. This is an
incorrect assumption.
- If the device supports interrupts, call HalGetInterruptVector and
IoConnectInterrupt:
Once IoConnectInterrupt has been called, the driver's interrupt service
routine (ISR) might be called if the PCI device is interrupting. For
this reason, it is best to connect the interrupt after translating all
the device registers. If the previous translation has not occurred or
the information is not available to the ISR (if, for example, the
translated memory and I/O ranges are not saved in the device extension),
the ISR will not be able to check the PCI device registers and clear the
interrupt. If the interrupt is not cleared, the system will hang.
REFERENCES
Please see the Windows NT Device Driver Kit, Kernel-mode Drivers, Design
Guide for an overview of Windows NT device drivers. The APIs mentioned
above are explained in detail in the Windows NT Device Driver Kit, Kernel-
mode Drivers, Reference section.
|