If IoCreateDevice returns successfully, the DeviceObject parameter is set to point to the new device object. Wdm1 calls this its Functional Device Object (FDO). The Plug and Play chapter explains FDOs and PDOs in detail. Wdm1AddDevice returns an error if IoCreateDevice fails.

The Wdm1 device extension is now available through the FDO DeviceExtension field. Wdm1AddDevice stores a pointer to the FDO in its device extension.

Wdm1AddDevice now attaches the Wdm1 device to the device stack using IoAttachDeviceToDeviceStack, which must be called at PASSIVE_LEVEL[11]. The FDO and the given PDO are passed to this function. It returns a pointer to yet another device object that is also stored in the Wdm1 device extension. All this shenanigans are explained in the Plug and Play chapters.

Finally, two bits in the FDO Flags field must be changed. AddDevice routines must clear the DO_DEVICE_INITIALIZING flag. You do not need to clear this flag if you call IoCreateDevice in your DriverEntry routine. The DO_BUFFERED_IO flag is set in the Flags field. Again, this is covered later.

Deleting Devices

The Wdm1 driver must delete its FDO if it receives a Remove Device request. This is an IRP_ MJ_PNP_IRP with an IRP_MN_REMOVE_DEVICE minor version code. The code in Wdm1Pnp first calls IoDetachDevice to detach the FDO from the device stack. Finally, IoDeleteDevice is called to delete the FDO and its device extension.

IoDetachDevice and IoDeleteDevice must be called at IRQL PASSIVE_LEVEL

Device Names

The Wdm1 AddDevice code calls IoCreateDevice to create a Wdm1 device object. There are two ways to provide a name that is available to Win32 programs. The old way is to provide an explicit symbolic link name. The newfangled approach is to use a device interface to identify the devices that support a defined API.

Symbolic Links

The IoCreateDevice call has a DeviceName parameter that you can use to give your device a name. This name identifies the device to the kernel, not to Win32.

You must create a symbolic link to make the kernel device name available to Win32. To do this the old way, call IoCreateSymbolicLink at IRQL PASSIVE_LEVEL, passing the desired symbolic link name and the kernel device name as parameters.

Explicit device names created using this technique usually have a device number at the end. By convention, kernel device name numbers increment from zero, while symbolic link names increment from one.

The best way to illustrate device naming is using the WinObj tool, which only runs in NT and Windows 2000. WinObj is supplied in the Platform SDK, not the W2000 DDK. The standard serial ports have kernel device names deviceSerial0, deviceSerial1, etc. In WinObj, these names appear in the device folder.

Windows provides symbolic links 'C0M1', 'COM2', etc., to make these serial ports available to Win32. Symbolic links appear in the ?? folder in WinObj. You can double-click on each symbolic link to find out to which kernel device it refers. The screenshot in Figure 5.2 shows that COM3, ??com3, is a symbolic link to kernel device Serial0.

The ?? folder used to be called DosDevices, which you might come across in some old documentation.

Figure 5.2 WinObj tool screenshot

The Wdm1 driver could have given its first device object a kernel name of device Wdm1device0 and called IoCreateSymbolicLink to create a symbolic link with the name ??Wdm1dev1. A Win32 program would then have been able to open \.Wdm1dev1 using CreateFile.

Note that C strings represent each character as '\', so you would have to pass ' \\.\Wdm1dev1' to CreateFile.

If this technique is used, you must keep track of how many devices are in use, so you can generate the correct names. If you use a global variable to keep the device count, you must use it carefully to ensure that simultaneous accesses do not corrupt its value. Achieve this using the InterlockedIncrement function that atomically increments a LONG value. This can be run at any IRQL and on paged memory. There are companion decrement, exchange, and compare functions.

Explicit kernel and symbolic link names are used most often in NT style drivers, which create devices in their DriverEntry routine. In this case, counting devices is easy, as you can be sure that no other part of your code will be trying to increment the device count.

You can use the QueryDosDevice Win32 function to get a list of all the symbolic link names currently available. For each symbolic link, you can call QueryDosDevice again to find the kernel name. QueryDosDevice is supposed to run in Windows 98, but I did not succeed in getting it to work. In NT and W2000, you can use the DefineDosDevice Win32 function to change symbolic links. You can experiment with both these commands using the dosdev utility in the W2000 DDK.

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.

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату