mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
574 lines
19 KiB
574 lines
19 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|