In the previous chapter, I discussed the various IRP_MJ_PNP requests that the Plug and Play (PnP) Manager sends you. IRP_MN_START_DEVICE is the vehicle for giving you information about the I/O resources that have been assigned by the PnP Manager for your use. I showed you how to obtain parallel lists of raw and translated resource descriptions and how to call a StartDevice helper function that would have the following prototype:
NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated) { ... } |
The time has now come to explain what to do with these resource lists. In summary, you'll extract descriptions of your assigned resources from the translated list and use those descriptions to create additional kernel objects that give you access to your hardware.
The CM_PARTIAL_RESOURCE_LIST structures contain a count and an array of CM_PARTIAL_RESOURCE_DESCRIPTOR structures, as illustrated in Figure 7-1. Each resource descriptor in the array has a Type member that indicates what type of resource it describes and some additional members that supply the particulars about some allocated resource. You're not going to be surprised by what you find in this array, by the way: if your device uses an IRQ and a range of I/O ports, you'll get two resource descriptors in the array. One of the descriptors will be for your IRQ, and the other will be for your I/O port range. Unfortunately, you can't predict in advance the order in which these descriptors will happen to appear in the array. Consequently, your StartDevice helper function has to begin with a loop that "flattens" the array by extracting resource values into a collection of local variables. You can later use the local variables to deal with the assigned resources in whatever order you please (which, it goes without saying, can be different from the order in which the PnP Manager chose to present them to you).
Figure 7-1. Structure of a partial resource list.
In sketch, then, your StartDevice function looks like this:
1 2 3 4 5 6 |
NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = translated->PartialDescriptors; ULONG nres = translated->Count; <local variable declarations> for (ULONG i = 0; i < nres; ++i, ++resource) { switch (resource->Type) { case CmResourceTypePort: <save port info in local variables> break; case CmResourceTypeInterrupt: <save interrupt info in local variables> break; case CmResourceTypeMemory: <save memory info in local variables> break; case CmResourceTypeDma: <save DMA info in local variables> break; } } <use local variables to configure driver & hardware> IoSetDeviceInterfaceState(&pdx->ifname, TRUE); } |
If you have more than one resource of a particular type, you need to invent a way to tell the resource descriptors apart. To give a concrete (but entirely fictitious) example, suppose that your device uses one 4-KB range of memory for control purposes and a different 16-KB range of memory as a data capture buffer. You expect to receive two CmResourceTypeMemory resources from the PnP Manager. The control memory is the block that's 4 KB long, whereas the data memory is the block that's 16 KB long. If your device's resources have a distinguishing characteristic such as the size difference in the example, you'll be able to tell which resource is which.
When dealing with multiple resources of the same type, don't assume that the resource descriptors will be in the same order that your configuration space lists them in, and don't assume that the same bus driver will always construct resource descriptors in the same order on every platform or every release of the operating system. The first assumption is tantamount to assuming that the bus driver programmer adopted a particular algorithm, while the second is tantamount to assuming that all bus driver programmers think alike and will never change their minds.
I'll explain how to deal with each of the four standard I/O resource types at appropriate places in the remainder of this chapter. Table 7-1 presents an overview of the critical step(s) for each type of resource.
Table 7-1. Overview of processing steps for I/O resources.
Resource Type | Overview |
---|---|
Port | Possibly maps port range; saves base port address in device extension |
Memory | Maps memory range; saves base address in device extension |
Dma | Calls IoGetDmaAdapter to create an adapter object |
Interrupt | Calls IoConnectInterrupt to create an interrupt object that points to your interrupt service routine (ISR) |