Programming the Microsoft Windows Driver Model

Configuring Your Device

In the preceding 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 the 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:

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> return STATUS_SUCCESS; }

  1. I ll use the resource pointer to point to the current resource descriptor in the variable-length array. By the end of the upcoming loop, it will point past the last valid descriptor.

  2. The Count member of a resource list indicates how many resource descriptors are in the PartialDescriptors array.

  3. You should declare appropriate local variables for each of the I/O resources you expect to receive. I ll detail what these should be later on when I discuss how to deal with each of the standard I/O resources.

  4. Within the loop over resource descriptors, you use a switch statement to save resource description information into the appropriate local variables. In the text, I posited a device that needed just an I/O port range and an interrupt, and such a device would expect to find resource types CmResourceTypePort and CmResourceTypeInterrupt. I m showing the other two standard resource types CmResourceTypeMemory and CmResourceTypeDma for thoroughness.

  5. Once outside the loop, the local variables you initialized in the various case labels will hold the resource information you need.

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 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 (or steps) for each type of resource.

Table 7-1. Overview of Processing Steps for I/O Resources

Resource Type

Overview

Port

Possibly map port range; save base port address in device extension

Memory

Map memory range; save base address in device extension

Dma

Call IoGetDmaAdapter to create an adapter object

Interrupt

Call IoConnectInterrupt to create an interrupt object that points to your interrupt service routine (ISR)

Категории