Книга: Writing Windows WDM Device Drivers
Device Interfaces
Device Interfaces
The Wdm1 driver uses a device interface to make its devices visible to Win32 programs. The idea is that each Wdm1 device makes a defined Application Programmer Interface (API) available. A Globally Unique Identifier (GUID) is used to identify this interface.
The device interface that each Wdm1 device exposes is the set of commands that a Win32 programmer can use to access the device. A Wdm1 device responds to Win32 CreateFile, ReadFile, WriteFile, DeviceIoControl, and CloseHandle calls in a defined way. For example, a Wdm1 device is nonexclusive, so more than one thread can open it at the same time.
The Wdm1 driver's job is to implement a memory buffer that is shared by all the Wdm1 devices. Any writes extend the buffer, if necessary. Reads do not extend the buffer. Four IOCTLs are supported: Zero buffer, remove buffer, get buffer size, and get buffer.
One benefit of device interfaces is that another driver could be written that also implements the Wdm1 API. As long as it does this faithfully, there is no reason why a Win32 program should notice the difference between the new driver and Wdm1.
While it seems unlikely that the Wdm1 device interface will be copied by others, this technique is very useful for other types of drivers. For example, audio miniport drivers should implement the IMiniport COM interface. This is identified using the IID_IMiniport GUID and means that the driver implements two functions, GetDescription and DataRangeIntersection.
Wdm1 Device Interface
Implementing a device interface for Wdm1 devices is straightforward. First, a new 16-byte GUID must be generated using the guidgen tool. Then, include code to register the Wdm1 device interface for each Wdm1 FDO device object.
Generating GUIDs
To get a suitable new GUID, run the guidgen tool in the Platform SDK or the VC++ executables directory. Figure 5.3 shows the window that appears. guidgen can generate GUIDs in formats suitable for four different uses. For driver header file definitions, choose the DEFINE_ GUID(…) option. Pressing the Copy button copies the necessary code to the clipboard. Paste it into your header and amend the text <<name>> to define your own identifier.
Microsoft says that all GUIDs that guidgen generates are unique.
If you need to allocate a series of identifiers, press the New GUID button as often as necessary and copy each one to the relevant header file. Each new GUID increments the last digit of the first eight characters of the GUID. For example, the next GUID after the one shown in the screenshot starts {F4886541… }. In this book, I often use the form {F4886540… } as an abbreviation for the full GUID {F4886540-749E-11d2-B677-00C0DFE4C1F3}.
Figure 5.3 guidgen tool
The definition of WDM1_GUID in GUIDs.h is shown in Listing 5.3. There is a final twist in the tail for GUID definitions. One of the driver modules, Pnp.cpp in Wdm1, must have the following preprocessor directive before including GUIDs.h to formally declare WDM1_GUID.[12]
#define INITGUID
Listing 5.3 WDM1_GUID definition in GUIDs.h
// Wdm1 device interface GUID
// {C0CF0640-5F6E-11d2-B677-00C0DFE4C1F31
DEFINE_GUID(WDM1_GUID, 0xc0cf0640, 0x5f6e, 0x11d2, 0xb6, 0x77, 0x0, 0xc0, 0xdf, 0xe4, 0xc1, 0xf3);
Device Interface Registration
Wdm1 does not give a kernel device name to its device object and does not use IoCreateSymbolicLink to create a device name accessible to Win32 programs. Instead, the Wdm1 AddDevice routine calls IoRegisterDeviceInterface to register its interface as shown in Listing 5.4. It does this after creating its device but before attaching it to the device stack. IoRegisterDeviceInterface creates a Win32 symbolic link name connection to the Wdm1 device object. This is stored in the device extension ifSymLinkNamefield.
The Wdm1AddDevice code checks the return value from IoRegisterDeviceInterface. If this call fails, it tidies up properly by deleting the FDO and returning the status error code.
The final step is to enable the device interface by calling IoSetDeviceInterfaceState, at IRQL PASSIVE_LEVEL In subsequent drivers, the device interface is only enabled when the PnP Start Device message has been received.
Listing 5.4 Wdm1 Pnp.cpp Device Interface code
// Register and enable our device interface
status = IoRegisterDeviceInterface(pdo, &WDM1_GUID, NULL, &dx->ifSymLinkName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(fdo);
return status;
}
IoSetDeviceInterfaceState(&dx->ifSymLinkName, TRUE);
Table 5.3 IoRegisterDeviceInterface function
NTSTATUS IoRegisterDeviceInterface |
|
---|---|
Parameter | Description |
IN PDEVICE_OBJECT PhysicalDeviceObject |
The device PDO |
IN CONST GUID *InterfaceClassGuid |
The GUID being registered |
IN PUNICODE_STRING ReferenceString |
Usually NULL. A reference string becomes part of the interface name and so can be used to distinguish between different interfaces to the same device. |
OUT PUNICODE_STRING SymbolicLinkName |
The output interface symbolic link name. Do not forget to free the Unicode string buffer using RtlFreeUnicodeString when finished with it. |
The code in Wdm1Pnp to handle device removal has to be altered, as well. First, the device interface has to be disabled. Then, it must free the memory buffer allocated for the interface symbolic link name. It does not have to Unregister the device interface.
IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);
RtlFreeUnicodeString(&dx->ifSytnLinkName);
Device Interface Notes
As mentioned in the last chapter, registering a device interface creates a registry entry in the HKLMSystemCurrentControlSetControlDeviceClasses key. A subkey named after the GUID and further subkeys for each device implements that GUID. Drivers can use IoOpenDeviceInterfaceRegistryKey to open a handle to this registry key. For example, you might want to add a FriendlyName value to this key using an INF file AddInterface section, as described in Chapter 11. Win32 applications can use SetupDiOpenDeviceInterfaceRegKey to open this same key and retrieve the friendly name.
Device interface registrations can be removed from user mode, if necessary.
A driver can register that a device supports more than one device interface if desired.
IoGetDeviceInterfaces can be used by a driver to find a list of symbolic links to devices that support a specific device interface.
Behind the scenes, Windows generates a kernel device name for your device. This might be something like devi ce04059. The symbolic link name generated by IoRegisterDeviceInterface looks like DosDevices00000000000001c#{c0cf0640…}. The WinObj entry for the symbolic link looks like ??Root#UNKN0WN#0000#{C0CF0640…}.
- Creating and Deleting Device Objects
- Networking Interfaces
- A Tour of Linux Network Devices
- Configuring IPX Interfaces
- Chapter 5 Interfaces
- Chapter 5 Device Interfaces
- Chapter 15. Graphical User Interfaces for Iptables
- Introduction to Serial Devices
- Devices
- The Disk as a Storage Device
- Chapter 8. Device Driver Basics
- 18.2.5. Block devices