Device Interfaces

< BACK  NEXT >
[oR]

In chapter 7, a primitive mechanism for extending the Dispatch interface is described. It relies upon a single user-mode call, DeviceIoControl, which allows specification of a control code that is generally device-specific. The remaining arguments of the Win32 API call specify separate input and output buffers. Of course, the structure and meaning of the buffers is code-dependent and relies upon a specification outside the scope of the I/O Manager or any other system component.

Interface Definition

With the introduction of Plug and Play for Windows 2000, Microsoft allows device drivers to extend a generic interface to user-mode code. An interface is a specification, or contract, between caller and implementer that is nothing more than a group of (hopefully) related function calls (methods). A single driver can support many interfaces, and interfaces can be reimplemented by many drivers. Thus, the functionality of a driver (using interfaces) is merely the mix and match conglomeration of the interfaces it implements.

An interface should be immutable. Once formally published (or distributed), an interface should not be changed. Once multiple drivers implement the same interface, it realistically cannot be changed. This includes additions as well as deletions. The immutability requirement allows client code to be certain that if a driver supports an interface, its meaning is completely unambiguous.

Interfaces are identified by a unique number, or interface type (ID). To avoid inter-vendor collisions, the ID space is somewhat large, consisting of 128 bits. Microsoft has chosen to conform to the Open Software Foundation (OSF) Distributed Computing Environment (DCE) specification for the generation of these Universally Unique Identifiers (UUID). To generate a statistically unique UUID, Microsoft supplies two tools, GUIDGEN (Windows based) and UUIDGEN (console based).

Although a direct call interface is available only for kernel-mode code, user-mode code can also benefit. The interfaces supported by a driver can be enumerated by an application, and if recognized, offer a level of assurance as to driver functionality.

Interface Construction

Once a suitable ID is generated, the interface is constructed with a structure that includes a function pointer for each method of the specification. All device interfaces are based on the following structure:

 typedef VOID (*PINTERFACE_REFERENCE)(PVOID pContext); typedef VOID (*PINTERFACE_DEREFERENCE)(PVOID pContext); typedef struct _INTERFACE {     USHORT Size;     USHORT Version;     PVOID Context;     PINTERFACE_REFERENCE InterfaceReference;     PINTERFACE_DEREFERENCE InterfaceDereference;     // interface-specific entries go here } INTERFACE, *PINTERFACE; 

If using C++, it is acceptable to define the new interface using inheritance (structs support inheritance as well as classes in C++). For example, to define an interface used to launch a missile,

 typedef struct _COORDINATES {     LONG latitude;     LONG longitude; }  COORDINATES, *PCOORDINATES; typedef BOOLEAN     (*PLAUNCH_INTERFACE_LAUNCH)(COORDINATES coords); typedef BOOLEAN    (*PLAUNCH_INTERFACE_DESTROY)(VOID); // Derive the new struct from the generic struct typedef struct _LAUNCH_INTERFACE : INTERFACE {      // only need to define interface-specific entries      PLAUNCH_INTERFACE_LAUNCH Launch;      // the next function cancels a missile launch      // we wouldn't want this function to be optional      PLAUNCH_INTERFACE_DESTROY Destroy; } LAUNCH_INTERFACE, *PLAUNCH_INTERFACE; 

Interface Reference Counting

Notice that the custom functions of the interface are provided as function pointers within the struct. Also, the base interface includes two standard functions, InterfaceReference and InterfaceDereference. These functions provide a counting mechanism that determine the lifetime of the interface. Each "user" or client of an interface must increment the interface's reference count by invoking InterfaceReference. As each user finishes its use of the interface, the reference count is decremented with InterfaceDereference. Typically, InterfaceReference is used whenever one function passes an interface to another function. Each function is independently responsible for decrementing the count when the function ultimately completes. This usage is analogous to COM's cAddRef and Release functionality.

As to whether an interface would actually care to know if it is being "used" or not largely depends on the nature of the interface. An interface that allocates significant resources could use the interface count to determine when to deallocate its usage.

Registering and Enabling an Interface

Once constructed, an interface is registered by a device driver during its AddDevice routine using IoRegisterDeviceInterface. The prototype for this function is shown in Table 9.8.

Table 9.8. Function Prototype for IoRegisterDeviceInterface
NTSTATUS IoRegisterDeviceInterface IRQL = PASSIVE_LEVEL
Parameter Description
IN PDEVICE_OBJECT pdo Pointer to physical device object
IN CONST GUID *pInterfaceClassGuid Pointer to Interface ID
IN PUNICODE_STRING refString Additional modifying string to differentiate conflicting Interface Ids (optional)
OUT PUNICODE_STRING by symbolicLinkName Name for referring to interface driver and user-mode code
Return value STATUS_SUCCESS
STATUS_INVALID_DEVICE_REQUEST

The symbolic link name generated by the system after the very first call to IoRegisterDeviceInterface is persisted in the system registry. Future calls return the original name. Drivers should save the created symbolic link name in the device extension. The symbolic link name is also the name by which user-mode code can refer to the device.

Once registered, a driver must still enable the interface, typically during receipt of the PnP subcode IRP_MN_START_DEVICE. To enable or disable an interface, the function IoSetDeviceInterfaceState is used and is described in Table 9.9.

Once registered and enabled, the interface is available to kernel-mode code via a PnP subcode request, IRP_MN_QUERY_INTERFACE. The IRP of the request contains a field, Parameters.QueryInterface.Interface that points to a caller-allocated structure of the size dictated by the interface specification. The implementing driver is responsible for filling in the function pointers (or data, for that matter) that actually implement the interface. By convention, a driver should increment the reference count on the interface when initialized in this manner.

Table 9.9. Function Prototype for IoSetDeviceInterfaceState
NTSTATUS IoSetDeviceInterfaceState IRQL = PASSIVE_LEVEL
Parameter Description
IN PUNICODE_STRING symbolicLinkName Reference name returned by previous call to IoRegisterDeviceInterface
IN BOOLEAN bEnableInterface TRUE - Enable the interface
FALSE - Disable the interface
Return value STATUS_SUCCESS
STATUS_OBJECT_NAME_NOT_FOUND

< 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