Mapping Bus-Relative Addresses to Virtual Addresses
Some processors implement separate memory and I/O address spaces, while other processors do not. Because of these differences in hardware platforms, the mechanism drivers use to access I/O- or memory-resident device resources differs from platform to platform.
A driver requests device I/O and memory resources in response to the PnP manager's IRP_MN_QUERY_RESOURCE_REQUIREMENTS IRP. Depending on the hardware architecture, the HAL can assign I/O resources in I/O space or in memory space, and can assign memory resources in I/O space or in memory space.
If the HAL uses bus-relative memory space to access device resources (such as device registers), a driver must map I/O space into virtual memory so that it can access these resources. The driver can determine whether the resources are I/O- or memory-resident by inspecting the translated resources passed to the driver by the PnP manager at device startup. If the HAL uses I/O space, no mapping is required.
Specifically, when a driver receives an IRP_MN_START_DEVICE request, it should examine the structures at IrpSp->Parameters.StartDevice.AllocatedResources and IrpSp->Parameters.StartDevice.AllocatedResourcesTranslated, which describe the raw (bus-relative) and translated resources, respectively, that the PnP manager has assigned to the device. Drivers should save a copy of each resource list in the device extension as an aid to debugging.
The resource lists are paired CM_RESOURCE_LIST structures, in which each element of the raw list corresponds to the same element of the translated list. For example, if AllocatedResources.List[0] describes a raw I/O port range, AllocatedResourcesTranslated.List[0] describes the same range after translation. Each translated resource includes a physical address and the type of the resource.
If a driver is assigned a translated memory resource (CmResourceTypeMemory), it must call MmMapIoSpace to map the physical address into a virtual address through which it can access device registers. For a driver to operate in a platform-independent manner, it should check every returned, translated resource and map it, if necessary.
A kernel-mode driver should take the following steps, in response to an IRP_MN_START_DEVICE request, to ensure access to all device resources
Copy IrpSp->Parameters.StartDevice.AllocatedResources to the device extension.
Copy IrpSp->Parameters.StartDevice.AllocatedResourcesTranslated to the device extension.
In a loop, inspect each descriptor element in AllocatedResourcesTranslated. If the descriptor resource type is CmResourceTypeMemory, call MmMapIoSpace, passing the physical address and length of the translated resource.
When the driver receives an IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE request from the PnP manager, it must release the mappings by calling MmUnmapIoSpace in a similar loop. The driver should also call MmUnmapIoSpace if it must fail the IRP_MN_START_DEVICE request.
The raw resource type indicates which HAL access routine a driver should call (READ_REGISTER_XXX, WRITE_REGISTER_XXX, READ_PORT_XXX, WRITE_PORT_XXX). Most drivers do not have to check the raw resource list to determine which of these routines to use, because the driver itself requested the resource or the driver writer knows the required type given the nature of the device hardware.
For a resource in I/O space (CmResourceTypePort, CmResourceTypeInterrupt, CmResourceTypeDma), the driver should use the low-order 32 bits of the returned physical address to access the device resource, for example, through the HAL's read and write READ_REGISTER_XXX, WRITE_REGISTER_XXX, READ_PORT_XXX, WRITE_PORT_XXX routines.