575 lines
19 KiB
C
575 lines
19 KiB
C
//+-------------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
//
|
||
// Copyright (C) Microsoft Corporation, 1998 - 1999
|
||
//
|
||
// File: pnpfdo.c
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
//
|
||
// This file contains functions for handing AddDevice and PnP IRPs sent to the FDO
|
||
//
|
||
|
||
#include "pch.h"
|
||
|
||
NTSTATUS
|
||
ParPnpNotifyHwProfileChange(
|
||
IN PHWPROFILE_CHANGE_NOTIFICATION NotificationStructure,
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
//
|
||
// We just completed either a dock or an undock - trigger bus rescan to check for new devices
|
||
//
|
||
{
|
||
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
||
PAGED_CODE();
|
||
|
||
if( IsEqualGUID( (LPGUID)&(NotificationStructure->Event), (LPGUID)&GUID_HWPROFILE_CHANGE_COMPLETE) ) {
|
||
IoInvalidateDeviceRelations( fdoExt->PhysicalDeviceObject, BusRelations );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParPnpFdoStartDevice(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
||
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
||
KEVENT event;
|
||
|
||
ParDumpP( ("IRP_MN_START_DEVICE - FDO\n") );
|
||
|
||
//
|
||
// The stack below us must successfully START before we can START.
|
||
//
|
||
// Pass the IRP down the stack and catch it on the way back up in our
|
||
// completion routine. Our completion routine simply sets "event"
|
||
// to its signalled state and returns STATUS_MORE_PROCESSING_REQUIRED,
|
||
// which allows us to regain control of the IRP in this routine after
|
||
// the stack below us has finished processing the START.
|
||
//
|
||
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
IoSetCompletionRoutine(Irp, ParSynchCompletionRoutine, &event, TRUE, TRUE, TRUE );
|
||
status = ParCallDriver(fdoExt->ParentDeviceObject, Irp);
|
||
|
||
// wait for our completion routine to signal that it has caught the IRP
|
||
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
||
|
||
//
|
||
// We have control of the IRP again and the stack below us has finished processing.
|
||
//
|
||
|
||
if( status == STATUS_PENDING ) {
|
||
// IRP completed asynchronously below us - extract "real" status from the IRP
|
||
status = Irp->IoStatus.Status;
|
||
}
|
||
|
||
//
|
||
// did anyone below us FAIL the IRP?
|
||
//
|
||
if( !NT_SUCCESS(status) ) {
|
||
// someone below us FAILed the IRP, bail out
|
||
ParDump2(PARERRORS, ("START IRP FAILED below us in stack, status=%x\n", status) );
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
ParReleaseRemoveLock(&fdoExt->RemoveLock, Irp);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// The stack below us is STARTed.
|
||
//
|
||
|
||
//
|
||
// Register for ParPort PnP Interface changes.
|
||
//
|
||
// We will get an ARRIVAL callback for every ParPort device that is STARTed and
|
||
// a REMOVAL callback for every ParPort that is REMOVEd
|
||
//
|
||
|
||
#if 0 // disable parclass enumeration to work with new parport enumerator - DFritz - 2000-03-25
|
||
status = IoRegisterPlugPlayNotification (EventCategoryDeviceInterfaceChange,
|
||
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
||
(PVOID)&GUID_PARALLEL_DEVICE,
|
||
Fdo->DriverObject,
|
||
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)ParPnpNotifyInterfaceChange,
|
||
(PVOID)Fdo,
|
||
&fdoExt->NotificationHandle);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
// registration failed, we will never have any ParPort devices to talk to
|
||
ParDumpP( ("IoRegisterPlugPlayNotification InterfaceChange FAILED, status= %x\n", status) );
|
||
}
|
||
|
||
status = IoRegisterPlugPlayNotification( EventCategoryHardwareProfileChange,
|
||
0,
|
||
NULL,
|
||
Fdo->DriverObject,
|
||
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)ParPnpNotifyHwProfileChange,
|
||
(PVOID)Fdo,
|
||
&fdoExt->HwProfileChangeNotificationHandle );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
// registration failed, we will never have any ParPort devices to talk to
|
||
ParDumpP( ("IoRegisterPlugPlayNotification HwProfileChange FAILED, status= %x\n", status) );
|
||
}
|
||
#endif // 0
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
ParReleaseRemoveLock(&fdoExt->RemoveLock, Irp);
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParPnpFdoQueryCapabilities(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
||
// KEVENT event;
|
||
PIO_STACK_LOCATION irpStack;
|
||
|
||
ParDumpP( ("IRP_MN_QUERY_CAPABILITIES - FDO\n") );
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
// - does RawDeviceOK = TRUE make sense for the FDO?
|
||
|
||
|
||
//
|
||
// Start us even if no function driver or filter driver is found.
|
||
//
|
||
irpStack->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = TRUE;
|
||
|
||
//
|
||
// The instance ID's that we report are system wide unique.
|
||
//
|
||
// irpStack->Parameters.DeviceCapabilities.Capabilities->UniqueID = TRUE;
|
||
// - change to FALSE because we reuse names for LPTx.y during rescan
|
||
// when we detect that the daisy chain devices changed
|
||
//
|
||
irpStack->Parameters.DeviceCapabilities.Capabilities->UniqueID = FALSE;
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
status = ParCallDriver(fdoExt->ParentDeviceObject, Irp);
|
||
ParReleaseRemoveLock(&fdoExt->RemoveLock, Irp);
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ParFdoParallelPnp (
|
||
IN PDEVICE_OBJECT pDeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles all PNP IRPs sent to the ParClass FDO.
|
||
We got here because !(Extension->IsPdo)
|
||
|
||
Arguments:
|
||
|
||
pDeviceObject - The ParClass FDO
|
||
|
||
pIrp - PNP Irp
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if successful.
|
||
STATUS_UNSUCCESSFUL - otherwise.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_NOT_SUPPORTED;
|
||
PDEVICE_EXTENSION Extension;
|
||
PVOID pDriverObject;
|
||
PIO_STACK_LOCATION pIrpStack;
|
||
PIO_STACK_LOCATION pNextIrpStack;
|
||
KEVENT Event;
|
||
ULONG cRequired;
|
||
// GUID Guid;
|
||
WCHAR wszGuid[64];
|
||
UNICODE_STRING uniGuid;
|
||
// WCHAR wszDeviceDesc[64];
|
||
UNICODE_STRING uniDevice;
|
||
|
||
pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
|
||
|
||
Extension = pDeviceObject->DeviceExtension; // FDO Extension
|
||
|
||
{
|
||
NTSTATUS status = ParAcquireRemoveLock(&Extension->RemoveLock, pIrp);
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
//
|
||
// Someone gave us a pnp irp after a remove. Unthinkable!
|
||
//
|
||
// ASSERT(FALSE);
|
||
pIrp->IoStatus.Information = 0;
|
||
pIrp->IoStatus.Status = status;
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
// dvdr
|
||
// pIrp->IoStatus.Information = 0;
|
||
|
||
switch (pIrpStack->MinorFunction) {
|
||
|
||
case IRP_MN_START_DEVICE:
|
||
|
||
return ParPnpFdoStartDevice(pDeviceObject, pIrp);
|
||
|
||
case IRP_MN_QUERY_CAPABILITIES:
|
||
|
||
ParDumpP( ("IRP_MN_QUERY_CAPABILITIES - FDO\n") );
|
||
pIrpStack->Parameters.DeviceCapabilities.Capabilities->RawDeviceOK = TRUE; // no Function Driver required
|
||
pIrpStack->Parameters.DeviceCapabilities.Capabilities->UniqueID = TRUE; // ID's reported are system wide unique
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoSkipCurrentIrpStackLocation(pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS: {
|
||
|
||
ParDumpP( ("QUERY_DEVICE_RELATIONS - FDO\n") );
|
||
|
||
if(pIrpStack->Parameters.QueryDeviceRelations.Type != BusRelations) {
|
||
break; // bail out if we don't handle this query type
|
||
} else {
|
||
return ParPnpFdoQueryDeviceRelationsBusRelations(pDeviceObject, pIrp);
|
||
}
|
||
}
|
||
|
||
|
||
case IRP_MN_QUERY_STOP_DEVICE:
|
||
|
||
// always SUCCEED
|
||
ParDumpP( ( "QUERY_STOP_DEVICE - FDO\n") );
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoSkipCurrentIrpStackLocation (pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
|
||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
|
||
ParDumpP( ("CANCEL_STOP_DEVICE - FDO\n") );
|
||
|
||
// handle IRP synchronously:
|
||
// - set completion routine and event to wake on
|
||
// - pass IRP down the stack
|
||
// - our completion routine sets the event which wakes us
|
||
// - we wake on the event and regain control of
|
||
// the IRP on its way back up
|
||
|
||
// setup
|
||
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
||
IoSetCompletionRoutine(pIrp, ParSynchCompletionRoutine, &Event, TRUE, TRUE, TRUE);
|
||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||
|
||
// pass IRP down the stack
|
||
Status = ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
// wait for our completion routine to wake us up
|
||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||
|
||
// we have NOW regained control of the IRP on its way back up the stack
|
||
|
||
// extract "real" status from IRP if IoCallDriver returned PENDING
|
||
if (Status == STATUS_PENDING) {
|
||
Status = pIrp->IoStatus.Status;
|
||
}
|
||
|
||
// check if anyone below us in the stack failed the IRP
|
||
if ( !NT_SUCCESS(Status) && (Status != STATUS_NOT_SUPPORTED) ) {
|
||
ParDumpP( ("CANCEL_STOP_DEVICE failed at parent, Status= %x\n", Status) );
|
||
break;
|
||
}
|
||
|
||
// SUCCESS
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return STATUS_SUCCESS;
|
||
|
||
|
||
case IRP_MN_STOP_DEVICE:
|
||
|
||
// always SUCCEED
|
||
ParDumpP( ("STOP_DEVICE - FDO\n") );
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoSkipCurrentIrpStackLocation (pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
|
||
// SUCCEED if no PODOs (i.e., no parallel ports), FAIL otherwise
|
||
if( Extension->ParClassPdo ) {
|
||
ParDumpP( ("QUERY_REMOVE_DEVICE - FDO - FAIL - Legacy PODOs may be using Ports\n") );
|
||
pIrp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
ParCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
return STATUS_DEVICE_BUSY;
|
||
} else {
|
||
ParDumpP( ("QUERY_REMOVE_DEVICE - FDO - SUCCESS - no ParPorts exist\n") );
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoSkipCurrentIrpStackLocation (pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
}
|
||
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
|
||
ParDumpP( ( "CANCEL_REMOVE_DEVICE - FDO\n") );
|
||
|
||
// handle IRP synchronously:
|
||
// - set completion routine and event to wake on
|
||
// - pass IRP down the stack
|
||
// - our completion routine sets the event which wakes us
|
||
// - we wake on the event and regain control of
|
||
// the IRP on its way back up
|
||
|
||
// setup
|
||
IoCopyCurrentIrpStackLocationToNext(pIrp);
|
||
IoSetCompletionRoutine(pIrp, ParSynchCompletionRoutine, &Event, TRUE, TRUE, TRUE);
|
||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||
|
||
// pass IRP down the stack
|
||
Status = ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
// wait for our completion routine to wake us up
|
||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||
|
||
// we have NOW regained control of the IRP on its way back up the stack
|
||
|
||
// extract "real" status from IRP if IoCallDriver returned PENDING
|
||
if (Status == STATUS_PENDING) {
|
||
Status = pIrp->IoStatus.Status;
|
||
}
|
||
|
||
// check if anyone below us in the stack failed the IRP
|
||
if (!NT_SUCCESS(Status)) {
|
||
ParDumpP( ("CANCEL_REMOVE_DEVICE FAILED, Status = %x\n", Status) );
|
||
break;
|
||
}
|
||
|
||
// SUCCESS
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return STATUS_SUCCESS;
|
||
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
|
||
ParDumpP( ("REMOVE_DEVICE - FDO\n") );
|
||
|
||
Extension->DeviceStateFlags |= PAR_DEVICE_REMOVED;
|
||
|
||
if(Extension->NotificationHandle) {
|
||
IoUnregisterPlugPlayNotification (Extension->NotificationHandle);
|
||
Extension->NotificationHandle = 0;
|
||
}
|
||
|
||
if( Extension->HwProfileChangeNotificationHandle ) {
|
||
IoUnregisterPlugPlayNotification( Extension->HwProfileChangeNotificationHandle );
|
||
Extension->HwProfileChangeNotificationHandle = 0;
|
||
}
|
||
|
||
IoSkipCurrentIrpStackLocation(pIrp);
|
||
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
Status = ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
ParReleaseRemoveLockAndWait(&Extension->RemoveLock, pIrp);
|
||
|
||
IoDetachDevice(Extension->ParentDeviceObject);
|
||
|
||
if (Extension->ClassName.Buffer) {
|
||
ExFreePool(Extension->ClassName.Buffer);
|
||
}
|
||
|
||
//
|
||
// walk the list of remaining ParClass ejected device objects and kill them
|
||
//
|
||
{
|
||
PDEVICE_OBJECT current;
|
||
PDEVICE_EXTENSION FdoExtension = Extension; // fix alanmo discovered bug from machine where parport not started
|
||
|
||
ExAcquireFastMutex(&FdoExtension->DevObjListMutex);
|
||
current = Extension->ParClassPdo;
|
||
while(current) {
|
||
PDEVICE_OBJECT next = ( (PDEVICE_EXTENSION)(current->DeviceExtension) )->Next;
|
||
ParKillDeviceObject(current);
|
||
current = next;
|
||
}
|
||
ExReleaseFastMutex(&FdoExtension->DevObjListMutex);
|
||
}
|
||
|
||
Extension->DeviceStateFlags |= PAR_DEVICE_DELETED;
|
||
IoDeleteDevice(pDeviceObject);
|
||
|
||
return Status;
|
||
|
||
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
|
||
// ParClass FDO is root enumerated - we should never get this IRP
|
||
ParDumpP( ("IRP_MN_SURPRISE_REMOVAL - FDO - We are not supposed to get this IRP!!!\n") );
|
||
|
||
// fall through into default case since we don't handle this
|
||
|
||
default:
|
||
|
||
// We don't handle this request, simply pass it down the stack
|
||
ParDumpP( ("Unhandled PNP IRP: %x - FDO\n", pIrpStack->MinorFunction) );
|
||
IoSkipCurrentIrpStackLocation(pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Set the return code only if we have something to add.
|
||
//
|
||
if( Status != STATUS_NOT_SUPPORTED ) {
|
||
pIrp->IoStatus.Status = Status ;
|
||
}
|
||
|
||
//
|
||
// Complete immediately if we have failed the Irp for any reason other
|
||
// than STATUS_NOT_SUPPORTED. Otherwise, pass down.
|
||
//
|
||
if( NT_SUCCESS(Status) || (Status == STATUS_NOT_SUPPORTED) ) {
|
||
IoSkipCurrentIrpStackLocation(pIrp);
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return ParCallDriver(Extension->ParentDeviceObject, pIrp);
|
||
}
|
||
|
||
//
|
||
// Complete the IRP...
|
||
//
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
|
||
ParReleaseRemoveLock(&Extension->RemoveLock, pIrp);
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ParPnpAddDevice(
|
||
IN PDRIVER_OBJECT pDriverObject,
|
||
IN PDEVICE_OBJECT pPhysicalDeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the ParClass AddDevice routine.
|
||
|
||
This routine creates the ParClass FDO and attaches it to the device stack
|
||
|
||
Arguments:
|
||
|
||
pDriverObject - pointer to the driver object for this instance of parport.
|
||
|
||
pPhysicalDeviceObject - pointer to the device object that represents the port.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if successful.
|
||
!STATUS_SUCCESS - otherwise.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_OBJECT pDeviceObject;
|
||
PDEVICE_EXTENSION Extension;
|
||
NTSTATUS Status;
|
||
|
||
ParBreak(PAR_BREAK_ON_ADD_DEVICE, ("ParPnpAddDevice(PDRIVER_OBJECT, PDEVICE_OBJECT)\n") );
|
||
|
||
//
|
||
// Create the device object for this device.
|
||
//
|
||
|
||
Status = ParCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), NULL,
|
||
FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &pDeviceObject);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
ParLogError(pDriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 9, STATUS_SUCCESS, Status);
|
||
ParDump(PARERRORS, ("PARALLEL: Could not create a Device Object for FDO\n") );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Setup buffered I/O
|
||
//
|
||
pDeviceObject->Flags |= DO_BUFFERED_IO;
|
||
|
||
Extension = pDeviceObject->DeviceExtension;
|
||
|
||
RtlZeroMemory(Extension, sizeof(DEVICE_EXTENSION));
|
||
|
||
Extension->DeviceType = PAR_DEVTYPE_FDO;
|
||
|
||
ExInitializeFastMutex(&Extension->OpenCloseMutex);
|
||
ExInitializeFastMutex(&Extension->DevObjListMutex); // only FDO has this Mutex
|
||
IoInitializeRemoveLock(&Extension->RemoveLock, PARCLASS_POOL_TAG, 1, 10);
|
||
|
||
Extension->ExtensionSignature = PARCLASS_EXTENSION_SIGNATURE;
|
||
Extension->ExtensionSignatureEnd = PARCLASS_EXTENSION_SIGNATURE;
|
||
|
||
Extension->DeviceObject = pDeviceObject;
|
||
|
||
|
||
//
|
||
// Attach our new Device to our parent's stack.
|
||
//
|
||
|
||
Extension->ParentDeviceObject = IoAttachDeviceToDeviceStack( pDeviceObject, pPhysicalDeviceObject);
|
||
|
||
ParDumpV( ("ParPnpAddDevice(...): "
|
||
"pDeviceObject= %08x , Extension= %08x , ParentDeviceObject= %08x\n",
|
||
pDeviceObject, Extension, Extension->ParentDeviceObject) );
|
||
|
||
if (NULL == Extension->ParentDeviceObject) {
|
||
ParDump2(PARERRORS, ("ParPnpAddDevice(...): IoAttachDeviceToDeviceStack FAILED\n") );
|
||
IoDeleteDevice(pDeviceObject);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Done initializing
|
||
//
|
||
|
||
Extension->PhysicalDeviceObject = pPhysicalDeviceObject;
|
||
|
||
pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|