Êíèãà: Writing Windows WDM Device Drivers

Claiming Resources

Claiming Resources

This section looks at how to allocate resources. The crucial kernel function is IoReportResourceUsage. This checks to see if any other devices are using the resources. If the resources are free, they are reserved so no other device can use them.

The PHDIo driver only finds out which resources are needed when the user calls CreateFile, passing the resource description in the filename string. The Create IRP arrives in the PHDIoCreate routine. All the previous Create IRP handlers have not done very much. However, PHDIoCreate has these jobs to do.

1. Get the resource details from the filename.

2. Check for resource conflicts and reserve the resources.

3. Translate and map the resources.

Getting the resource details is handled by the GetResourcesFromFilename routine. I will not go into the details of this code here, apart from saying that it uses three support routines that work with the UNICODE_STRING structure: usStrCmpN, usGetHex, and usGetDec. In the end, the GotPortOrMemory device extension field is true if an I/O port specifier has been found, GotInterrupt is true if an interrupt has been found and ResourceOverride is true if the override specifier was used.

PHDIoCreate checks that an I/O port was specified. It then calls ClaimResources to check for resource conflicts and reserve the resources. Finally, TranslateAndMapResources is used to translate resource information and map memory. PHDIoCreate carefully ensures that all the resource bool fields are reset to false at the fail label if any error occurs.

Listing 18.2 PHDIoCreate routine

NTSTATUS PHDIoCreate(IN PDEVICE_OBJECT phddo, IN PIRP Irp) {
 PPHDIO_DEVICE_EXTENSION dx = (PPHDIO_DEVICE_EXTENSION)phddo->DeviceExtension;
 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
 DebugPrint("Create File is %T", &(IrpStack->FileObject->FileName));
 dx->GotPortOrMemory = false;
 dx->GotInterrupt = false;
 dx->PortNeedsMapping = false;
 dx->ConnectedToInterrupt = false;
 dx->ResourceOverride = FALSE;
 // Get resources from filename string
 PUNICODE_STRING usfilename = &(IrpStack->FileObject->FileName);
 NTSTATUS status = *(usfilename,dx);
 if (!NT_SUCCESS(status)) goto fail;
 // We must have IO port resource
 if (!dx->GotPortOrMemory) {
  DebugPrintMsg("No IO Port resource in filename");
  status = STATUS_INVALID_PARAMETER:
  goto fail;
 }
 // Claim resources
 status = ClaimResources(phddo);
 if (!NT_SUCCESS(status)) {
  DebugPrintMsg("Could not ClaimResources");
  goto fail;
 }
 // Translate and map resources
 status = TranslateAndMapResources(phddo);
 if (!NT_SUCCESS(status)) {
  UnclaimResources(phddo);
  goto fail;
 }
 // Complete
 return CompleteIrp(Irp,status);
 // On error, make sure everything's off fail:
 dx->GotPortOrMemory = false;
 dx->GotInterrupt = false;
 dx->PortNeedsMapping = false;
 dx->ConnectedToInterrupt = false;
 return CompleteIrp(Irp,status);
}

Claiming resources means working with Full and Partial Resource Descriptors. Chapter 9 showed that a device's resource assignments are given in these structures when the Plug and Play Start Device is received. The WdmIo driver obtains its resource assignments in this way.

NT style drivers have to build a resource list of these descriptors to pass to the IoReportResourceUsage routine. Note carefully that the raw resource details must be passed to IoReportResourceUsage, not the translated values. The kernel resource list structures are sufficiently intricate that it is worth showing them in Listing 1 8.3.

A resource list consists of one Full Resource Descriptor for each bus instance. Note carefully that this is an expandable structure. Although it is declared with only one Full Resource Descriptor, it may in fact contain one or more such descriptors.

A Full Resource Descriptor specifies the bus type and instance number. It also contains a Partial Resource List structure.

A Partial Resource List primarily contains an array of Partial Resource Descriptors. Again, note that a Partial Resource List is a structure that expands as more Partial Resource Descriptors are used.

A Partial Resource Descriptor finally contains the details of an individual resource. Table 9.2 in Chapter 9 gives full details of Partial Resource Descriptors.

Listing 18.3 Kernel resource list structures

typedef struct _CM_RESOURCE_LIST {
 ULONG Count;
 CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;
typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
 INTERFACE_TYPE InterfaceType; // unused for WDM
 ULONG BusNumber; // unused for WDM
 CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;
typedef struct _CM_PARTIAL_RESOURCE_LIST {
 USHORT Version;
 USHORT Revision;
 ULONG Count;
 CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
 UCHAR Type;
 UCHAR ShareDisposition;
 USHORT Flags;
 union {
  // …
 } u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;

The PHDIo driver deals with only the first instance of the ISA bus, so it needs only one Full Resource Descriptor. It always has one Partial Resource Descriptor for the I/O port. It can also have a second Partial Resource Descriptor for the interrupt, if one was specified.

ClaimResources, in Listing 18.4, builds a resource list structure and passes it to IoReportResourceUsage.

As you can guess, it is quite a job building the resource list correctly. The correct size for the whole structure must be determined first. A suitably sized block of paged memory is allocated and zeroed.

ClaimResources gradually fills the resource list. The resource list Count is set to one, as there is only one Full Resource Descriptor. The Full Resource Descriptor InterfaceType field is set to Isa and the BusNumber is set to 0. The Partial Resource List Count is set to 1 or 2, depending on how many resources are declared. The Partial Resource Descriptor for the I/O port is generated, then the one for the interrupt, if required. Whew!

There is a final complication to calling IoReportResourceUsage. You must either specify the resource list as belonging to the whole driver, or associate the resource list with an individual device. PHDIo says that the resources belong to the whole driver. If PHDIo were enhanced to provide more than one device, it would make sense to allocate resources on a per-device basis. One call to IoReportResourceUsage per device would be needed in this case.

Listing 18.4 ClaimResources routine

NTSTATUS ClaimResources(IN PDEVICE_OBJECT phddo) {
 PPHDIO_DEVICE_EXTENSION dx = (PPHDIO_DEVICE_EXTENSION)phddo->DeviceExtension;
 // Get resource count: either 1 (IOport) or 2 (IOport&IRQ)
 ULONG PartialResourceCount = 1;
 if (dx->GotInterrupt) PartialResourceCount++;
 // Get size of required CM_RESOURCE_LIST
 ULONG ListSize = FIELD_OFFSET(CM_RESOURCE_LIST, List[0]);
 ListSize += sizeof(CM_FULL_RESOURCE_DESCRIPTOR) + ((PartialResourceCount-1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
 // Allocate CM_RESOURCE_LIST
 PCM_RESOURCE_LIST ResourceList = (PCM_RESOURCE_LIST)ExAllocatePool(PagedPool, ListSize);
 if (ResourceList==NULL) {
  DebugPrintMsg("Cannot allocate memory for ResourceList");
  return STATUS_INSUFFICIENT_RESOURCES;
 }
 RtlZeroMemory(ResourceList, ListSize);
 // Only one Full Resource Descriptor needed, for ISA
 ResourceList->Count = 1;
 // Initialise Full Resource Descriptor
 PCM_FULL_RESOURCE_DESCRIPTOR FullRD = &ResourceList->List[0];
 FullRD->InterfaceType = Isa;
 FullRD->BusNumber = 0;
 FullRD->PartialResourceList.Count = PartialResourceCount;
 // Initialise Partial Resource Descriptor for IO port
 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = &FullRD->PartialResourceList.PartialDescriptors[0];
 resource->Type = CmResourceTypePort;
 resource->ShareDisposition = CmResourceShareDriverExciusive;
 resource->Flags = CM_RESOURCE_PORT_IO;
 resource->u.Port.Start = dx->PortStartAddress;
 resource->u.Port.Length = dx->PortLength;
 // Initialise Partial Resource Descriptor for Interrupt
 if (dx->GotInterrupt) {
  resource++;
  resource->Type = CmResourceTypeInterrupt;
  resource->ShareDisposition = CmResourceShareDriverExclusive;
  if (dx->Mode==Latched) resource->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
  else resource->F1ags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
  resource->u.Interrupt.Level = dx->Irql;
  resource->u.Interrupt.Vector = dx->Irql;
  resource->u.Interrupt.Affinity = 1;
 }
 // Ask for resources for the driver
 DebugPrint("Allocating %d resources", PartialResourceCount);
 DebugPrint("phddo->DriverObject %x", phddo->DriverObject);
 if (dx->ResourceOverride) DebugPrintMsg("Resource override conflict");
 BOOLEAN ConflictDetected;
 NTSTATUS status = IoReportResourceUsage(NULL,
  phddo->DriverObject, ResourceList, ListSize, // Driver resources
  NULL, NULL, 0, // Device resources
  dx->ResourceOverride, &ConflictDetected);
 // Cope (or override) if resource conflict found
 if (ConflictDetected) {
  DebugPrintMsg("ConflictDetected");
  if (dx->ResourceOverride) {
   DebugPrintMsg("Conflict detected and overridden");
   status = STATUS_SUCCESS;
  }
 }
 // Free allocated memory
 ExFreePool(ResourceList);
 return status;
}

Table 18.1 shows the parameters for IoReportResourceUsage. If you are allocating resources for the whole driver, use the DriverObject, DriverList, and DriverListSize parameters; otherwise, set these to NULL. Do the same for the per-device parameters, DeviceObject, DeviceList, and DeviceListSize.

In the NT and Windows 2000 platforms, the resource assignments end up in the registry in the HKLMHARDWARERESOURCEMAP key[45]. If you specify a DriverClassName parameter, this string is used as a subkey to hold the resource assignments. Otherwise, in NT 4 and NT 3.51, the resource assignments end up in the "OtherDrivers" subkey. In W2000, the resource assignments are in the "PnP Manager" subkey. You can use RegEdt32 to inspect the raw and translated resource assignments. In NT 3.51 and NT 4 this is the only way to view the resources used by the system. In Windows 2000 and Windows 98, the Device Manager properties for a device shows its resource usage; in W2000, the PHDIo device can be found if you opt to show hidden devices.

IoReportResourceUsage checks for resource conflicts and, if none, assigns the new resources. The OverrideConflict parameter can be used to force the storage of the new resource list, even if there were conflicts. The output ConflictDetected BOOLEAN says whether a conflict was detected. If its ResourceOverride field is true, PHDIo ignores a resource conflict and forces a STATUS_SUCCESS return.

Table 18.1 IoReportResourceUsage function

NTSTATUS IoReportResourceUsage (IRQL==PASSIVE_LEVEL)
Parameter Description
IN PUNICODE_STRING DriverClassName Optional resource class name
IN PDRIVER_OBJECT DriverObject Driver object pointer
IN PCM_RESOURCE_LIST DriverList Resource list for driver
IN ULONG DriverListSize Driver resource list size
IN PDEVICE_OBJECT DeviceObject Device object pointer
IN PCM_RESOURCE_LIST DeviceList Resource list for device
IN ULONG DeviceListSize Device resource list size
IN BOOLEAN OverrideConflict If TRUE, store resource list even if a conflict was detected
OUT PBOOLEAN ConflictDetected BOOLEAN that is set TRUE if a conflict was detected

A driver releases its claim on any resources by calling IoReportResourceUsage again; this time to report that it uses no resources. Listing 18.5 shows how UnclaimResources does this job. UnclaimResources is called when the file handle is closed.

Listing 18.5 UnclaimResources routine

void UnclaimResources(IN PDEVICE_OBJECT phddo) {
 DebugPrintMsg("Freeing all allocated resources");
 // Release all driver's resources by declaring we have none.
 CM_RESOURCE_LIST ResourceList;
 ResourceList.Count = 0;
 BOOLEAN ConflictDetected;
 IoReportResourceUsage(NULL,
  phddo->DriverObject, &ResourceList, sizeof(ResourceList), // Driver
  NULL, NULL, 0, // Device resources
  FALSE, &ConflictDetected); // ignore return result
}

Îãëàâëåíèå êíèãè


Ãåíåðàöèÿ: 2.536. Çàïðîñîâ Ê ÁÄ/Cache: 3 / 0
ïîäåëèòüñÿ
Ââåðõ Âíèç