[Previous] [Next]

Configuring Your Device

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).

Click to view at full size.

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);
  }

  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 would 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.
  6. If you registered a device interface during AddDevice, this is the time to enable that interface so that applications can find you and open handles to your device.

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 TypeOverview
PortPossibly maps port range; saves base port address in device extension
MemoryMaps memory range; saves base address in device extension
DmaCalls IoGetDmaAdapter to create an adapter object
InterruptCalls IoConnectInterrupt to create an interrupt object that points to your interrupt service routine (ISR)