Hardware Resources


After Windows discovers a device on the system, the driver for that device must process the device's hardware resources. The driver determines which I/O ports, memory-mapped addresses, and interrupts are used to communicate with its device, stores that information in a driver-defined location as required for later use, and maps any memory-based resources into the kernel virtual address space.

Device registers can be mapped into memory or into the system's I/O space, depending on the type of device, the bus to which it is attached, and the underlying hardware platform. Most modern processors and common buses-including PCI, PCI Express, ISA, and EISA-support both memory mapping and I/O mapping.

I/O mapping is a holdover from early microprocessor designs when few devices had their own addressable memory. The I/O space was designed as a separate address space through which to address device registers, thus saving important address space for the operating system and applications. Today, however, devices are typically memory mapped. In a memory-mapped device, the device registers are mapped to addresses in the physical memory space. The driver then maps those addresses into the virtual address space before it uses them, as follows:

  • Device registers that are mapped into I/O space are read and written with the READ_PORT_Xxx and WRITE_PORT_Xxx macros.

    Thus, I/O mapping is sometimes called PORT mapping, and the mapped resources are called PORT resources, with or without the capital letters.

  • Device registers that are mapped into memory are read and written with the READ_REGISTER_Xxx and WRITE_REGISTER_Xxx macros.

    Memory mapping is sometimes called REGISTER mapping, and the corresponding resources are called REGISTER resources.

Although you might know how your device hardware is designed, that information does not necessarily tell you how its resources will be mapped because some chipsets change the mapping. Therefore, drivers must be prepared to support both types of mapping for each register. A common strategy for supporting both types of mappings is to define wrappers for the PORT and REGISTER macros.

Hardware Resource Identification and Teardown

A function driver identifies and tears down the hardware resources that its device requires in its EvtDevicePrepareHardware and EvtDeviceReleaseHardware event callbacks, respectively. These callbacks provide a way for a driver to prepare its device hardware when the device is removed and reinserted or stopped to rebalance resources. The framework calls EvtDevicePrepareHardware immediately before calling EvtDeviceD0Entry and calls EvtDeviceReleaseHardware immediately after EvtDeviceD0Exit returns.

Chapter 7, "Plug and Play and Power Management," includes a complete sequence of callbacks in startup and shutdown.

Resource Identification: Preparing the Hardware

The framework calls EvtDevicePrepareHardware whenever resources are assigned to the device, specifically at the following times:

  • At system startup.

  • When a user connects the device to the running system.

  • When Windows restarts the device after stopping it to rebalance system resources.

EvtDevicePrepareHardware should map device resources but should not load firmware or perform other device initialization tasks. The framework calls EvtDevicePrepareHardware only at initial system or device startup and after stopping the device to rebalance resources. When the device returns to the working power state after idling in a low-power state, the framework does not call EvtDevicePrepareHardware. If the transition to a low-power idle state clears the downloaded firmware, EvtDevicePrepareHardware is not invoked to restore it.

In a typical driver, this callback function stores copies of the resources in the device context area for future use and maps the physical addresses of any memory-based resources to kernel virtual addresses.

The device is not yet in the working state when the framework calls EvtDevicePrepareHardware, although the device is addressable. The driver should restrict its hardware access to only what is required to identify the version of the device accurately.

Teardown: Releasing the Hardware

EvtDeviceReleaseHardware undoes any work that was done by EvtDevicePrepareHardware. The framework calls EvtDeviceReleaseHardware at the following times:

  • When the system is being shut down, but not when only the device is being powered down.

  • When a user removes the device from the system.

  • When Windows stops the device to rebalance system resources.

In EvtDeviceReleaseHardware, the driver tears down any software state that it established in EvtDevicePrepareHardware. Typically, this function unmaps any memory-based resources. The driver must not access device hardware from the EvtDeviceReleaseHardware callback function because the device's hardware resources have already been returned to the system and the device has already been transitioned to the D3 state. If the device was surprise removed, the device is already gone.

A driver registers callbacks for the EvtDevicePrepareHardware and EvtDeviceReleaseHardware events in the EvtDriverDeviceAdd function. To register the callbacks, the driver initializes the appropriate fields in the WDF_PNPPOWER_EVENT_CALLBACKS structure and then calls WdfDeviceInitSetPnpPowerEventCallbacks to fill the information into the WDFDEVICE_INIT structure before creating the WDFDEVICE object.

Chapter 7, "Plug and Play and Power Management," provides more information about registering these callbacks.

Resource Lists

At device enumeration, the bus driver requests I/O or memory mapping for each device resource in response to queries from the PnP manager. The PnP manager then loads the FDO for the device and repeats the query so that the function driver can add or remove resources from the list. A KMDF function driver can register the EvtDeviceFilterXxx callbacks to participate in requesting resources.

The PnP manager assigns raw and translated resources to the device and creates lists of the assigned resources. The framework receives the resource lists from the PnP manager and passes them to the KMDF function driver in the EvtDevicePrepareHardware callback. Thus, the driver receives two resource lists:

  • Raw resource list This list identifies hardware resources from the point of view of the I/O bus to which the device is attached. The raw resource list indicates how the device is designed.

  • Translated resource list This list identifies the resources from the point of view of the processor bus. This list indicates where each resource is mapped on the current system and thus whether the driver should use PORT or REGISTER macros to read and write the resource.

Both the raw and translated resource lists describe the same resources in the same order, with a one-to-one correspondence between the lists. The difference is that the raw resource list contains device bus-relative addresses, whereas the translated resource list contains system physical addresses.

Driver Inspection of Hardware Resources

To determine the mapping for each individual hardware resource, a driver inspects the translated resource lists.

Each resource list is represented by a WDFCMRESLIST object and describes one or more resources. To determine how many resources are assigned to its device, the driver calls WdfCmResourceListGetCount on the translated resource list. The driver can then loop through the list and call WdfCmResourceListGetDescriptor to get details about each individual resource.

WdfCmResourceListGetDescriptor returns a pointer to a CM_PARTIAL_RESOURCE_DESCRIPTOR structure that describes an individual resource. The structure contains the following member fields:

Type

Contains a constant that indicates the type of resource. If you're familiar with WDM drivers, you'll notice that the resource types as listed in Table 16-1 are the same as those for WDM.

ShareDisposition

Indicates whether the resource can be shared.

Flags

Provides type-specific bit flags.

union u

Provides additional information specific to that resource type, as listed in Table 16-1.

Table 16-1: Resource Types and Corresponding Union Members in Resource Descriptor
Open table as spreadsheet

Type

u member for type

CmResourceTypePort

u.Port

CmResourceTypeInterrupt

u.Interrupt for an interrupt vector or u.MessageInterrupt for a message-signaled interrupt (MSI) message.

If the CM_RESOURCE_INTERRUPT_MESSAGE flag in set in Flags, use u.MessageInterrupt; otherwise, use u.Interrupt.

CmResourceTypeMemory

u.Memory

CmResourceTypeMemoryLarge

One of u.Memory40, u.Memory48, or u.Memory64.

The CM_RESOURCE_MEMORY_LARGE_XXX flags set in Flags indicate which structure is used.

CmResourceTypeDma

u.Dma

CmResourceTypeDevicePrivate

u.DevicePrivate

CmResourceTypeBusNumber

u.BusNumber

CmResourceTypeDeviceSpecific

u.DeviceSpecificData

CmResourceTypePcCardConfig

u.DevicePrivate

CmResourceTypeMfCardConfig

u.DevicePrivate

CmResourceTypeConfigData

Reserved for system use

CmResourceTypeNonArbitrated

Not used

Drivers can generally ignore resources of any types other than CmResourceTypeMemory, CmResourceTypePort, CmResourceTypeInterrupt, and CmResourceTypeMemoryLarge.

Resources of type CmResourceTypeMemory and CmResourceTypeMemoryLarge are memory mapped, and resources of type CmResourceTypePort are I/O mapped. Each resource has a starting address and a length. Drivers should parse and save the translated resource lists, and then use the translated resources to read and write device registers. Most drivers do not need the raw resources.

Platform Independence and Driver Resource Mapping

The chipset on an individual machine can map hardware resources into either I/O space or memory space, regardless of how the device itself is designed. To be platform independent, all drivers should support both types of mappings, as follows:

  • For an I/O-mapped resource (that is, CmResourceTypePort), the driver saves the base address and range at which the resource is mapped, and it saves a pointer to an internal function that uses the PORT macros to access the resource.

  • For a memory-mapped resource (that is, CmResourceTypeMemory or CmResourceTypeMemoryLarge), the driver checks that the allocated size is adequate. If so, the driver maps the returned physical address to a virtual address by calling MmMapIoSpace, and it saves pointers to the internal functions that use the REGISTER macros to access the resource.

MmMapIoSpace is a kernel-mode memory manager function that maps a range of physical addresses to a range of addresses in nonpaged virtual memory. The driver uses the returned virtual addresses to access the mapped resources.

 Tip  See "MmMapIoSpace" in the WDK for more information-online at http://go.microsoft.com/fwlink/?LinkId=81589. See also "Hardware Abstraction Layer Routines" in the WDK for information about the PORT and REGISTER macros-online at http://go.microsoft.com/fwlink/?LinkId=81591.

For interrupt resources, the WDF interrupt object itself picks up its resources with no driver intervention. The driver creates an interrupt object in its EvtDriverDeviceAdd callback and the framework manages the work of assigning interrupt resources and connecting the interrupt. For information about creating an interrupt object and servicing interrupts, see "Interrupts and Interrupt Handling" later in this chapter.

Example: How to Map Resources

The PCI device supported by the Pcidrv sample driver has three base address registers (BARs):

  • BAR 0 is memory mapped.

  • BAR 1 is I/O mapped.

  • BAR 3 is flash memory mapped.

The driver determines whether to use the I/O-mapped BAR or the memory-mapped BAR to access the control and status registers. The sample driver checks for registers in both memory and I/O space. On some platforms, the I/O registers can be mapped into memory space; every driver should be coded to handle this.

In the sample, the code to map resources is isolated in the NICMapHwResources function, which is called by the driver's EvtDevicePrepareHardware callback. NICMapHwResources has two parameters: a pointer to the device context area (that is, FdoData) and a handle to the list of translated resources (that is, ResourcesTranslated) that was passed to EvtDevicePrepareHardware. The driver must use the translated resources to map device registers into port and memory space.

The code in Listing 16-1 is excerpted from the Pcidrv\Sys\Hw\Nic_init.c file.

Listing 16-1: Mapping hardware resources

image from book
 NTSTATUS NICMapHWResources(     IN OUT PFDO_DATA FdoData,     WDFCMRESLIST ResourcesTranslated     ) {     PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;     ULONG       i;     NTSTATUS    status = STATUS_SUCCESS;     BOOLEAN     bResPort      = FALSE;     BOOLEAN     bResInterrupt = FALSE;     BOOLEAN     bResMemory    = FALSE;     ULONG       numberOfBARs  = 0;     PAGED_CODE();     for(i=0; i<WdfCmResourceListGetCount(ResourcesTranslated);i++){        descriptor =             WdfCmResourceListGetDescriptor(ResourcesTranslated, i);        if(!descriptor){            return STATUS_DEVICE_CONFIGURATION_ERROR;        }        switch (descriptor->Type) {        case CmResourceTypePort:            numberOfBARs++;            . . . //Code omitted.            // The port is in I/O space on this machine.            FdoData->IoBaseAddress =                   ULongToPtr(descriptor->u.Port.Start.LowPart);            FdoData->IoRange = descriptor->u.Port.Length;            FdoData->ReadPort = NICReadPortUShort;            FdoData->WritePort = NICWritePortUShort;            bResPort = TRUE;            FdoData->MappedPorts = FALSE;            break;        case CmResourceTypeMemory:            numberOfBARs++;            if(numberOfBARs == 1) {                // CSR memory space should be 0x1000 in size.                ASSERT(descriptor->u.Memory.Length == 0x1000);                FdoData->MemPhysAddress = descriptor->u.Memory.Start;                FdoData->CSRAddress = MmMapIoSpace(                           descriptor->u.Memory.Start,                           NIC_MAP_IOSPACE_LENGTH,                           MmNonCached);                bResMemory = TRUE;            }            else if(numberOfBARs == 2){                // Map the physical to virtual address and use the                // READ/WRITE_REGISTER_xxx macros.                FdoData->IoBaseAddress = MmMapIoSpace(                           descriptor->u.Memory.Start,                           descriptor->u.Memory.Length,                           MmNonCached);                FdoData->ReadPort = NICReadRegisterUShort;                FdoData->WritePort = NICWriteRegisterUShort;                 FdoData->MappedPorts = TRUE;                 bResPort = TRUE;             }             else if(numberOfBARs == 3){                // We don't access the flash memory.                . . . // Code omitted.           }           else {               status = STATUS_DEVICE_CONFIGURATION_ERROR;               return status;           }           break;         case CmResourceTypeInterrupt:             . . . // Code omitted.         default:             . . . // Code omitted.         }     }     // Make sure we got all the resources.     if (!(bResPort && bResInterrupt && bResMemory)) {         status = STATUS_DEVICE_CONFIGURATION_ERROR;     }     . . . //Code omitted     return status; } 
image from book

A bus driver uses the raw resources to set up the device, but a function driver typically uses only the translated resources. The function driver in the listing parses the list of translated resources in a loop that starts at zero and ends when it has reached the last resource in the list. The driver determines the number of resources in the list by calling the WdfCmResourceListGetCount function.

For each resource in the list, the driver calls WdfCmResourceListGetDescriptor to get a pointer to the resource descriptor structure.

For CmResourceTypePort resources, the driver saves the starting address and range in the device context area and then sets the addresses of the functions that it uses to access the port resources. The NICReadPortUShort and NICWritePortUShort functions are wrappers for the READ_PORT_USHORT and WRITE_PORT_USHORT macros in the HAL.

For CmResourceTypeMemory resources, the driver also saves the starting address and range in the device context area, but then uses MmMapIoSpace to map the resources and to get a virtual address through which it can access them. MmMapIoSpace returns a pointer to the base virtual address of the mapped range, and the driver stores this pointer and the length of the range in the device context area. The driver also saves pointers to the functions that it uses to read and write the resources. For memory-mapped resources, the driver sets the NICReadRegisterUShort and NICWriteRegisterUShort functions, which are wrappers for the HAL's READ_REGISTER_USHORT and WRITE_REGISTER_USHORT macros.

For CmResourceTypeInterrupt resources, the driver is not required to save the resource information, because the framework handles this transparently for the driver. The sample driver merely checks this resource for completeness.

Example: How to Unmap Resources

When the device is removed or when the system rebalances resources, the driver must release its mapped resources in an EvtDeviceReleaseHardware callback. The framework calls this function after calling the driver's EvtDeviceD0Exit function.

The Pcidrv sample unmaps hardware resources in the NICUnmapHwResources internal function, which is called by its EvtDeviceReleaseHardware callback. NICUnmapHwResources appears in the Pcidrv\sys\hw\nic_init.c source file. The code that unmaps resources is shown in Listing 16-2.

Listing 16-2: Unmapping hardware resources

image from book
 if (FdoData->CSRAddress) {     MmUnmapIoSpace(FdoData->CSRAddress, NIC_MAP_IOSPACE_LENGTH);     FdoData->CSRAddress = NULL; } if (FdoData->MappedPorts) {     MmUnmapIoSpace(FdoData->IoBaseAddress, FdoData->IoRange);     FdoData->IoBaseAddress = NULL; } 
image from book

If the sample driver has previously mapped resources, the CSRAddress field of the device context area contains a valid pointer and the MappedPorts field is TRUE. The driver calls MmUnmapIoSpace to unmap the range of addresses at CSRAddress and the range of addresses at IoBaseAddress. The driver then sets the corresponding fields of the device context area to NULL.




Developing Drivers with the Microsoft Windows Driver Foundation
Developing Drivers with the Windows Driver Foundation (Pro Developer)
ISBN: 0735623740
EAN: 2147483647
Year: 2007
Pages: 224

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net