The Role of Driver Layers in Plug and Play

< BACK  NEXT >
[oR]

The WDM model of drivers is built upon structured layers of Physical Device Objects (PDOs) and Functional Device Objects (FDOs). Generally, a PDO exists for each physical piece of hardware attached to a bus and assumes the responsibility for low-level device control common to multiple functions realized by the hardware. An FDO exists for each logical or abstract function presented to higher-level software.

As an example, consider a physical disk drive and driver. It can be represented by a PDO that implements bus adapter functions (e.g., adapting an IDE disk bus to a PCI bus). Once the PDO for the disk bus adapter is realized, an FDO assumes responsibility for the functional operations of the disk itself. The FDO may choose to handle a particular disk I/O request directly (e.g., reading a disk sector). It may choose to pass down other requests to its physical device partner (e.g., a power down request).

In reality, the role of a PDO can quickly become complicated and recursive. For example, a USB host adapter starts life as a physical device of the bus it adapts (e.g., PCI). The host adapter then takes on the role of a new bus driver, enumerating each USB device it discovers as its collection of PDOs. Each PDO then controls its FDO.

The technique is further complicated by allowing FDOs to be surrounded by filter device objects. These upper- and lower-level filter objects may exist in any number to modify or enhance the way that I/O Requests are handled by the resulting stack of device objects.

In order to distinguish between those FDOs which implement hardware buses and those that do not, the terms bus FDO and nonbus FDO are used within the DDK. A bus FDO implements the bus driver's responsibility of enumerating the devices attached to a bus. The bus FDO then creates a PDO for each attached device.

It is also worth noting that there is only a conceptual difference between a nonbus FDO and a filter device object. From the PnP Manager's perspective, all Device objects position themselves within the device stack, and the fact that some devices choose to consider themselves more than a filter is inconsequential.

The arrangement of a device stack is shown in Figure 9.2. The distinction between a bus FDO and a nonbus FDO is depicted in Figure 9.3.

Figure 9.2. The device stack.
graphics/09fig02.gif
Figure 9.3. Bus FDOs and nonbus FDOs.
graphics/09fig03.gif

Understanding the device stack is important in order to describe when the AddDevice function is called for a specific device. The overall algorithm used by Windows 2000 to load drivers and invoke the driver's AddDevice function is described by the following:

  1. During the installation of the operating system, Windows 2000 discovers and enumerates all buses in the system registry. The topology and interconnect of the buses is also discovered and registered.

  2. During the boot process, a bus driver for each known bus is loaded. Typically, Microsoft supplies all bus drivers but specialized drivers can also be supplied for proprietary buses.

  3. One of the prime responsibilities of a bus driver is to enumerate all devices attached to the bus. A PDO is created for each device found.

  4. For each device discovered, a class of device is located within the system registry that defines lower and upper filters, if any, as well as the driver for the FDO.

  5. If the filter or FDO driver is not yet loaded, the system performs the load and invokes DriverEntry.

  6. AddDevice is called for each FDO, which in turn invokes IoCreateDevice and IoAttachDeviceToDeviceStack, building the device stack.

The function IoAttachDeviceToDeviceStack is made from AddDevice to place the FDO at the (current) top of the device stack. Its prototype is shown in Table 9.2.

For convenience, it is advisable to maintain a relationship of the device stack elements within the Device extension structures of each PDO, FDO, and filter drivers. This is best done by reserving space for a pLowerDevice and pUpperDevice pointer within each Device extension. Unfortunately, while the pLowerDevice pointer initialization is straightforward, the upward pointer can only be initialized safely if the lower device type (filter or function) is known. This is because the return value from IoAttachDeviceToDeviceStack is simply a DEVICE_OBJECT. The extension, extracted from the returned Device object, needs to be explicitly cast so that the pUpperDevice offset is accurate. In a generalized device stack, no driver could be certain, a priori, of its lower device type. Fortunately, the upward pointer is generally unnecessary, so only the lower pointer is routinely maintained.

Table 9.2. Prototype for IoAttachDeviceToDeviceStack
PDEVICE_OBJECT IoAttachDeviceToDeviceStack IRQL == PASSIVE_LEVEL
Parameter Description
IN PDEVICE_OBJECT pThisDevice Pointer to new top of stack device object
IN PDEVICE_OBJECT pdo Pointer to PDO for this stack
Return value Pointer to previous "top of stack" device

The final task of the AddDevice function is to create a symbolic link name, if any, for the newly created and enabled device. This technique is exactly as described in chapter 6. A completed AddDevice function might be

 //++ // Function:   AddDevice // // Description: //  Called by the PNP Manager when a new device is //  detected on a bus. The responsibilities include //  creating an FDO, device name, and symbolic link. // // Arguments: //  pDriverObject - Passed from PNP Manager //  pdo      - pointer to Physcial Device Object //          passed from PNP Manager // // Return value: //  NTSTATUS signaling success or failure //-- NTSTATUS AddDevice ( IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pdo  ) { NTSTATUS status; PDEVICE_OBJECT pfdo; PDEVICE_EXTENSION pDevExt; // Form the internal Device Name CUString devName("\\Device\\MINPNP"); // for "minimal" dev UlDeviceNumber++; devName += CUString(ulDeviceNumber); // Now create the device status =     IoCreateDevice( pDriverObject,                         sizeof(DEVICE_EXTENSION),                         &(UNICODE_STRING)devName,                         FILE_DEVICE_UNKNOWN,                         0, TRUE,                         &pfdo ); if (!NT_SUCCESS(status))     return status; // Initialize the Device Extension pDevExt = (PDEVICE_EXTENSION)pfdo->DeviceExtension; pDevExt->pDevice = pfdo;   // back pointer pDevExt->DeviceNumber = ulDeviceNumber; pDevExt->ustrDeviceName = devName; // Pile this new fdo on top of the existing lower stack pDevExt->pLowerDevice =   // downward pointer     IoAttachDeviceToDeviceStack( pfdo, pdo); // This is where the upper pointer would be initialized. // Notice how the cast of the lower device's extension // must be known in order to find the offset pUpperDevice. // PLOWER_DEVEXT pLowerDevExt = (PLOWER_DEVEXT) //    pDevExt->pLowerDevice->DeviceExtension; // pLowerDevExt->pUpperDevice = pfdo; // Form the symbolic link name CUString symLinkName("\\??\\MPNP"); symLinkName += CUString(ulDeviceNumber+1);  // 1 based pDevExt->ustrSymLinkName = symLinkName; // Now create the link name status =     IoCreateSymbolicLink( &(UNICODE_STRING)symLinkName,                         &(UNICODE_STRING)devName ); if (!NT_SUCCESS(status)) {     // if it fails now, must delete Device object     IoDeleteDevice( pfdo );     return status; } // Made it return STATUS_SUCCESS; } 

The device stack (multilayered) approach to Plug and Play drivers is more flexible during the hardware discovery process and better models the actual implementation of hardware (i.e., devices "layer" on a bus).

< BACK  NEXT >


The Windows 2000 Device Driver Book(c) A Guide for Programmers
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
ISBN: 0130204315
EAN: 2147483647
Year: 2000
Pages: 156

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