How to Use System Services Outside the Frameworks


Both UMDF and KMDF drivers can use the following system services in addition to the services that the frameworks provide:

  • UMDF drivers can use many of the functions in the Windows API. The primary exceptions are functions that create and manipulate user interfaces.

  • KMDF drivers can use kernel-mode system functions, including functions that manipulate the WDM objects that underlie the WDF objects.

How to Use the Windows API in UMDF Drivers

With certain restrictions, UMDF drivers can use the Windows API to perform tasks that the framework does not support. For example:

  • A driver that requires a timer or worker thread uses the Windows API to access these features.

  • A driver that requires an event to signal between threads or requires a lock other than the per-object lock that WdfObject::AcquireLock provides might use an event, semaphore, or another Windows locking primitive.

  • A driver that sends I/O to a device that is not supported by the UMDF I/O target objects might be required to call Winsock functions or remote procedure call (RPC) functions to send the I/O request and to handle completion.

  • A driver that uses properties of its device that UMDF does not expose might use the SetupDiXxx functions to get that information.

A UMDF driver calls functions in the Windows API in the same way that any other user-mode application does. Listing 14-1 shows how a UMDF driver can use the Windows SetupDiXxx functions. This example is a modified version of code from the Fx2_Driver sample.

Listing 14-1: How to call the Windows API from a UMDF driver

image from book
 ULONG instanceIdCch = 0; PWSTR instanceId = NULL; HDEVINFO deviceInfoSet = NULL; SP_DEVINFO_DATA deviceInfo = {sizeof(SP_DEVINFO_DATA)}; HRESULT hr; hr = m_FxDevice->RetrieveDeviceInstanceId(NULL, &instanceIdCch); if (FAILED(hr))  {     . . . //Error handling omitted } instanceId = new WCHAR[instanceIdCch]; if (instanceId == NULL)  {     . . . //Error handling omitted } hr = m_FxDevice->RetrieveDeviceInstanceId(instanceId, &instanceIdCch); if (FAILED(hr))  {     . . . //Error handling omitted } deviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL); if (deviceInfoSet == INVALID_HANDLE_VALUE)  {     . . . //Error handling omitted } if (SetupDiOpenDeviceInfo(deviceInfoSet,                           instanceId,                           NULL,                           0,                           &deviceInfo) == FALSE)  {     . . . //Error handling omitted } if (SetupDiGetDeviceRegistryProperty(deviceInfoSet,                                      &deviceInfo,                                      SPDRP_BUSTYPEGUID,                                      NULL,                                      (PBYTE) &m_BusTypeGuid,                                      sizeof(m_BusTypeGuid),                                      NULL) == FALSE)  {     . . . //Error handling omitted } SetupDiDestroyDeviceInfoList(deviceInfoSet); delete[] instanceId; 
image from book

The sample driver in the listing requires the globally unique identifier (GUID) for the type of bus to which the device is attached, which is not available from UMDF. However, if the driver has the device instance ID, it can call SetupDiXxx functions to get the GUID. In the listing, the driver calls IWDFDevice::RetrieveDeviceInstanceId to get a pointer to the instance ID for the device. The driver then calls a sequence of SetupDiXxx functions. SetupDiCreateDeviceInfoList creates a device information list that the driver passes to SetupDiOpenDeviceInfo, along with the device instance ID, to get information about the device. Finally, the driver calls SetupDiGetDeviceRegistryProperty to extract the bus type GUID from the device information. The driver then destroys the device information list and deletes the instance ID.

Whenever possible, drivers should use framework interfaces for creating, sending, and completing I/O requests, locking framework objects, and interacting with system components such as WinUSB. The framework interfaces provide additional error checking and parameter validation and implement additional features-such as request completion callbacks-that are particularly useful to drivers.

For example, instead of using the Windows CreateFile, ReadFile, and WriteFile functions to create and send an I/O request, a UMDF driver should call IWDFDevice::CreateRequest and IWdfIoRequest::Send and use an I/O target. By creating an I/O target, the driver gains access to the IWDFIoTargetStateManagement interface, through which it can query and control the state of the I/O target.

In addition, the I/O target mechanism enables the driver to register an I/O completion callback, which the framework invokes when the target completes the I/O request. The driver is not required to poll for completion or to handle completion on a separate thread. This level of control is important for drivers, which must conform to Windows guidelines for timely I/O completion. Even when a driver performs file I/O, it should always use the file handle I/O target even if it does not use any other I/O targets. Using the file handle I/O target means that the driver thread does not block while waiting for an I/O completion port.

image from book
For UMDF Drivers that Run on Windows XP

The I/O target mechanism provides a significant advantage over the Windows API for drivers that run on Windows XP: the ability to cancel a single I/O request. Windows XP supports only CancelIo, which cancels all outstanding I/O requests for the target file. A UMDF driver that uses I/O targets can cancel a single I/O request by using the IWDFIoRequest::CancelSentRequest method. Windows Vista and later releases support CancelIoEx, which provides this capability. A UMDF driver that runs on Windows Vista or later can simply call CancelIoEx to cancel a single I/O request.

image from book

If your UMDF driver calls the Windows API, you must follow these guidelines:

  • Do not create a user interface. Use any functions that rely on a user interface, or use functions that create, manage, or communicate with a window.

    A driver must not assume that a monitor or an end user is present. UMDF drivers run in a nonuser session.

  • Always use framework interfaces to send I/O within the device stack.

    Do not use I/O functions from the Windows API to send synchronous I/O requests to other drivers in the UMDF driver's device stack because doing so can cause deadlocks.

  • Use asynchronous I/O whenever possible to avoid blocking the dispatching thread.

  • Pay attention to the security implications of any functions outside the framework that the driver calls.

How to Use Kernel-Mode Driver Support Routines in KMDF Drivers

Occasionally, KMDF drivers require the use of kernel-mode driver support routines-also called WDM interfaces-that are not part of the framework. Among the most commonly used are:

  • ExInterlockedXxx and InterlockedXxx routines that perform synchronized, atomic operations such as incrementing a shared variable.

  • Runtime library routines that convert data and manage lists.

In general, these commonly used functions do not access or manipulate kernel-mode objects such as the DEVICE_OBJECT or IRP. KMDF drivers can use such kernel-mode functions without restriction if the driver routine that calls the function is running at the correct IRQL.

However, certain other kernel-mode driver support routines use WDM objects. KMDF drivers can call such routines and use WDM objects, but must conform to all the WDM rules for such access.

Tip 

The only firm restriction as of WDK Build 6000 on the use of WDM objects is not to use the final two elements of the Tail.Overlay.DriverContext field of the IRP structure because these two elements are designated for the framework to use.

KMDF provides numerous methods-all of which have Wdm in their names-that a driver can call to get WDM objects. For example, a driver can access the IRP that underlies a WDFREQUEST object, the WDM device object that underlies a WDFDEVICE object, and so forth. Table 14-1 lists the methods that provide access to WDM objects.

Table 14-1: Access to WDM Objects
Open table as spreadsheet

Method name

Description

WdfDeviceWdmGetAttachedDevice

Returns a pointer to the next-lower WDM DEVICE_OBJECT in the device stack.

WdfDeviceWdmGetDeviceObject

Returns a pointer to the WDM DEVICE_OBJECT that is associated with a specified framework device object.

WdfDeviceWdmGetPhysicalDevice

Returns a pointer to the WDM DEVICE_OBJECT that represents the PDO for the device.

WdfDpcWdmGetDpc

Returns a pointer to the KDPC that is associated with a specified framework DPC object.

WdfFdoInitWdmGetPhysicalDevice

Returns a pointer to the WDM DEVICE_OBJECT that represents the PDO for the device.

WdfFileObjectWdmGetFileObject

Returns a pointer to the WDM FILE_OBJECT that is associated with a specified framework file object.

WdfInterruptWdmGetInterrupt

Returns a pointer to the KINTERRUPT that is associated with a specified framework interrupt object.

WdfIoTargetWdmGetTargetDeviceObject

Returns a pointer to the WDM DEVICE_OBJECT that is associated with a local or remote I/O target.

WdfIoTargetWdmGetTargetFileHandle

Returns a handle to the file that is associated with a remote I/O target.

WdfIoTargetWdmGetTargetFileObject

Returns a pointer to the WDM FILE_OBJECT that is associated with a remote I/O target.

WdfIoTargetWdmGetTargetPhysicalDevice

Returns a pointer to the WDM DEVICE_OBJECT that represents PDO for a remote I/O target.

WdfRegistryWdmGetHandle

Returns a WDM handle to the registry key that a framework registry-key object represents.

WdfRequestCreateFromIrp

Creates a WDFREQUEST object to represent a WDM IRP.

WdfRequestRetrieveInputWdmMdl

Returns a pointer to an MDL that represents an I/O request's input buffer.

WdfRequestRetrieveOutputWdmMdl

Returns a pointer to an MDL that represents an I/O request's output buffer.

WdfRequestWdmGetIrp

Returns a pointer to the WDM IRP that is associated with a specified framework request object.

WdfUsbTargetPipeWdmGetPipeHandle

Returns a handle of type USBD_PIPE_HANDLE that is associated with a specified framework pipe object.

WdfWdmDeviceGetWdfDeviceHandle

Returns a handle to the framework device object that is associated with a specified WDM DRIVER_OBJECT.

WdfWdmDriverGetWdfDriverHandle

Returns a handle to the framework driver object that is associated with a specified WDM DRIVER_OBJECT.

A KMDF driver might require access to the underlying WDM objects for several reasons. Most commonly, a driver might require a pointer to the underlying WDM DEVICE_OBJECT so that it can open an interface to a driver in a different device stack or call a kernel-mode function to perform some other device-related task that is not available in KMDF, such as registering for certain types of device notification.

In addition, the WDM information can often be useful during the debug and test phase of driver development. Many of the current driver testing tools, such as Driver Verifier, report information by using the underlying kernel objects rather than the WDF objects. Recording the values of the WDM DRIVER_OBJECT and DEVICE_OBJECT in a trace log can save time during debugging.

Listing 14-2, from the Toastmon sample that is part of the KMDF Toaster sample suite, shows how a KMDF driver calls an I/O manager function and passes it a pointer to the WDM DEVICE_OBJECT.

Listing 14-2: How to call an I/O manager function from a KMDF driver

image from book
 status = IoRegisterPlugPlayNotification (             EventCategoryDeviceInterfaceChange,             PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,             (PVOID)&GUID_DEVINTERFACE_TOASTER,             WdfDriverWdmGetDriverObject(WdfDeviceGetDriver(device)),             (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)                      ToastMon_PnpNotifyInterfaceChange,             (PVOID)deviceExtension,             &deviceExtension->NotificationHandle); 
image from book

IoRegisterPlugPlayNotification is a Windows I/O manager function that a driver calls to request notification when certain Plug and Play actions occur in its device stack. The Toastmon sample uses this function to register for device interface change notification, so that the system notifies it whenever a Toaster device is plugged in or removed. In this function, the first three parameters describe the action for which the driver requests notification. The fourth parameter is a pointer to the WDM DRIVER_OBJECT of the caller. Getting this pointer is a two-step process, in which the KMDF driver:

  1. Calls WdfDeviceGetDriver with a handle to the WDFDEVICE object to get a handle to the WDFDRIVER object.

  2. Calls WdfDriverWdmGetDriverObject with the WDFDRIVER object handle that it just retrieved to get a pointer to the WDM DRIVER_OBJECT.

The fifth parameter is a pointer to the callback routine that the system invokes when the specified Plug and Play change occurs. The sixth parameter is a pointer to context information that is passed to the callback routine, and the final parameter is a registration identifier that the driver uses later to undo the registration.




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