What does DO_DEVICE_INITIALIZING really do?
When a device driver creates a device object by calling IoCreateDevice, the I/O manager sets DO_DEVICE_INITIALIZING in the Flags field of the DEVICE_OBJECT structure. The purpose of DO_DEVICE_INITIALIZING is to prevent other components from sending I/O to a device before the driver has finished initializing the device object.
The I/O Manager checks DO_DEVICE_INITIALIZING every time a handle to the device is opened and whenever a driver attempts to attach to a device stack (the top device object is checked for this flag). If DO_DEVICE_INITIALIZING is set, the I/O Manager won't allow another component to open the device by name using functions such as CreateFile, OpenFile, or IoGetDeviceObjectPointer. DO_DEVICE_INITIALIZING does not prevent a driver from receiving Plug and Play requests or requests from higher-level drivers in the stack, it just prevents a component from opening the device.
The I/O Manager clears DO_DEVICE_INITIALIZING for any device object that the driver creates in its DriverEntry routine. The driver is responsible for clearing DO_DEVICE_INITIALIZING for any device object that it creates in any routine other than DriverEntry.
Legacy drivers, such as non-Plug and Play drivers written for Windows NT 4, create their device objects in DriverEntry. Plug and Play drivers usually create their device objects in AddDevice, but a Plug and Play driver might create control device objects in DriverEntry—for example, to expose its own device interfaces or support a device in an exclusive stack that an application can open. (The driver does not report control device objects to Plug and Play or attach them to the device stack.)
The driver is responsible for preparing itself to receive I/O requests before returning from the routine in which it creates the device object. The steps involved vary somewhat for different kinds of drivers; for a Plug and Play driver, they usually include storing the pointer to the device's PDO in the device extension, defining flags to track the Plug and Play state of the device, setting power management flags, and creating or initializing software resources such as events and spin locks that the driver uses to manage its device. A driver might also register device interfaces or create one or more symbolic links to its device.
For device objects created in DriverEntry, the I/O Manager allows the device to be opened as soon as DriverEntry returns. For a Plug and Play driver, the driver stack must successfully complete a start request (IRP_MN_START_DEVICE) before the I/O Manager allows the device to be opened. Although a Plug and Play driver might need to handle some requests from other drivers in the stack between AddDevice and the start request (for example, storage port drivers must handle SRB_FUNCTION_CLAIM_DEVICE during this interval), the driver does not receive any create requests or file-object-based I/O requests until the device has been started. The driver does not need to synchronize against IRP_MJ_CREATE or incoming I/O requests based on file handles between the time the driver clears this flag and the time the Plug and Play Manager starts the device.
The following code shows a simplified example of an AddDevice routine based on the Toaster sample in the Windows DDK. (Error checking and code to initialize the device extension have been removed from this example. See the Toaster function driver sample at %winddk%\src\general\toaster\func\featured1\ for the complete code).
NTSTATUS
ToasterAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
PFDO_DATA fdoData;
POWER_STATE powerState;
PAGED_CODE();
// Create a function device object.
status = IoCreateDevice (DriverObject,
sizeof (FDO_DATA),
NULL, // No Name
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
// Code to initialize the device extension.
// Attach driver to the device stack.
fdoData->NextLowerDriver = IoAttachDeviceToDeviceStack
(deviceObject,
PhysicalDeviceObject);
// Clear the DO_DEVICE_INITIALIZING flag.
// Note: Do not clear this flag until the driver has set the
// device power state and the power DO flags.
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
}
What should you do?
- Clear the DO_DEVICE_INITIALIZING flag for any device objects your driver creates in routines other than DriverEntry (such as a Plug and Play driver's AddDevice routine).
- Clear DO_DEVICE_INITIALIZING after initializing the device object and just before returning from the routine.