5333 lines
157 KiB
C
5333 lines
157 KiB
C
/*++
|
||
|
||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
||
Module Name:
|
||
|
||
busenum.c
|
||
|
||
Abstract:
|
||
|
||
Demand load device enumerator services.
|
||
|
||
Author:
|
||
|
||
Bryan A. Woodruff (bryanw) 20-Feb-1997
|
||
|
||
--*/
|
||
|
||
#include <strsafe.h>
|
||
#include "ksp.h"
|
||
|
||
typedef struct _WORKER_CONTEXT
|
||
{
|
||
PIRP Irp;
|
||
KEVENT CompletionEvent;
|
||
NTSTATUS Status;
|
||
|
||
} WORKER_CONTEXT, *PWORKER_CONTEXT;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
NTSTATUS
|
||
IssueReparseForIrp(
|
||
IN PIRP Irp,
|
||
IN PDEVICE_REFERENCE DeviceReference
|
||
);
|
||
VOID
|
||
CompletePendingIo(
|
||
IN PDEVICE_REFERENCE DeviceReference,
|
||
IN PFAST_MUTEX DeviceListMutex,
|
||
IN NTSTATUS Status
|
||
);
|
||
LARGE_INTEGER
|
||
ComputeNextSweeperPeriod(
|
||
IN PFDO_EXTENSION FdoExtension
|
||
);
|
||
VOID
|
||
EnableDeviceInterfaces(
|
||
IN PDEVICE_REFERENCE DeviceReference,
|
||
IN BOOLEAN EnableState
|
||
);
|
||
VOID
|
||
InterfaceReference(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
);
|
||
VOID
|
||
InterfaceDereference(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
);
|
||
VOID
|
||
ReferenceDeviceObject(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
);
|
||
VOID
|
||
DereferenceDeviceObject(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
);
|
||
NTSTATUS
|
||
QueryReferenceString(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN OUT PWCHAR *String
|
||
);
|
||
NTSTATUS
|
||
OpenDeviceInterfacesKey(
|
||
OUT PHANDLE DeviceInterfacesKey,
|
||
IN PUNICODE_STRING BaseRegistryPath
|
||
);
|
||
NTSTATUS
|
||
EnumerateRegistrySubKeys(
|
||
IN HANDLE ParentKey,
|
||
IN PWCHAR Path OPTIONAL,
|
||
IN PFNREGENUM_CALLBACK EnumCallback,
|
||
IN PVOID EnumContext
|
||
);
|
||
NTSTATUS
|
||
EnumerateDeviceReferences(
|
||
IN HANDLE DeviceListKey,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN PVOID EnumContext
|
||
);
|
||
VOID
|
||
KspInstallBusEnumInterface(
|
||
IN PWORKER_CONTEXT WorkerContext
|
||
);
|
||
VOID
|
||
KspRemoveBusEnumInterface(
|
||
IN PWORKER_CONTEXT WorkerContext
|
||
);
|
||
|
||
#pragma alloc_text( PAGE, BuildBusId )
|
||
#pragma alloc_text( PAGE, BuildInstanceId )
|
||
#pragma alloc_text( PAGE, ClearDeviceReferenceMarks )
|
||
#pragma alloc_text( PAGE, CreateDeviceAssociation )
|
||
#pragma alloc_text( PAGE, CreateDeviceReference )
|
||
#pragma alloc_text( PAGE, CreatePdo )
|
||
#pragma alloc_text( PAGE, RemoveInterface )
|
||
#pragma alloc_text( PAGE, InstallInterface )
|
||
#pragma alloc_text( PAGE, KspRemoveBusEnumInterface )
|
||
#pragma alloc_text( PAGE, KsRemoveBusEnumInterface )
|
||
#pragma alloc_text( PAGE, ScanBus )
|
||
#pragma alloc_text( PAGE, QueryDeviceRelations )
|
||
#pragma alloc_text( PAGE, QueryId )
|
||
#pragma alloc_text( PAGE, RegisterDeviceAssociation )
|
||
#pragma alloc_text( PAGE, RemoveDevice )
|
||
#pragma alloc_text( PAGE, RemoveDeviceAssociations )
|
||
#pragma alloc_text( PAGE, RemoveUnreferencedDevices )
|
||
#pragma alloc_text( PAGE, SweeperWorker )
|
||
#pragma alloc_text( PAGE, EnableDeviceInterfaces )
|
||
#pragma alloc_text( PAGE, IssueReparseForIrp )
|
||
#pragma alloc_text( PAGE, CompletePendingIo )
|
||
#pragma alloc_text( PAGE, InterfaceReference )
|
||
#pragma alloc_text( PAGE, InterfaceDereference )
|
||
#pragma alloc_text( PAGE, ReferenceDeviceObject )
|
||
#pragma alloc_text( PAGE, ComputeNextSweeperPeriod )
|
||
#pragma alloc_text( PAGE, DereferenceDeviceObject )
|
||
#pragma alloc_text( PAGE, QueryReferenceString )
|
||
#pragma alloc_text( PAGE, StartDevice )
|
||
#pragma alloc_text( PAGE, OpenDeviceInterfacesKey )
|
||
#pragma alloc_text( PAGE, EnumerateRegistrySubKeys )
|
||
#pragma alloc_text( PAGE, EnumerateDeviceReferences )
|
||
#pragma alloc_text( PAGE, KsCreateBusEnumObject )
|
||
#pragma alloc_text( PAGE, KsGetBusEnumPnpDeviceObject )
|
||
#pragma alloc_text( PAGE, KspInstallBusEnumInterface )
|
||
#pragma alloc_text( PAGE, KsInstallBusEnumInterface )
|
||
#pragma alloc_text( PAGE, KsIsBusEnumChildDevice )
|
||
#pragma alloc_text( PAGE, KsServiceBusEnumCreateRequest )
|
||
#pragma alloc_text( PAGE, KsServiceBusEnumPnpRequest )
|
||
#pragma alloc_text( PAGE, KsGetBusEnumIdentifier )
|
||
#pragma alloc_text( PAGE, KsGetBusEnumParentFDOFromChildPDO )
|
||
#endif
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma const_seg("PAGECONST")
|
||
#endif // ALLOC_DATA_PRAGMA
|
||
|
||
static const WCHAR BusIdFormat[] = L"%s\\%s%c";
|
||
|
||
static const WCHAR BusReferenceStringFormat[] = L"%s&%s";
|
||
|
||
static const WCHAR DeviceCreateFileFormat[] = L"%s\\%s";
|
||
|
||
#if !defined( INVALID_HANDLE_VALUE )
|
||
#define INVALID_HANDLE_VALUE ((HANDLE)-1)
|
||
#endif
|
||
|
||
static const WCHAR DeviceReferencePrefix[] = L"\\Device\\KSENUM#%08x";
|
||
static const WCHAR ServicesPath[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services";
|
||
#if !defined( WIN9X_KS )
|
||
static const WCHAR ServicesRelativePathFormat[] = L"%s\\%s\\%s";
|
||
static const WCHAR ServicesPathFormat[] = L"%s\\%s";
|
||
#else
|
||
static const WCHAR ServicesRelativePathFormat[] = L"%s\\%s";
|
||
static const WCHAR ServicesPathFormat[] = L"%s";
|
||
#endif
|
||
|
||
static ULONG UniqueId = 0;
|
||
|
||
#define _100NS_IN_MS (10*1000)
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma const_seg()
|
||
#endif // ALLOC_DATA_PRAGMA
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsCreateBusEnumObject(
|
||
IN PWCHAR BusIdentifier,
|
||
IN PDEVICE_OBJECT BusDeviceObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN PDEVICE_OBJECT PnpDeviceObject OPTIONAL,
|
||
IN REFGUID InterfaceGuid OPTIONAL,
|
||
IN PWCHAR ServiceRelativePath OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
The demand-load bus enumerator object extends a Plug and Play device by
|
||
servicing bus enumerator queries via the KsServiceBusEnumPnpRequest()
|
||
function for the given functional device object. This function creates
|
||
a demand-load bus enumerator object and initializes it for use with the
|
||
demand-load bus enumerator services.
|
||
|
||
Arguments:
|
||
IN PWCHAR BusIdentifier -
|
||
a string prefix (wide-character) identifier for the bus such
|
||
as L"SW" or L"KSDSP". This prefix is used to create the unique
|
||
hardware identifier for the device such as
|
||
SW\{cfd669f1-9bc2-11d0-8299-0000f822fe8a}.
|
||
|
||
IN PDEVICE_OBJECT BusDeviceObject -
|
||
The functional device object for this bus. This is the device object
|
||
created and attached the physical device object for this device.
|
||
|
||
N.B. The first PVOID of the device extension of this device object
|
||
must be reserved for the resultant demand-load bus enumerator object.
|
||
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject -
|
||
The Plug and Play supplied physical device object for this device.
|
||
|
||
IN PDEVICE_OBJECT PnpDeviceObject OPTIONAL -
|
||
Optionally specifies the driver stack to forward Plug and Play IRPs.
|
||
If this parameter is not specified, the BusDeviceObject is attached to
|
||
the PhysicalDeviceObject and the resulting device object from that
|
||
operation is used to forward IRPs.
|
||
|
||
IN REFGUID InterfaceGuid OPTIONAL -
|
||
An interface GUID with which the demand-load bus enum object is
|
||
associated. This associates the bus with a device interface which
|
||
is enumerable through Io* or SetupApi* services for device interfaces.
|
||
This allows a driver to expose an interface with which clients
|
||
(user-mode or kernel-mode) can register new demand-load devices.
|
||
|
||
IN PWCHAR ServiceRelativePath OPTIONAL -
|
||
If specified, provides a path where a hierarchy of interfaces and
|
||
device identifiers is stored. For example "Devices" will store
|
||
the list of supported intefaces and devices in a path relative
|
||
to the services key for this bus such as:
|
||
|
||
REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Services\SWENUM\Devices.
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, otherwise an appropriate error code.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
PFDO_EXTENSION FdoExtension;
|
||
ULONG FdoExtensionSize;
|
||
USHORT Length;
|
||
|
||
PAGED_CODE();
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("KsCreateBusEnumObject()") );
|
||
|
||
//
|
||
// Note, FDO_EXTENSION includes the size of the UNICODE_NULL
|
||
// for BusPrefix.
|
||
//
|
||
FdoExtensionSize =
|
||
sizeof( FDO_EXTENSION ) +
|
||
(wcslen( BusIdentifier ) * sizeof( WCHAR ));
|
||
|
||
FdoExtension =
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
FdoExtensionSize,
|
||
POOLTAG_DEVICE_FDOEXTENSION );
|
||
|
||
if (NULL == FdoExtension) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// N.B.:
|
||
//
|
||
// SWENUM uses the first PVOID in the device extension -- any
|
||
// client using SWENUM services must reserve this area for
|
||
// SWENUM storage.
|
||
//
|
||
|
||
ASSERT( (*(PVOID *)BusDeviceObject->DeviceExtension) == NULL );
|
||
*(PFDO_EXTENSION *)BusDeviceObject->DeviceExtension = FdoExtension;
|
||
RtlZeroMemory( FdoExtension, sizeof( FDO_EXTENSION ) );
|
||
|
||
if (FAILED( StringCbCopy(
|
||
FdoExtension->BusPrefix,
|
||
FdoExtensionSize -
|
||
sizeof( FDO_EXTENSION ) + sizeof( WCHAR ),
|
||
BusIdentifier ) )) {
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Save the registry path location to the device listing
|
||
//
|
||
|
||
Length =
|
||
BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.MaximumLength +
|
||
sizeof( ServicesPath ) +
|
||
sizeof( UNICODE_NULL );
|
||
|
||
if (ServiceRelativePath) {
|
||
Length += wcslen( ServiceRelativePath ) * sizeof( WCHAR );
|
||
}
|
||
|
||
FdoExtension->BaseRegistryPath.Buffer =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
Length,
|
||
POOLTAG_DEVICE_DRIVER_REGISTRY );
|
||
|
||
if (NULL == FdoExtension->BaseRegistryPath.Buffer) {
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
FdoExtension->BaseRegistryPath.MaximumLength = Length;
|
||
|
||
//
|
||
// If the caller specified a service relative path, then append this
|
||
// to the HKLM\CurrentControlSet\Services\{service-name} path.
|
||
//
|
||
#if defined( WIN9X_KS)
|
||
if (ServiceRelativePath) {
|
||
if (FAILED(
|
||
StringCbPrintf(
|
||
FdoExtension->BaseRegistryPath.Buffer,
|
||
FdoExtension->BaseRegistryPath.MaximumLength,
|
||
ServicesRelativePathFormat,
|
||
BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.Buffer,
|
||
ServiceRelativePath ) )) {
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
} else {
|
||
if (FAILED(
|
||
StringCbPrintf(
|
||
FdoExtension->BaseRegistryPath.Buffer,
|
||
FdoExtension->BaseRegistryPath.MaximumLength,
|
||
ServicesPathFormat,
|
||
BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.Buffer ) )) {
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
#else
|
||
if (ServiceRelativePath) {
|
||
if (FAILED(
|
||
StringCbPrintf(
|
||
FdoExtension->BaseRegistryPath.Buffer,
|
||
FdoExtension->BaseRegistryPath.MaximumLength,
|
||
ServicesRelativePathFormat,
|
||
ServicesPath,
|
||
BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.Buffer,
|
||
ServiceRelativePath ) )) {
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
} else {
|
||
if (FAILED(
|
||
StringCbPrintf(
|
||
FdoExtension->BaseRegistryPath.Buffer,
|
||
FdoExtension->BaseRegistryPath.MaximumLength,
|
||
ServicesPathFormat,
|
||
ServicesPath,
|
||
BusDeviceObject->DriverObject->DriverExtension->ServiceKeyName.Buffer ) )) {
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
ExFreePool( FdoExtension );
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
FdoExtension->BaseRegistryPath.Length =
|
||
wcslen( FdoExtension->BaseRegistryPath.Buffer ) * sizeof( WCHAR );
|
||
|
||
//
|
||
// Automatically register the interface to this DO if specified.
|
||
//
|
||
|
||
if (InterfaceGuid) {
|
||
Status =
|
||
IoRegisterDeviceInterface(
|
||
PhysicalDeviceObject,
|
||
InterfaceGuid,
|
||
NULL,
|
||
&FdoExtension->linkName);
|
||
|
||
//
|
||
// Set up the device interface association (e.g. symbolic link).
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("linkName = %S", FdoExtension->linkName.Buffer) );
|
||
|
||
Status =
|
||
IoSetDeviceInterfaceState(
|
||
&FdoExtension->linkName,
|
||
TRUE );
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("IoSetDeviceInterfaceState = %x", Status) );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
ExFreePool( FdoExtension->linkName.Buffer );
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
ExFreePool( FdoExtension );
|
||
return Status;
|
||
}
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Initialize critical members of the device extension.
|
||
//
|
||
|
||
ExInitializeFastMutex( &FdoExtension->DeviceListMutex );
|
||
KeInitializeTimer( &FdoExtension->SweeperTimer );
|
||
KeInitializeDpc(
|
||
&FdoExtension->SweeperDpc,
|
||
SweeperDeferredRoutine,
|
||
FdoExtension );
|
||
ExInitializeWorkItem(
|
||
&FdoExtension->SweeperWorkItem,
|
||
SweeperWorker,
|
||
FdoExtension );
|
||
|
||
FdoExtension->ExtensionType = ExtensionTypeFdo;
|
||
FdoExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
||
FdoExtension->FunctionalDeviceObject = BusDeviceObject;
|
||
InitializeListHead( &FdoExtension->DeviceReferenceList );
|
||
|
||
//
|
||
// If the caller has not specified a PnpDeviceObject, then go
|
||
// ahead and attach the bus device object to the PDO. Otherwise,
|
||
// it is assumed that the caller has done or will do so.
|
||
//
|
||
|
||
if (PnpDeviceObject) {
|
||
FdoExtension->PnpDeviceObject = PnpDeviceObject;
|
||
} else {
|
||
FdoExtension->PnpDeviceObject =
|
||
IoAttachDeviceToDeviceStack(
|
||
BusDeviceObject,
|
||
PhysicalDeviceObject );
|
||
if (FdoExtension->PnpDeviceObject) {
|
||
FdoExtension->AttachedDevice = TRUE;
|
||
} else {
|
||
Status = STATUS_DEVICE_REMOVED;
|
||
}
|
||
}
|
||
if (NT_SUCCESS( Status )) {
|
||
//
|
||
// Obtain counter frequency and compute the maximum timeout.
|
||
//
|
||
|
||
KeQueryPerformanceCounter( &FdoExtension->CounterFrequency );
|
||
|
||
FdoExtension->MaximumTimeout.QuadPart =
|
||
MAXIMUM_TIMEOUT_IN_SECS * FdoExtension->CounterFrequency.QuadPart;
|
||
|
||
//
|
||
// The power code is pagable.
|
||
//
|
||
|
||
BusDeviceObject->Flags |= DO_POWER_PAGABLE;
|
||
|
||
Status = ScanBus( BusDeviceObject );
|
||
}
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Failure, perform cleanup.
|
||
//
|
||
|
||
if (FdoExtension->linkName.Buffer) {
|
||
|
||
//
|
||
// Remove device interface association...
|
||
//
|
||
|
||
IoSetDeviceInterfaceState( &FdoExtension->linkName, FALSE );
|
||
|
||
//
|
||
// and free the symbolic link.
|
||
//
|
||
|
||
ExFreePool( FdoExtension->linkName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Delete the copy of the registry path.
|
||
//
|
||
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
|
||
//
|
||
// Detach the device if attached.
|
||
//
|
||
|
||
if (FdoExtension->AttachedDevice) {
|
||
IoDetachDevice( FdoExtension->PnpDeviceObject );
|
||
}
|
||
|
||
ExFreePool( FdoExtension );
|
||
|
||
//
|
||
// Clear the reference in the DeviceExtension to the FDO_EXTENSION
|
||
//
|
||
*(PVOID *)BusDeviceObject->DeviceExtension = NULL;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
SweeperDeferredRoutine(
|
||
IN PKDPC Dpc,
|
||
IN PVOID DeferredContext,
|
||
IN PVOID SystemArgument1,
|
||
IN PVOID SystemArgument2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Deferred routine for the sweeper. Simply queues a work item.
|
||
|
||
Arguments:
|
||
IN PKDPC Dpc -
|
||
pointer to DPC
|
||
|
||
IN PVOID DeferredContext -
|
||
pointer to context
|
||
|
||
IN PVOID SystemArgument1 -
|
||
not used
|
||
|
||
IN PVOID SystemArgument2 -
|
||
not used
|
||
|
||
Return:
|
||
No return value.
|
||
|
||
--*/
|
||
|
||
{
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("SweeperDeferredRoutine") );
|
||
ExQueueWorkItem(
|
||
&((PFDO_EXTENSION) DeferredContext)->SweeperWorkItem,
|
||
CriticalWorkQueue );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
IssueReparseForIrp(
|
||
IN PIRP Irp,
|
||
IN PDEVICE_REFERENCE DeviceReference
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Completes the given IRP to reparse to the actual device path as
|
||
describe in the device reference and device association.
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Arguments:
|
||
IN PIRP Irp -
|
||
I/O request packet
|
||
|
||
IN PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference structure
|
||
|
||
Return:
|
||
Status return value as no return value
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||
PIO_STACK_LOCATION irpSp;
|
||
USHORT ReparseDataLength;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Mark this device reference as active.
|
||
//
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceActive;
|
||
|
||
//
|
||
// Reset the timeout for this device reference.
|
||
//
|
||
|
||
DeviceReference->TimeoutRemaining = DeviceReference->TimeoutPeriod;
|
||
|
||
//
|
||
// Reparse to the real PDO.
|
||
//
|
||
|
||
ReparseDataLength =
|
||
(wcslen( DeviceReference->DeviceName ) +
|
||
wcslen( DeviceReference->DeviceReferenceString ) + 2) *
|
||
sizeof( WCHAR );
|
||
|
||
if (irpSp->FileObject->FileName.MaximumLength < ReparseDataLength) {
|
||
ExFreePool( irpSp->FileObject->FileName.Buffer );
|
||
|
||
irpSp->FileObject->FileName.Buffer =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
ReparseDataLength,
|
||
POOLTAG_DEVICE_REPARSE_STRING );
|
||
|
||
if (NULL == irpSp->FileObject->FileName.Buffer) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to reallocate filename buffer for reparse") );
|
||
Status =
|
||
Irp->IoStatus.Status =
|
||
STATUS_INSUFFICIENT_RESOURCES;
|
||
irpSp->FileObject->FileName.Length =
|
||
irpSp->FileObject->FileName.MaximumLength = 0;
|
||
|
||
} else {
|
||
irpSp->FileObject->FileName.MaximumLength = ReparseDataLength;
|
||
}
|
||
}
|
||
|
||
if (irpSp->FileObject->FileName.Buffer) {
|
||
|
||
if (FAILED( StringCbPrintf(
|
||
irpSp->FileObject->FileName.Buffer,
|
||
ReparseDataLength,
|
||
DeviceCreateFileFormat,
|
||
DeviceReference->DeviceName,
|
||
DeviceReference->DeviceReferenceString ) )) {
|
||
|
||
ExFreePool( irpSp->FileObject->FileName.Buffer );
|
||
irpSp->FileObject->FileName.Buffer = NULL;
|
||
irpSp->FileObject->FileName.Length =
|
||
irpSp->FileObject->FileName.MaximumLength = 0;
|
||
Status =
|
||
Irp->IoStatus.Status =
|
||
STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
irpSp->FileObject->FileName.Length =
|
||
wcslen( irpSp->FileObject->FileName.Buffer ) *
|
||
sizeof( WCHAR );
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("reparse to: %S", irpSp->FileObject->FileName.Buffer) );
|
||
|
||
Irp->IoStatus.Information = IO_REPARSE;
|
||
|
||
Status =
|
||
Irp->IoStatus.Status =
|
||
STATUS_REPARSE;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
CompletePendingIo(
|
||
IN PDEVICE_REFERENCE DeviceReference,
|
||
IN PFAST_MUTEX DeviceListMutex OPTIONAL,
|
||
IN NTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Walks the I/O queue, completing pending I/O with the given status
|
||
code.
|
||
|
||
Arguments:
|
||
IN PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference
|
||
|
||
IN PFAST_MUTEX DeviceListMutex OPTIONAL -
|
||
pointer to optional mutex to lock while walking the I/O list
|
||
|
||
IN NTSTATUS Status -
|
||
status for Irp
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
PLIST_ENTRY ListEntry;
|
||
|
||
if (DeviceListMutex) {
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( DeviceListMutex );
|
||
}
|
||
|
||
while (!IsListEmpty( &DeviceReference->IoQueue )) {
|
||
|
||
ListEntry = RemoveHeadList( &DeviceReference->IoQueue );
|
||
Irp =
|
||
CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
|
||
//
|
||
// Note: If while processing of the IoQueue, a failure is experienced
|
||
// during a reparse, the subsequent IRPs will be failed with the same
|
||
// status code.
|
||
//
|
||
if (Status == STATUS_REPARSE) {
|
||
Status =
|
||
IssueReparseForIrp( Irp, DeviceReference );
|
||
}
|
||
Irp->IoStatus.Status = Status;
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
}
|
||
|
||
if (DeviceListMutex) {
|
||
ExReleaseFastMutexUnsafe( DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
EnableDeviceInterfaces(
|
||
PDEVICE_REFERENCE DeviceReference,
|
||
BOOLEAN EnableState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Walks the device associations and enables or disables
|
||
the device interfaces.
|
||
|
||
Arguments:
|
||
PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference
|
||
|
||
BOOLEAN EnableState -
|
||
TRUE if enabling, FALSE otherwise
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PDEVICE_ASSOCIATION DeviceAssociation;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Walk the associated list of device interface aliases
|
||
//
|
||
|
||
for (DeviceAssociation =
|
||
(PDEVICE_ASSOCIATION) DeviceReference->DeviceAssociations.Flink;
|
||
DeviceAssociation !=
|
||
(PDEVICE_ASSOCIATION) &DeviceReference->DeviceAssociations;
|
||
DeviceAssociation = (PDEVICE_ASSOCIATION) DeviceAssociation->ListEntry.Flink) {
|
||
|
||
if (DeviceAssociation->linkName.Buffer) {
|
||
|
||
//
|
||
// Enable or disable the interface
|
||
//
|
||
|
||
IoSetDeviceInterfaceState(
|
||
&DeviceAssociation->linkName, EnableState );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SweeperWorker(
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Sweeper work item handler. This routine actually performs the
|
||
work of sweeping the device reference list and marking devices
|
||
for deletion.
|
||
|
||
Arguments:
|
||
IN PVOID Context -
|
||
pointer to context
|
||
|
||
Return:
|
||
No return value.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN RescheduleTimer = FALSE;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PFDO_EXTENSION FdoExtension = (PFDO_EXTENSION) Context;
|
||
LARGE_INTEGER TimerPeriod;
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("SweeperWorker") );
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
//
|
||
// While walking the device reference list, the next timeout period
|
||
// is computed as the minimum of the remaining timeout periods for
|
||
// all device references.
|
||
//
|
||
|
||
TimerPeriod.QuadPart = MAXLONGLONG;
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
|
||
if ((DeviceReference->PhysicalDeviceObject) &&
|
||
(DeviceReference->SweeperMarker == SweeperDeviceActive)) {
|
||
|
||
PPDO_EXTENSION PdoExtension;
|
||
|
||
PdoExtension =
|
||
*(PPDO_EXTENSION *)
|
||
DeviceReference->PhysicalDeviceObject->DeviceExtension;
|
||
if (!PdoExtension->DeviceReferenceCount &&
|
||
(DeviceReference->State != ReferenceFailedStart)) {
|
||
LONGLONG TimeDelta;
|
||
|
||
//
|
||
// Compute the remaining timeout using the current time.
|
||
// If the timeout has expired, mark this device for removal.
|
||
//
|
||
|
||
TimeDelta =
|
||
KeQueryPerformanceCounter( NULL ).QuadPart -
|
||
DeviceReference->IdleStartTime.QuadPart;
|
||
|
||
//
|
||
// Watch for roll-over in the timer.
|
||
//
|
||
|
||
if (TimeDelta < 0) {
|
||
TimeDelta += MAXLONGLONG;
|
||
}
|
||
|
||
DeviceReference->TimeoutRemaining.QuadPart =
|
||
DeviceReference->TimeoutPeriod.QuadPart - TimeDelta;
|
||
|
||
if (DeviceReference->TimeoutRemaining.QuadPart <= 0) {
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceRemoval;
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("DeviceReference: %x, timed out", DeviceReference) );
|
||
|
||
if (DeviceReference->State < ReferenceWaitingForStart) {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_TERSE,
|
||
("DeviceReference: %x, failed to start", DeviceReference) );
|
||
|
||
//
|
||
// This device has not responded -- thus, installation
|
||
// failed or it failed to start.
|
||
//
|
||
|
||
//
|
||
// Keep this PDO in tact so that Plug-And-Play will
|
||
// continue to see the device as installed. Hopefully,
|
||
// it will attempt to complete the installation of this
|
||
// device.
|
||
//
|
||
|
||
DeviceReference->State = ReferenceFailedStart;
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceActive;
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("disabling device interfaces for device reference: %x", DeviceReference) );
|
||
|
||
EnableDeviceInterfaces( DeviceReference, FALSE );
|
||
|
||
// Complete any pending I/O with failure.
|
||
|
||
CompletePendingIo(
|
||
DeviceReference, NULL, STATUS_OBJECT_NAME_NOT_FOUND );
|
||
|
||
} else {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("marked %08x for removal", DeviceReference->PhysicalDeviceObject) );
|
||
|
||
//
|
||
// This device has been marked for removal, request
|
||
// a bus reenumeration.
|
||
//
|
||
IoInvalidateDeviceRelations(
|
||
FdoExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
}
|
||
} else {
|
||
//
|
||
// A timer will be rescheduled, compute the minimum timeout
|
||
// period required.
|
||
//
|
||
TimerPeriod.QuadPart =
|
||
min(
|
||
TimerPeriod.QuadPart,
|
||
DeviceReference->TimeoutRemaining.QuadPart
|
||
);
|
||
RescheduleTimer = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Continuing sweeping the device list while we have devices
|
||
// without device object references.
|
||
//
|
||
|
||
if (RescheduleTimer) {
|
||
|
||
//
|
||
// Reschedule the timer to attempt a removal later.
|
||
//
|
||
|
||
TimerPeriod.QuadPart =
|
||
-1L *
|
||
(LONGLONG)
|
||
KSCONVERT_PERFORMANCE_TIME(
|
||
FdoExtension->CounterFrequency.QuadPart,
|
||
TimerPeriod );
|
||
|
||
|
||
if (!TimerPeriod.QuadPart) {
|
||
TimerPeriod.QuadPart = SWEEPER_TIMER_FREQUENCY;
|
||
}
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("setting timer for %d ms",
|
||
((TimerPeriod.QuadPart * -1L) / _100NS_IN_MS)) );
|
||
|
||
KeSetTimer(
|
||
&FdoExtension->SweeperTimer,
|
||
TimerPeriod,
|
||
&FdoExtension->SweeperDpc );
|
||
}
|
||
else {
|
||
InterlockedExchange( &FdoExtension->TimerScheduled, FALSE );
|
||
}
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("SweeperWorker, exit") );
|
||
}
|
||
|
||
|
||
VOID
|
||
InterfaceReference(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This is the standard bus interface reference function.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
ASSERT( PdoExtension->ExtensionType == ExtensionTypePdo );
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("Referencing interface") );
|
||
InterlockedIncrement(
|
||
&PdoExtension->BusDeviceExtension->InterfaceReferenceCount );
|
||
}
|
||
|
||
|
||
VOID
|
||
InterfaceDereference(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This is the standard bus interface dereference function.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
ASSERT( PdoExtension->ExtensionType == ExtensionTypePdo );
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("Dereferencing interface") );
|
||
InterlockedDecrement(
|
||
&PdoExtension->BusDeviceExtension->InterfaceReferenceCount );
|
||
}
|
||
|
||
|
||
VOID
|
||
ReferenceDeviceObject(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Increments the reference count for the given physical device object.
|
||
This assures that the device object will remain active and enumerated
|
||
by SWENUM until the reference count returns to 0.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
ASSERT( PdoExtension->ExtensionType == ExtensionTypePdo );
|
||
InterlockedIncrement( &PdoExtension->DeviceReferenceCount );
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("Referenced PDO: %d", PdoExtension->DeviceReferenceCount) );
|
||
}
|
||
|
||
|
||
LARGE_INTEGER
|
||
ComputeNextSweeperPeriod(
|
||
IN PFDO_EXTENSION FdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Computes the next sweeper timeout period based on the requirements of
|
||
the device reference list.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION FdoExtension -
|
||
pointer to the FDO extension
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
LARGE_INTEGER TimerPeriod;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
|
||
TimerPeriod.QuadPart = MAXLONGLONG;
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
|
||
if ((DeviceReference->PhysicalDeviceObject) &&
|
||
(DeviceReference->SweeperMarker == SweeperDeviceActive)) {
|
||
|
||
PPDO_EXTENSION PdoExtension;
|
||
|
||
PdoExtension =
|
||
*(PPDO_EXTENSION *)
|
||
DeviceReference->PhysicalDeviceObject->DeviceExtension;
|
||
|
||
if ((!PdoExtension->DeviceReferenceCount) &&
|
||
(DeviceReference->State != ReferenceFailedStart) &&
|
||
(DeviceReference->TimeoutRemaining.QuadPart)) {
|
||
|
||
//
|
||
// Compute the minimum timeout period required.
|
||
//
|
||
|
||
TimerPeriod.QuadPart =
|
||
min(
|
||
TimerPeriod.QuadPart,
|
||
DeviceReference->TimeoutRemaining.QuadPart
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
if (TimerPeriod.QuadPart == MAXLONGLONG) {
|
||
TimerPeriod.QuadPart = SWEEPER_TIMER_FREQUENCY_IN_SECS *
|
||
FdoExtension->CounterFrequency.QuadPart;
|
||
}
|
||
|
||
TimerPeriod.QuadPart =
|
||
-1L *
|
||
(LONGLONG) KSCONVERT_PERFORMANCE_TIME(
|
||
FdoExtension->CounterFrequency.QuadPart,
|
||
TimerPeriod );
|
||
|
||
return TimerPeriod;
|
||
}
|
||
|
||
|
||
VOID
|
||
DereferenceDeviceObject(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Decrements the reference count of the physical device object. When
|
||
this PDO's reference count is 0 it become eligible for removal.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
ASSERT( PdoExtension->ExtensionType == ExtensionTypePdo );
|
||
ASSERT( PdoExtension->DeviceReferenceCount );
|
||
|
||
if (!InterlockedDecrement( &PdoExtension->DeviceReferenceCount )) {
|
||
|
||
PDEVICE_REFERENCE DeviceReference = PdoExtension->DeviceReference;
|
||
|
||
//
|
||
// We have a device that we will attempt to close after sweeping.
|
||
//
|
||
|
||
// Reset idle period start.
|
||
|
||
DeviceReference->IdleStartTime = KeQueryPerformanceCounter( NULL );
|
||
|
||
//
|
||
// Reset the timeout period. If a timer is not already scheduled,
|
||
// set the timeout based on the requirement for this device.
|
||
//
|
||
|
||
DeviceReference->TimeoutRemaining = DeviceReference->TimeoutPeriod;
|
||
|
||
if (!InterlockedExchange(
|
||
&PdoExtension->BusDeviceExtension->TimerScheduled,
|
||
TRUE )) {
|
||
|
||
LARGE_INTEGER TimerPeriod;
|
||
|
||
TimerPeriod =
|
||
ComputeNextSweeperPeriod( PdoExtension->BusDeviceExtension );
|
||
|
||
if (!TimerPeriod.QuadPart) {
|
||
TimerPeriod.QuadPart = SWEEPER_TIMER_FREQUENCY;
|
||
}
|
||
KeSetTimer(
|
||
&PdoExtension->BusDeviceExtension->SweeperTimer,
|
||
TimerPeriod,
|
||
&PdoExtension->BusDeviceExtension->SweeperDpc );
|
||
}
|
||
}
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("Dereferenced PDO: %d", PdoExtension->DeviceReferenceCount) );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
QueryReferenceString(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN OUT PWCHAR *String
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Creates a buffer from the paged pool and copies the reference string
|
||
associated with this PDO. The caller is expected to free this buffer
|
||
using ExFreePool().
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO extension
|
||
|
||
IN OUT PWCHAR *String -
|
||
pointer to receive an array pointer containing the
|
||
reference string
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG StringLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( PdoExtension->ExtensionType == ExtensionTypePdo );
|
||
|
||
StringLength =
|
||
wcslen( PdoExtension->DeviceReference->DeviceReferenceString ) *
|
||
sizeof( WCHAR ) + sizeof( UNICODE_NULL );
|
||
|
||
*String =(PWCHAR)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
StringLength,
|
||
POOLTAG_DEVICE_REFERENCE_STRING );
|
||
|
||
if (*String == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (FAILED( StringCbCopy(
|
||
*String,
|
||
StringLength,
|
||
PdoExtension->DeviceReference->DeviceReferenceString ) )) {
|
||
ExFreePool( *String );
|
||
*String = NULL;
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsServiceBusEnumPnpRequest(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Services the PnP request on behalf of the demand-load bus enumerator
|
||
object as created with KsCreateBusEnumObject().
|
||
|
||
N.B. This service does not complete the IRP.
|
||
|
||
The following Plug and Play IRPs are handle by this service for the
|
||
functional device object (FDO) or parent device:
|
||
|
||
IRP_MN_START_DEVICE
|
||
IRP_MN_QUERY_BUS_INFORMATION
|
||
IRP_MN_QUERY_DEVICE_RELATIONS
|
||
IRP_MN_QUERY_STOP_DEVICE
|
||
IRP_MN_QUERY_REMOVE_DEVICE
|
||
IRP_MN_CANCEL_STOP_DEVICE
|
||
IRP_MN_CANCEL_REMOVE_DEVICE
|
||
IRP_MN_STOP_DEVICE
|
||
IRP_MN_REMOVE_DEVICE
|
||
|
||
The following Plug and Play IRPs are handle by this service for the
|
||
physical device object (CDO) or child device:
|
||
|
||
IRP_MN_START_DEVICE
|
||
IRP_MN_QUERY_STOP_DEVICE
|
||
IRP_MN_QUERY_REMOVE_DEVICE
|
||
IRP_MN_STOP_DEVICE
|
||
IRP_MN_REMOVE_DEVICE
|
||
IRP_MN_QUERY_DEVICE_RELATIONS (TargetDeviceRelations)
|
||
IRP_MN_QUERY_PNP_DEVICE_STATE
|
||
IRP_MN_QUERY_ID
|
||
IRP_MN_QUERY_INTERFACE
|
||
IRP_MN_QUERY_RESOURCES
|
||
IRP_MN_QUERY_RESOURCE_REQUIREMENTS
|
||
IRP_MN_READ_CONFIG
|
||
IRP_MN_WRITE_CONFIG
|
||
IRP_MN_QUERY_CAPABILITIES
|
||
|
||
|
||
A caller of this service should first determine if the request is for
|
||
a child or the parent using KsIsBusEnumChildDevice(). If this service
|
||
fails, then complete the IRP with the status code. Otherwise, call
|
||
this service to perform the initial processing for the bus and if the
|
||
request is for the parent, perform whatever additional processing may
|
||
be necessary for the parent device as demonstrated in the code fragment
|
||
below:
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Get the PnpDeviceObject and determine FDO/PDO.
|
||
//
|
||
|
||
Status = KsIsBusEnumChildDevice( DeviceObject, &ChildDevice );
|
||
|
||
//
|
||
// If we're unable to obtain any of this information, fail now.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
CompleteIrp( Irp, Status, IO_NO_INCREMENT );
|
||
return Status;
|
||
}
|
||
|
||
Status = KsServiceBusEnumPnpRequest( DeviceObject, Irp );
|
||
|
||
//
|
||
// FDO processing may return STATUS_NOT_SUPPORTED or may require
|
||
// overrides.
|
||
//
|
||
|
||
if (!ChildDevice) {
|
||
NTSTATUS tempStatus;
|
||
|
||
//
|
||
// FDO case
|
||
//
|
||
// First retrieve the DO we will forward everything to...
|
||
//
|
||
tempStatus = KsGetBusEnumPnpDeviceObject( DeviceObject, &PnpDeviceObject );
|
||
|
||
if (!NT_SUCCESS( tempStatus )) {
|
||
//
|
||
// No DO to forward to. Actually a fatal error, but just complete
|
||
// with an error status.
|
||
//
|
||
return CompleteIrp( Irp, tempStatus, IO_NO_INCREMENT );
|
||
}
|
||
|
||
switch (irpSp->MinorFunction) {
|
||
|
||
case IRP_MN_QUERY_RESOURCES:
|
||
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
||
//
|
||
// This is normally passed on to the PDO, but since this is a
|
||
// software only device, resources are not required.
|
||
//
|
||
Irp->IoStatus.Information = (ULONG_PTR)NULL;
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
|
||
//
|
||
// Forward everything...
|
||
//
|
||
break;
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
//
|
||
// The KsBusEnum services cleaned up attachments, etc. However,
|
||
// we must remove our own FDO.
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
IoDeleteDevice( DeviceObject );
|
||
break;
|
||
}
|
||
|
||
if (Status != STATUS_NOT_SUPPORTED) {
|
||
|
||
//
|
||
// Set the Irp status only if we have something to add.
|
||
//
|
||
Irp->IoStatus.Status = Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Forward this IRP down the stack only if we are successful or
|
||
// we don't know how to handle this Irp.
|
||
//
|
||
if (NT_SUCCESS( Status ) || (Status == STATUS_NOT_SUPPORTED)) {
|
||
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
return IoCallDriver( PnpDeviceObject, Irp );
|
||
}
|
||
|
||
//
|
||
// On error, fall through and complete the IRP with the status.
|
||
//
|
||
}
|
||
|
||
|
||
//
|
||
// KsServiceBusEnumPnpRequest() handles all other child PDO requests.
|
||
//
|
||
|
||
if (Status != STATUS_NOT_SUPPORTED) {
|
||
Irp->IoStatus.Status = Status;
|
||
} else {
|
||
Status = Irp->IoStatus.Status;
|
||
}
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
return Status;
|
||
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT DeviceObject -
|
||
pointer to the device object
|
||
|
||
IN PIRP Irp -
|
||
pointer to the associated Irp
|
||
|
||
Return:
|
||
STATUS_NOT_SUPPORTED if not handled by this service,
|
||
STATUS_INVALID_DEVICE_REQUEST if the device object is neither a parent
|
||
or child of the demand-load bus enumerator object, otherwise the status
|
||
code for the IRP processing.
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION irpSp;
|
||
NTSTATUS Status;
|
||
DEVICE_RELATION_TYPE relationType;
|
||
BUS_QUERY_ID_TYPE busQueryId;
|
||
PVOID Extension;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get a pointer to our stack location and take appropriate action based
|
||
// on the minor function.
|
||
//
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Extension = *(PVOID *) DeviceObject->DeviceExtension;
|
||
if (!Extension) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
|
||
switch (((PPDO_EXTENSION) Extension)->ExtensionType) {
|
||
|
||
case ExtensionTypeFdo:
|
||
|
||
//
|
||
// This IRP is for the Functional Device Object (FDO)
|
||
//
|
||
|
||
switch (irpSp->MinorFunction) {
|
||
|
||
case IRP_MN_START_DEVICE:
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
relationType = irpSp->Parameters.QueryDeviceRelations.Type;
|
||
if (relationType != TargetDeviceRelation) {
|
||
Status =
|
||
QueryDeviceRelations(
|
||
(PFDO_EXTENSION) Extension,
|
||
relationType,
|
||
(PDEVICE_RELATIONS *) &Irp->IoStatus.Information );
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_QUERY_STOP_DEVICE:
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
{
|
||
PFDO_EXTENSION FdoExtension;
|
||
|
||
FdoExtension = (PFDO_EXTENSION) Extension;
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE, ("query remove device, FDO %x", DeviceObject) );
|
||
|
||
//
|
||
// Block out sweeper...
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (InterlockedExchange( &FdoExtension->TimerScheduled, TRUE )) {
|
||
if (!KeCancelTimer( &FdoExtension->SweeperTimer )) {
|
||
Status = STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
{
|
||
PFDO_EXTENSION FdoExtension;
|
||
LARGE_INTEGER TimerPeriod;
|
||
|
||
//
|
||
// It's OK to let the sweeper run again.
|
||
//
|
||
FdoExtension = (PFDO_EXTENSION) Extension;
|
||
InterlockedExchange( &FdoExtension->TimerScheduled, FALSE );
|
||
|
||
TimerPeriod = ComputeNextSweeperPeriod( FdoExtension );
|
||
|
||
if (TimerPeriod.QuadPart) {
|
||
InterlockedExchange( &FdoExtension->TimerScheduled, TRUE );
|
||
|
||
KeSetTimer(
|
||
&FdoExtension->SweeperTimer,
|
||
TimerPeriod,
|
||
&FdoExtension->SweeperDpc );
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_STOP_DEVICE:
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("surprise removal, FDO %x", DeviceObject) );
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
{
|
||
|
||
PFDO_EXTENSION FdoExtension;
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("remove device, FDO %x", DeviceObject) );
|
||
|
||
FdoExtension = (PFDO_EXTENSION) Extension;
|
||
|
||
if (FdoExtension->linkName.Buffer) {
|
||
|
||
//
|
||
// Remove device interface association...
|
||
//
|
||
|
||
IoSetDeviceInterfaceState( &FdoExtension->linkName, FALSE );
|
||
|
||
//
|
||
// and free the symbolic link.
|
||
//
|
||
|
||
ExFreePool( FdoExtension->linkName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Delete the copy of the registry path.
|
||
//
|
||
|
||
ExFreePool( FdoExtension->BaseRegistryPath.Buffer );
|
||
|
||
//
|
||
// Detach the device if attached.
|
||
//
|
||
|
||
if (FdoExtension->AttachedDevice) {
|
||
IoDetachDevice( FdoExtension->PnpDeviceObject );
|
||
}
|
||
|
||
ExFreePool( FdoExtension );
|
||
|
||
//
|
||
// Clear the reference in the DeviceExtension to the FDO_EXTENSION
|
||
//
|
||
*(PVOID *)DeviceObject->DeviceExtension = NULL;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case ExtensionTypePdo:
|
||
{
|
||
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
|
||
DeviceReference =
|
||
((PPDO_EXTENSION) Extension)->DeviceReference;
|
||
|
||
if (DeviceObject != DeviceReference->PhysicalDeviceObject) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("specified PDO is not valid (%08x).", DeviceObject) );
|
||
Status = STATUS_NO_SUCH_DEVICE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// This IRP is for the Physical Device Object (PDO)
|
||
//
|
||
|
||
switch (irpSp->MinorFunction) {
|
||
|
||
case IRP_MN_START_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("start device, PDO %x", DeviceObject) );
|
||
Status = StartDevice( DeviceObject );
|
||
#if defined( WIN9X_KS )
|
||
if (NT_SUCCESS( Status )) {
|
||
CompletePendingIo(
|
||
DeviceReference,
|
||
&((PPDO_EXTENSION)Extension)->BusDeviceExtension->DeviceListMutex,
|
||
STATUS_REPARSE );
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
case IRP_MN_QUERY_STOP_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("query/cancel stop device, PDO %x", DeviceObject) );
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE, ("query remove device, PDO %x", DeviceObject) );
|
||
|
||
if ((((PPDO_EXTENSION) Extension)->DeviceReferenceCount) ||
|
||
(DeviceReference->State == ReferenceFailedStart)) {
|
||
Status = STATUS_INVALID_DEVICE_STATE;
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_STOP_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("stop device, PDO %x", DeviceObject) );
|
||
DeviceReference->State = ReferenceStopped;
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_SURPRISE_REMOVAL:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("surprise removal, PDO %x", DeviceObject) );
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("cancel remove device, PDO %x", DeviceObject) );
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_REMOVE_DEVICE:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("remove device, PDO %x", DeviceObject) );
|
||
Status = RemoveDevice( DeviceObject );
|
||
break;
|
||
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("PDO QueryDeviceRelations") );
|
||
relationType = irpSp->Parameters.QueryDeviceRelations.Type;
|
||
if (relationType == TargetDeviceRelation) {
|
||
Status =
|
||
QueryDeviceRelations(
|
||
(PFDO_EXTENSION) Extension,
|
||
relationType,
|
||
(PDEVICE_RELATIONS *) &Irp->IoStatus.Information );
|
||
} else {
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
||
{
|
||
|
||
PFDO_EXTENSION BusDeviceExtension;
|
||
PPNP_DEVICE_STATE DeviceState;
|
||
|
||
DeviceState = (PPNP_DEVICE_STATE) &Irp->IoStatus.Information;
|
||
|
||
*DeviceState |= PNP_DEVICE_DONT_DISPLAY_IN_UI | PNP_DEVICE_NOT_DISABLEABLE;
|
||
|
||
#if !defined( WIN9X_KS )
|
||
//
|
||
// NTOSKRNL fails IRP_MJ_CREATEs that arrive before
|
||
// the device stack is started. The very first
|
||
// IRP_MN_QUERY_PNP_DEVICE_STATE received after the IRP_MN_START_DEVICE
|
||
// will be a notification that the stack has started.
|
||
//
|
||
BusDeviceExtension = ((PPDO_EXTENSION)Extension)->BusDeviceExtension;
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
if (DeviceReference->State == ReferenceWaitingForStart) {
|
||
DeviceReference->State = ReferenceStarted;
|
||
CompletePendingIo( DeviceReference, NULL, STATUS_REPARSE );
|
||
}
|
||
ExReleaseFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
#endif
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_QUERY_ID:
|
||
|
||
//
|
||
// Get a pointer to the query id structure and process.
|
||
//
|
||
|
||
busQueryId = irpSp->Parameters.QueryId.IdType;
|
||
Status =
|
||
QueryId(
|
||
(PPDO_EXTENSION) Extension,
|
||
busQueryId,
|
||
(PWSTR *)&Irp->IoStatus.Information );
|
||
break;
|
||
|
||
case IRP_MN_QUERY_INTERFACE:
|
||
if (IsEqualGUID(
|
||
irpSp->Parameters.QueryInterface.InterfaceType,
|
||
&BUSID_SoftwareDeviceEnumerator) &&
|
||
(irpSp->Parameters.QueryInterface.Size ==
|
||
sizeof( BUS_INTERFACE_SWENUM )) &&
|
||
(irpSp->Parameters.QueryInterface.Version ==
|
||
BUS_INTERFACE_SWENUM_VERSION )) {
|
||
|
||
PBUS_INTERFACE_SWENUM BusInterface;
|
||
|
||
BusInterface =
|
||
(PBUS_INTERFACE_SWENUM)irpSp->Parameters.QueryInterface.Interface;
|
||
|
||
BusInterface->Interface.Size = sizeof( *BusInterface );
|
||
BusInterface->Interface.Version = BUS_INTERFACE_SWENUM_VERSION;
|
||
BusInterface->Interface.Context = Extension;
|
||
BusInterface->Interface.InterfaceReference = InterfaceReference;
|
||
BusInterface->Interface.InterfaceDereference = InterfaceDereference;
|
||
BusInterface->ReferenceDeviceObject = ReferenceDeviceObject;
|
||
BusInterface->DereferenceDeviceObject = DereferenceDeviceObject;
|
||
BusInterface->QueryReferenceString = QueryReferenceString;
|
||
InterfaceReference( BusInterface->Interface.Context );
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_QUERY_RESOURCES:
|
||
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
||
|
||
//
|
||
// No resource requirements nor resource assignment.
|
||
//
|
||
|
||
Irp->IoStatus.Information = (ULONG_PTR)NULL;
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_READ_CONFIG:
|
||
case IRP_MN_WRITE_CONFIG:
|
||
|
||
//
|
||
// There is no bus specific configuration to be written
|
||
// or read for a device, just return success with no bytes
|
||
// read/written.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_CAPABILITIES:
|
||
{
|
||
|
||
PDEVICE_CAPABILITIES Caps;
|
||
USHORT size, version;
|
||
ULONG reserved;
|
||
|
||
//
|
||
// fill in the capabilities structure
|
||
//
|
||
|
||
Caps = irpSp->Parameters.DeviceCapabilities.Capabilities;
|
||
size = Caps->Size;
|
||
version = Caps->Version;
|
||
reserved = Caps->Reserved;
|
||
RtlZeroMemory( Caps, sizeof( DEVICE_CAPABILITIES ) );
|
||
Caps->Size = size;
|
||
Caps->Version = version;
|
||
Caps->Reserved = reserved;
|
||
|
||
// Cannot wake the system.
|
||
Caps->SystemWake = PowerSystemWorking;
|
||
Caps->DeviceWake = PowerDeviceD0;
|
||
|
||
Caps->DeviceState[PowerSystemUnspecified] = PowerDeviceUnspecified;
|
||
Caps->DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
||
Caps->DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
|
||
Caps->DeviceState[PowerSystemSleeping2] = PowerDeviceD1;
|
||
Caps->DeviceState[PowerSystemSleeping3] = PowerDeviceD1;
|
||
Caps->DeviceState[PowerSystemHibernate] = PowerDeviceD1;
|
||
Caps->DeviceState[PowerSystemShutdown] = PowerDeviceD1;
|
||
|
||
// Have no latencies.
|
||
|
||
// Do not support locking or ejecting.
|
||
Caps->LockSupported = FALSE;
|
||
Caps->EjectSupported = FALSE;
|
||
|
||
// Technically there is no physical device to remove, but this bus
|
||
// driver can yank the PDO from the PlugPlay system, when ever it
|
||
// we hit an access timeout.
|
||
Caps->SurpriseRemovalOK = TRUE;
|
||
Caps->Removable = FALSE;
|
||
Caps->DockDevice = FALSE;
|
||
Caps->UniqueID = TRUE;
|
||
|
||
Caps->SilentInstall = TRUE;
|
||
|
||
//
|
||
// return the number of bytes read
|
||
//
|
||
|
||
Irp->IoStatus.Information = sizeof( DEVICE_CAPABILITIES );
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case IRP_MN_QUERY_BUS_INFORMATION:
|
||
{
|
||
|
||
PPNP_BUS_INFORMATION BusInformation;
|
||
|
||
BusInformation =
|
||
(PPNP_BUS_INFORMATION)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof( PNP_BUS_INFORMATION ),
|
||
POOLTAG_DEVICE_BUSID );
|
||
if (NULL == BusInformation) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
RtlZeroMemory( BusInformation, sizeof( PNP_BUS_INFORMATION ) );
|
||
BusInformation->BusTypeGuid = BUSID_SoftwareDeviceEnumerator;
|
||
BusInformation->LegacyBusType = InterfaceTypeUndefined;
|
||
Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
}
|
||
break;
|
||
|
||
}
|
||
|
||
default:
|
||
|
||
//
|
||
// This is neither an FDO or PDO, return invalid device request.
|
||
//
|
||
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
}
|
||
|
||
|
||
return Status;
|
||
}
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsGetBusEnumIdentifier(
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Returns the software bus enumerator id for the bus device associated with
|
||
this request.
|
||
|
||
Arguments:
|
||
IN PIRP Irp -
|
||
I/O request packet specifying the address and size of the user output
|
||
buffer to receive the requested bus enumerator id.
|
||
|
||
Return:
|
||
STATUS_SUCCESS if the bus enumerator id was retrieved succesfully,
|
||
STATUS_INVALID_PARAMETER if the specified device is not valid,
|
||
STATUS_BUFFER_TOO_SMALL is the specified buffer was not large enough,
|
||
STATUS_BUFFER_OVERFLOW (with the required size) if no buffer was specified,
|
||
otherwise the status return from probing the user buffer, or pool allocation
|
||
failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFDO_EXTENSION BusExtension;
|
||
PIO_STACK_LOCATION irpSp;
|
||
ULONG BufferLength, IdLength;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
BusExtension = *(PVOID *) irpSp->DeviceObject->DeviceExtension;
|
||
if (!BusExtension) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
if (BusExtension->ExtensionType == ExtensionTypePdo) {
|
||
BusExtension = ((PPDO_EXTENSION) BusExtension)->BusDeviceExtension;
|
||
}
|
||
|
||
IdLength =
|
||
(wcslen( BusExtension->BusPrefix ) * sizeof( WCHAR )) +
|
||
sizeof( UNICODE_NULL );
|
||
|
||
//
|
||
// The IRP serviced by this routine should specify the I/O buffering method
|
||
// of METHOD_NEITHER. For this type of (non)-buffering, the I/O manager
|
||
// does not allocate anything for the SystemBuffer. OutputBufferLength and
|
||
// InputBufferLength are supplied exactly as specified by the caller, with
|
||
// the address of the caller-supplied output buffer specified by UserBuffer,
|
||
// and the address of the caller-supplied input buffer specified by
|
||
// Type3InputBuffer. The I/O manager has done NO validation whatsoever on
|
||
// these buffers, so they must be probed by the driver for their specified
|
||
// length, for the access desired.
|
||
//
|
||
ASSERT( (irpSp->Parameters.DeviceIoControl.IoControlCode & 3) == METHOD_NEITHER );
|
||
|
||
BufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
if (BufferLength > 0) {
|
||
//
|
||
// The caller claims they've supplied us with a buffer, first check if
|
||
// it is large enough for the data requested.
|
||
//
|
||
if (BufferLength < IdLength) {
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// The caller says their output buffer is large enough for the data
|
||
// requested, so we'll try to pass it back to them.
|
||
//
|
||
// To do this, we:
|
||
//
|
||
// - Validate the caller-specified output buffer for write access, for
|
||
// the length specified.
|
||
//
|
||
// - Allocate a SystemBuffer, with quota charged against
|
||
// the caller (this can be done since for METHOD_NEITHER IRPs, the
|
||
// driver can be sent such a request only while it is running in the
|
||
// context of the thread that originates the I/O control request).
|
||
//
|
||
// - Modify the IRP to specify buffered I/O, and that upon completion,
|
||
// the I/O manager should copy what's in the SystemBuffer back to the
|
||
// user buffer (already probed) and free the SystemBuffer.
|
||
//
|
||
|
||
try {
|
||
//
|
||
// Probe the destination buffer where the I/O manager will copy data
|
||
// from the system buffer to for write access, for the length the
|
||
// caller specified.
|
||
//
|
||
ProbeForWrite(Irp->UserBuffer, BufferLength, sizeof(BYTE));
|
||
|
||
//
|
||
// Allocate safe and aligned buffer. Note there's no need to zero
|
||
// the memory to prevent random kernel-memory from being returned to
|
||
// the caller because the I/O manager will only copy the amount we
|
||
// specify in irp->IoStatus.Information to the caller's buffer.
|
||
//
|
||
Irp->AssociatedIrp.SystemBuffer =
|
||
ExAllocatePoolWithQuotaTag(
|
||
NonPagedPool,
|
||
BufferLength,
|
||
POOLTAG_DEVICE_IO_BUFFER );
|
||
|
||
//
|
||
// Modify the IRP for buffered I/O, such that data is copied from
|
||
// the system buffer to the user buffer, and the system buffer is
|
||
// deallocated by the I/O manager on completion.
|
||
//
|
||
Irp->Flags |=
|
||
(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Note that if the exception was raised during the probe, no
|
||
// buffering will be done upon completion of the IRP. If
|
||
// ExAllocatePoolWithQuotaTag raised the exception, no buffer was
|
||
// allocated, and we have not modified the buffering type for the
|
||
// IRP, so the I/O manager will do nothing on completion. If both
|
||
// operations were successful, we have a buffer which the I/O
|
||
// manager will copy from and free the upon completion.
|
||
//
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Copy the BusPrefix string to the SystemBuffer.
|
||
//
|
||
ASSERT( IdLength <= BufferLength );
|
||
|
||
if (FAILED( StringCbCopyEx(
|
||
Irp->AssociatedIrp.SystemBuffer,
|
||
IdLength,
|
||
BusExtension->BusPrefix,
|
||
NULL, NULL,
|
||
STRSAFE_NULL_ON_FAILURE ) )) {
|
||
Irp->IoStatus.Information = 0;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Irp->IoStatus.Information = IdLength;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
//
|
||
// No output buffer was specified by the caller.
|
||
// Return the required size.
|
||
//
|
||
Irp->IoStatus.Information = IdLength;
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
}
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsIsBusEnumChildDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
OUT PBOOLEAN ChildDevice
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Returns TRUE if the given device object is a child device of the demand-load
|
||
bus enumerator object, FALSE otherwise.
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT DeviceObject -
|
||
pointer to a device object
|
||
|
||
IN PBOOLEAN ChildDevice
|
||
pointer to receive BOOLEAN result
|
||
|
||
Return:
|
||
STATUS_SUCCESS if DeviceExtension is valid, otherwise an error code.
|
||
If the device object is determined to be a child device, *ChildDevice
|
||
is set to TRUE.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID Extension;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (DeviceObject->DeviceExtension) {
|
||
Extension = *(PVOID *) DeviceObject->DeviceExtension;
|
||
*ChildDevice = FALSE;
|
||
if (Extension) {
|
||
*ChildDevice =
|
||
(ExtensionTypePdo == ((PPDO_EXTENSION) Extension)->ExtensionType);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsGetBusEnumPnpDeviceObject(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
OUT PDEVICE_OBJECT *PnpDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Returns the associated Pnp Device Object stack to which this
|
||
device object is attached.
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT DeviceObject -
|
||
device object pointer
|
||
|
||
OUT PDEVICE_OBJECT *PnpDeviceObject -
|
||
pointer resultant device object pointer
|
||
|
||
Return:
|
||
STATUS_SUCCESS or STATUS_INVALID_PARAMETER
|
||
|
||
--*/
|
||
|
||
{
|
||
PFDO_EXTENSION FdoExtension;
|
||
|
||
PAGED_CODE();
|
||
FdoExtension = *(PFDO_EXTENSION *) DeviceObject->DeviceExtension;
|
||
if (!FdoExtension) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
if (ExtensionTypeFdo != FdoExtension->ExtensionType) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
*PnpDeviceObject = FdoExtension->PnpDeviceObject;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
StartDevice(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Processes the start device request for the given PDO by scanning
|
||
the device associations looking for pending I/O. If an I/O request
|
||
is found (presumably an IRP_MJ_CREATE), the IRP is completed with
|
||
STATUS_REPARSE via the IssueReparseForIrp() function.
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject -
|
||
pointer to the device object
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, STATUS_INSUFFICIENT_RESOURCES if
|
||
pool allocation failure otherwise an appropriate error return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PPDO_EXTENSION PdoExtension;
|
||
ULONG ResultLength;
|
||
WCHAR DeviceName[ 256 ];
|
||
|
||
|
||
PdoExtension = *(PPDO_EXTENSION *) PhysicalDeviceObject->DeviceExtension;
|
||
DeviceReference = PdoExtension->DeviceReference;
|
||
|
||
if (DeviceReference->State == ReferenceStopped) {
|
||
DeviceReference->State = ReferenceStarted;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (PhysicalDeviceObject->AttachedDevice) {
|
||
|
||
//
|
||
// Validate that the child is setting DO_POWER_PAGABLE.
|
||
// Fatal error if not, thus issue the bugcheck.
|
||
//
|
||
|
||
if (0 ==
|
||
(PhysicalDeviceObject->AttachedDevice->Flags & DO_POWER_PAGABLE)) {
|
||
#if defined( WIN9X_KS )
|
||
DbgPrint(
|
||
"ERROR! the child FDO has not set DO_POWER_PAGABLE, FDO: %08x\n",
|
||
PhysicalDeviceObject->AttachedDevice);
|
||
|
||
DbgPrint("**** The owning driver is broken and is not acceptable per ****\n");
|
||
DbgPrint("**** HCT requirements for WDM drivers. This driver will ****\n");
|
||
DbgPrint("**** bugcheck running under Windows 2000. ****\n");
|
||
|
||
#ifndef WIN98GOLD_KS
|
||
DbgBreakPoint();
|
||
#endif
|
||
#else
|
||
_DbgPrintF(
|
||
DEBUGLVL_TERSE,
|
||
("ERROR! the child FDO has not set DO_POWER_PAGABLE, use \"!devobj %08x\" to report the culprit",
|
||
PhysicalDeviceObject->AttachedDevice) );
|
||
KeBugCheckEx (
|
||
DRIVER_POWER_STATE_FAILURE,
|
||
0x101,
|
||
(ULONG_PTR) PhysicalDeviceObject->AttachedDevice,
|
||
(ULONG_PTR) PhysicalDeviceObject,
|
||
(ULONG_PTR) PdoExtension->BusDeviceExtension->PhysicalDeviceObject
|
||
);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
Status =
|
||
IoGetDeviceProperty(
|
||
PhysicalDeviceObject,
|
||
DevicePropertyPhysicalDeviceObjectName,
|
||
sizeof( DeviceName ),
|
||
DeviceName,
|
||
&ResultLength );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_ERROR,
|
||
("failed to retrieve PDO's device name: %x", Status) );
|
||
return Status;
|
||
}
|
||
|
||
DeviceReference->DeviceName =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
ResultLength,
|
||
POOLTAG_DEVICE_NAME );
|
||
|
||
if (NULL == DeviceReference->DeviceName) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory( DeviceReference->DeviceName, DeviceName, ResultLength );
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &PdoExtension->BusDeviceExtension->DeviceListMutex );
|
||
|
||
//
|
||
// Scan the device reference for pending I/O, build the new
|
||
// device path and complete the IRPs with STATUS_REPARSE.
|
||
//
|
||
|
||
//
|
||
// Snap the current time to compute idle time
|
||
//
|
||
|
||
DeviceReference->IdleStartTime = KeQueryPerformanceCounter( NULL );
|
||
#if (DEBUG_LOAD_TIME)
|
||
DeviceReference->LoadTime.QuadPart =
|
||
DeviceReference->IdleStartTime.QuadPart -
|
||
DeviceReference->LoadTime.QuadPart;
|
||
|
||
DeviceReference->LoadTime.QuadPart =
|
||
KSCONVERT_PERFORMANCE_TIME(
|
||
PdoExtension->BusDeviceExtension->CounterFrequency.QuadPart,
|
||
DeviceReference->LoadTime );
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_TERSE,
|
||
("driver load time: %d ms",
|
||
((DeviceReference->LoadTime.QuadPart) / _100NS_IN_MS)) );
|
||
#endif
|
||
|
||
//
|
||
// Adjust the timeout for the default timeout period.
|
||
//
|
||
DeviceReference->TimeoutRemaining = DeviceReference->TimeoutPeriod;
|
||
|
||
if (DeviceReference->State == ReferenceFailedStart) {
|
||
//
|
||
// The interfaces were disabled when the device was detected to
|
||
// have failed the Start or AddDevice. Enable the interfaces to
|
||
// give everyone a chance to try again.
|
||
//
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("enabling device interfaces for device reference: %x", DeviceReference) );
|
||
EnableDeviceInterfaces( DeviceReference, TRUE );
|
||
}
|
||
|
||
//
|
||
// Note, for the Windows NT case, we have to wait for the device stack
|
||
// to complete startup before dispatching the create IRPs. Therefore,
|
||
// we have an extra state (ReferenceWaitingForStart) which transitions
|
||
// to ReferenceStarted after we receive the first IRP_MN_QUERY_PNP_DEVICE_STATE
|
||
// down the stack. This is guaranteed by the Windows NT implementation.
|
||
//
|
||
|
||
#if !defined( WIN9X_KS )
|
||
DeviceReference->State = ReferenceWaitingForStart;
|
||
#else
|
||
DeviceReference->State = ReferenceStarted;
|
||
#endif
|
||
|
||
ExReleaseFastMutexUnsafe( &PdoExtension->BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RemoveDevice(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Processes the remove device request for the given PDO by
|
||
completing any pending I/O with STATUS_OBJECT_NAME_NOT_FOUND and
|
||
deleting the device object.
|
||
|
||
NOTE! IRP_MN_REMOVE_DEVICE is sent to the stack after AddDevice() has
|
||
succeeded. Thus, if we fail during IRP_MN_START_DEVICE we will receive
|
||
an IRP_MN_REMOVE_DEVICE, thus we can perform appropriate cleanup.
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject -
|
||
pointer to the device object
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, STATUS_INSUFFICIENT_RESOURCES if
|
||
pool allocation failure otherwise an appropriate error return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PFDO_EXTENSION BusDeviceExtension;
|
||
PPDO_EXTENSION PdoExtension;
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("RemoveDevice(), entry.") );
|
||
|
||
PdoExtension = *(PPDO_EXTENSION *)PhysicalDeviceObject->DeviceExtension;
|
||
|
||
DeviceReference = PdoExtension->DeviceReference;
|
||
BusDeviceExtension = PdoExtension->BusDeviceExtension;
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
|
||
//
|
||
// If the device failed to start or failed during installation, complete any
|
||
// pending create IRPs as STATUS_OBJECT_NAME_NOT_FOUND. We will mark the
|
||
// device for removal and issue a bus enumeration below. If additional
|
||
// create requests are made before then, we will create a new PDO and
|
||
// attempt to restart the device on the final removal, below.
|
||
//
|
||
|
||
if ((DeviceReference->State < ReferenceStarted) &&
|
||
(DeviceReference->State != ReferenceRemoved)) {
|
||
CompletePendingIo( DeviceReference, NULL, STATUS_OBJECT_NAME_NOT_FOUND );
|
||
}
|
||
|
||
//
|
||
// No matter what circumstances we hit below, we must restart the
|
||
// device before we can accept IRP_MJ_CREATE on the associated PDO.
|
||
//
|
||
|
||
if (DeviceReference->DeviceName) {
|
||
ExFreePool( DeviceReference->DeviceName );
|
||
DeviceReference->DeviceName = NULL;
|
||
}
|
||
|
||
DeviceReference->State = ReferenceRemoved;
|
||
|
||
//
|
||
// In the case where the device is forcefully removed by the system,
|
||
// the device reference marker will be "SweeperDeviceActive".
|
||
// For this case, treat the device as if it just timed out and
|
||
// set the marker as appropriate. Issue a bus reenumeration and
|
||
// this PDO will be finally removed with another IRP_MN_REMOVE_DEVICE
|
||
// Irp after it is reported as not present in the bus scan to the
|
||
// system.
|
||
//
|
||
|
||
if (DeviceReference->SweeperMarker == SweeperDeviceActive) {
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceRemoval;
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("IRP_MN_REMOVE_DEVICE (exit), marked active device %08x for removal",
|
||
DeviceReference->PhysicalDeviceObject) );
|
||
|
||
//
|
||
// This device has been marked for removal, request a bus reenumeration.
|
||
//
|
||
|
||
ExReleaseFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
IoInvalidateDeviceRelations(
|
||
BusDeviceExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if ( DeviceReference->SweeperMarker == SweeperDeviceRemoval ) {
|
||
|
||
//
|
||
// If an non-active device receives a remove IRP before having been
|
||
// reported as not present in the bus scan to the system, then the
|
||
// device is being forcefully removed by the system after its timeout
|
||
// period. Leave the device reference marker in its current state, and
|
||
// succeed the remove IRP without deleting the PDO. We still expect a
|
||
// bus re-enumeration to report the device missing, and will receive
|
||
// another remove IRP to delete the PDO.
|
||
//
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("IRP_MN_REMOVE_DEVICE (exit), non-active device %08x already marked for removal",
|
||
DeviceReference->PhysicalDeviceObject) );
|
||
|
||
//
|
||
// This device has been marked for removal, bus reenumeration is pending.
|
||
//
|
||
|
||
ExReleaseFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if ( !IsListEmpty( &DeviceReference->IoQueue ) ) {
|
||
|
||
//
|
||
// We're about to delete this device, but we've got another pending
|
||
// request. Create a new PDO for this device and if successful, delete
|
||
// the old PDO and request re-enumeration. Otherwise, fall through and
|
||
// complete IRPs as STATUS_OBJECT_NAME_NOT_FOUND and then delete the
|
||
// PDO.
|
||
//
|
||
|
||
Status =
|
||
CreatePdo(
|
||
BusDeviceExtension,
|
||
DeviceReference,
|
||
&DeviceReference->PhysicalDeviceObject );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("RemoveDevice(), created PDO %x.", DeviceReference->PhysicalDeviceObject) );
|
||
|
||
IoDeleteDevice( PhysicalDeviceObject );
|
||
ExFreePool( PdoExtension );
|
||
*(PVOID *)PhysicalDeviceObject->DeviceExtension = NULL;
|
||
|
||
//
|
||
// This device has been accessed after issuing the device removal
|
||
// request. Keep everything associated with this PDO in tact but
|
||
// reset the SweeperMarker and invalidate the bus relations so that
|
||
// we'll reenumerate this PDO.
|
||
//
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceActive;
|
||
DeviceReference->TimeoutRemaining = DeviceReference->TimeoutPeriod;
|
||
|
||
ExReleaseFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Force a re-enumeration of the bus.
|
||
//
|
||
|
||
IoInvalidateDeviceRelations(
|
||
BusDeviceExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("RemoveDevice(), exited w/ reenumeration.") );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
//
|
||
// Unable to create another PDO, fail any pending I/O and
|
||
// mark this device reference as removed.
|
||
//
|
||
DeviceReference->SweeperMarker = SweeperDeviceRemoved;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We're really removing this device, set the PhysicalDeviceObject
|
||
// reference to NULL.
|
||
//
|
||
|
||
DeviceReference->PhysicalDeviceObject = NULL;
|
||
|
||
//
|
||
// Since we are really going to remove this device, scan the device
|
||
// reference for pending I/O and mark them with an error.
|
||
//
|
||
|
||
CompletePendingIo( DeviceReference, NULL, STATUS_OBJECT_NAME_NOT_FOUND );
|
||
|
||
//
|
||
// Free the PDO extension and clear the reference in the
|
||
// DeviceExtension to it.
|
||
//
|
||
ExFreePool( PdoExtension );
|
||
*(PVOID *)PhysicalDeviceObject->DeviceExtension = NULL;
|
||
IoDeleteDevice( PhysicalDeviceObject );
|
||
|
||
ExReleaseFastMutexUnsafe( &BusDeviceExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("RemoveDevice(), exit.") );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsServiceBusEnumCreateRequest(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN OUT PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine services the IRP_MJ_CREATE request for the registered
|
||
device interface by matching the FileObject->FileName with
|
||
the registered "bus" reference strings. If the device reference
|
||
is present, enumerated and created, the IRP is simply re-routed to
|
||
the actual device via IssueReparseForIrp().
|
||
|
||
If the reference string is NULL, it is assumed that this is a request
|
||
for the bus interface and the IRP_MJ_CREATE is completed.
|
||
|
||
If the device reference has not already been enumerated or is not
|
||
active, the IRP is queued and a PDO is created and a bus enumeration is
|
||
initiated by IoInvalidateDeviceRelations().
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT DeviceObject -
|
||
pointer to the device object
|
||
|
||
IN OUT PIRP Irp -
|
||
pointer to the I/O request packet
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, STATUS_OBJECT_NAME_NOT_FOUND if the
|
||
FileObject.FileName is NULL or if the reference string can not be
|
||
located. STATUS_REPARSE may be returned via IssueReparseForIrp()
|
||
else an appropriate error return.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PFDO_EXTENSION FdoExtension;
|
||
|
||
PAGED_CODE();
|
||
|
||
FdoExtension = *(PFDO_EXTENSION *) DeviceObject->DeviceExtension;
|
||
ASSERT( FdoExtension->ExtensionType == ExtensionTypeFdo );
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("KsServiceCreateRequest()") );
|
||
|
||
//
|
||
// Check if we're handling the request for the child.
|
||
//
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("KsServiceCreateRequest() scanning for %S", irpSp->FileObject->FileName.Buffer) );
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
//
|
||
// Walk the device reference list looking for the reference string.
|
||
//
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
|
||
//
|
||
// Search for bus reference string (skip the initial backslash).
|
||
//
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("comparing against %S", DeviceReference->BusReferenceString) );
|
||
|
||
if ((DeviceReference->BusReferenceString) &&
|
||
(0 ==
|
||
_wcsnicmp(
|
||
&irpSp->FileObject->FileName.Buffer[ 1 ],
|
||
DeviceReference->BusReferenceString,
|
||
irpSp->FileObject->FileName.Length ))) {
|
||
|
||
//
|
||
// Found the reference. If the PDO exists, kick off a reparse.
|
||
// Otherwise we need to mark this IRP pending, activate this
|
||
// device, and invalidate device relations to cause the
|
||
// actual enumeration of the device.
|
||
//
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("found device reference") );
|
||
|
||
//
|
||
// If the device has not been enumerated, then
|
||
// create the PDO and invalidate the device
|
||
// relations.
|
||
//
|
||
|
||
if (!DeviceReference->PhysicalDeviceObject) {
|
||
LARGE_INTEGER TotalIdlePeriod;
|
||
|
||
//
|
||
// The device was inactive when it was accessed and will be
|
||
// reenumerated.
|
||
//
|
||
// In an attempt to increase the hit rate, the device timeout
|
||
// is recomputed.
|
||
//
|
||
// To compute the new timeout value, the total idle is determined
|
||
// for this device based on the time the last idle period was
|
||
// started (e.g. the last time the reference count for the device
|
||
// wnt to zero). If the idle period is greater than the maximum
|
||
// timeout period (e.g. 20 minutes), the device timeout period is
|
||
// reduced by half -- providing a timeout decay when the device
|
||
// is used very infrequently. If the idle period is less than the
|
||
// maximum timeout period but greater than the current timeout
|
||
// value for the device, the new timeout value for the device is
|
||
// set to the idle period.
|
||
//
|
||
|
||
//
|
||
// Compute the total idle period for this device.
|
||
//
|
||
|
||
TotalIdlePeriod.QuadPart =
|
||
KeQueryPerformanceCounter( NULL ).QuadPart -
|
||
DeviceReference->IdleStartTime.QuadPart;
|
||
|
||
//
|
||
// Watch for roll-over in the timer.
|
||
//
|
||
|
||
if (TotalIdlePeriod.QuadPart < 0) {
|
||
TotalIdlePeriod.QuadPart += MAXLONGLONG;
|
||
}
|
||
|
||
|
||
if (TotalIdlePeriod.QuadPart <
|
||
FdoExtension->MaximumTimeout.QuadPart) {
|
||
if (TotalIdlePeriod.QuadPart > DeviceReference->TimeoutPeriod.QuadPart) {
|
||
DeviceReference->TimeoutPeriod.QuadPart = TotalIdlePeriod.QuadPart;
|
||
}
|
||
} else {
|
||
//
|
||
// The device is not being used frequently enough -- reduce
|
||
// the timeout. The minimum timeout period is constant.
|
||
//
|
||
|
||
DeviceReference->TimeoutPeriod.QuadPart /= 2;
|
||
DeviceReference->TimeoutPeriod.QuadPart =
|
||
max(
|
||
DeviceReference->TimeoutPeriod.QuadPart,
|
||
FdoExtension->CounterFrequency.QuadPart *
|
||
SWEEPER_TIMER_FREQUENCY_IN_SECS
|
||
);
|
||
}
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE, ("device timeout period is %d ms",
|
||
KSCONVERT_PERFORMANCE_TIME(
|
||
FdoExtension->CounterFrequency.QuadPart,
|
||
DeviceReference->TimeoutPeriod ) / _100NS_IN_MS ) );
|
||
|
||
//
|
||
// The device has not been enumerated
|
||
//
|
||
Status =
|
||
CreatePdo(
|
||
FdoExtension,
|
||
DeviceReference,
|
||
&DeviceReference->PhysicalDeviceObject );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("IRP_MJ_CREATE: unable to create PDO (%8x)", Status) );
|
||
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
Irp->IoStatus.Status = Status;
|
||
} else {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("IRP_MJ_CREATE: created PDO (%8x)",
|
||
DeviceReference->PhysicalDeviceObject) );
|
||
|
||
//
|
||
// This IRP is not cancelable.
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
InsertTailList(
|
||
&DeviceReference->IoQueue,
|
||
&Irp->Tail.Overlay.ListEntry );
|
||
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Force a re-enumeration of the bus.
|
||
//
|
||
IoInvalidateDeviceRelations(
|
||
FdoExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
Status = STATUS_PENDING;
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// We have a PDO.
|
||
//
|
||
|
||
//
|
||
// If the device failed to start or failed during installation,
|
||
// complete this IRP as STATUS_OBJECT_NAME_NOT_FOUND.
|
||
//
|
||
|
||
if (DeviceReference->State == ReferenceFailedStart) {
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
Irp->IoStatus.Status = Status;
|
||
} else {
|
||
|
||
//
|
||
// If the device has not been started or has
|
||
// a pending removal IRP, queue the request, otherwise issue the
|
||
// reparse.
|
||
//
|
||
|
||
if ((DeviceReference->State < ReferenceStarted) ||
|
||
(DeviceReference->SweeperMarker == SweeperDeviceRemoved)) {
|
||
|
||
//
|
||
// Reset the device reference timeout.
|
||
//
|
||
DeviceReference->TimeoutRemaining =
|
||
DeviceReference->TimeoutPeriod;
|
||
|
||
//
|
||
// This IRP is not cancelable.
|
||
//
|
||
|
||
IoMarkIrpPending( Irp );
|
||
InsertTailList(
|
||
&DeviceReference->IoQueue,
|
||
&Irp->Tail.Overlay.ListEntry );
|
||
Status = STATUS_PENDING;
|
||
|
||
} else {
|
||
//
|
||
// The device has been created and a pending removal IRP is
|
||
// not emminent. Issue a reparse to the actual device name.
|
||
//
|
||
|
||
Status =
|
||
IssueReparseForIrp(
|
||
Irp,
|
||
DeviceReference );
|
||
|
||
}
|
||
}
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// The device reference string was not found, the object name was
|
||
// not valid.
|
||
//
|
||
|
||
Status =
|
||
Irp->IoStatus.Status =
|
||
STATUS_OBJECT_NAME_NOT_FOUND;
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenDeviceInterfacesKey(
|
||
OUT PHANDLE DeviceInterfacesKey,
|
||
IN PUNICODE_STRING BaseRegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Opens a handle to the supplied registry key that stores device references
|
||
for devices on the "software bus".
|
||
|
||
Arguments:
|
||
OUT PHANDLE DeviceInterfacesKey -
|
||
pointer to receive a handle to the registry key where device references
|
||
for the "software bus" are stored
|
||
|
||
|
||
IN PUNICODE_STRING BaseRegistryPath -
|
||
pointer to registry key path where device references for the "software
|
||
bus" are stored
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
BaseRegistryPath,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwCreateKey(
|
||
DeviceInterfacesKey,
|
||
KEY_ALL_ACCESS,
|
||
&ObjectAttributes,
|
||
0, // IN ULONG TitleIndex
|
||
NULL, // IN PUNICODE_STRING Class
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL ); // OUT PULONG Disposition
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("failed in OpenDeviceInterfacesKey(): %08x", Status) );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
QueryDeviceRelations(
|
||
IN PFDO_EXTENSION FdoExtension,
|
||
IN DEVICE_RELATION_TYPE RelationType,
|
||
OUT PDEVICE_RELATIONS *DeviceRelations
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Processes the PnP IRP_MN_QUERY_DEVICE_RELATIONS request.
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to the FDO device extension
|
||
|
||
IN DEVICE_RELATION_TYPE RelationType -
|
||
relation type, currently only BusRelations are supported
|
||
|
||
OUT PDEVICE_RELATIONS *DeviceRelations -
|
||
pointer to receive the child PDO list
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, STATUS_NOT_SUPPORTED if RelationType !=
|
||
BusRelations else an approprate error return.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG pdoCount;
|
||
PDEVICE_RELATIONS deviceRelations;
|
||
ULONG deviceRelationsSize;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
|
||
PAGED_CODE();
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("QueryDeviceRelations") );
|
||
|
||
switch (RelationType) {
|
||
|
||
case BusRelations:
|
||
|
||
ASSERT( FdoExtension->ExtensionType == ExtensionTypeFdo );
|
||
|
||
//
|
||
// First count the child PDOs
|
||
//
|
||
|
||
pdoCount = 0;
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
if ((DeviceReference->PhysicalDeviceObject) &&
|
||
(DeviceReference->SweeperMarker < SweeperDeviceRemoval)) {
|
||
pdoCount++;
|
||
}
|
||
}
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("%d active device%s",
|
||
pdoCount, ((pdoCount == 0) || (pdoCount > 1)) ? "s" : "") );
|
||
break;
|
||
|
||
case TargetDeviceRelation:
|
||
ASSERT( FdoExtension->ExtensionType == ExtensionTypePdo );
|
||
|
||
pdoCount = 1;
|
||
break;
|
||
|
||
default:
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("QueryDeviceRelations: RelationTType %d not handled", RelationType) );
|
||
return STATUS_NOT_SUPPORTED;
|
||
|
||
}
|
||
|
||
//
|
||
// Now allocate a chunk of memory big enough to hold the DEVICE_RELATIONS
|
||
// structure along with the array
|
||
//
|
||
|
||
deviceRelationsSize =
|
||
FIELD_OFFSET( DEVICE_RELATIONS, Objects ) +
|
||
pdoCount * sizeof( PDEVICE_OBJECT );
|
||
|
||
deviceRelations =
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
deviceRelationsSize,
|
||
POOLTAG_DEVICE_RELATIONS );
|
||
if (!deviceRelations) {
|
||
if (RelationType == BusRelations) {
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (RelationType == TargetDeviceRelation) {
|
||
PPDO_EXTENSION PdoExtension = (PPDO_EXTENSION) FdoExtension;
|
||
|
||
ObReferenceObject( PdoExtension->PhysicalDeviceObject );
|
||
deviceRelations->Objects[ 0 ] = PdoExtension->PhysicalDeviceObject;
|
||
} else {
|
||
//
|
||
// Walk the device references list again and
|
||
// build the DEVICE_RELATIONS structure.
|
||
//
|
||
|
||
for (pdoCount = 0, DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
if (DeviceReference->PhysicalDeviceObject) {
|
||
if (DeviceReference->SweeperMarker < SweeperDeviceRemoval) {
|
||
ObReferenceObject( DeviceReference->PhysicalDeviceObject );
|
||
deviceRelations->Objects[ pdoCount++ ] =
|
||
DeviceReference->PhysicalDeviceObject;
|
||
} else {
|
||
DeviceReference->SweeperMarker= SweeperDeviceRemoved;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
deviceRelations->Count = pdoCount;
|
||
*DeviceRelations = deviceRelations;
|
||
|
||
if (RelationType == BusRelations) {
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("QueryDeviceRelations, exit") );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
EnumerateRegistrySubKeys(
|
||
IN HANDLE ParentKey,
|
||
IN PWCHAR Path OPTIONAL,
|
||
IN PFNREGENUM_CALLBACK EnumCallback,
|
||
IN PVOID EnumContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Enumerates the registry keys and calls the callback with
|
||
|
||
|
||
Arguments:
|
||
IN HANDLE ParentKey -
|
||
handle to the parent registry key
|
||
|
||
IN PWCHAR Path OPTIONAL -
|
||
path to registry key relative to the parent
|
||
|
||
IN PFNREGENUM_CALLBACK EnumCallback -
|
||
enumeration callback function
|
||
|
||
IN PVOID EnumContext -
|
||
context passed to callback
|
||
|
||
Return:
|
||
STATUS_SUCCESS if successful, STATUS_INSUFFFICIENT resources if unable
|
||
to allocate pool memory otherwise the status return from the callback
|
||
function.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
HANDLE EnumPathKey;
|
||
KEY_FULL_INFORMATION FullKeyInformation;
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
PKEY_BASIC_INFORMATION KeyInformation;
|
||
ULONG Index, InformationSize, ReturnedSize;
|
||
UNICODE_STRING KeyName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Enumerate the reference strings associated with this device
|
||
// (the path is relative to the DeviceKey).
|
||
//
|
||
|
||
RtlInitUnicodeString( &KeyName, Path );
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&KeyName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ParentKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
if (!NT_SUCCESS( Status =
|
||
ZwOpenKey(
|
||
&EnumPathKey,
|
||
KEY_READ,
|
||
&ObjectAttributes ) )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("ZwOpenKey (%s) returned %x", Path, Status) );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Prepare to enumerate the subkeys.
|
||
//
|
||
|
||
KeyInformation = NULL;
|
||
if (!NT_SUCCESS( Status =
|
||
ZwQueryKey(
|
||
EnumPathKey,
|
||
KeyFullInformation,
|
||
&FullKeyInformation,
|
||
sizeof( FullKeyInformation ),
|
||
&ReturnedSize ) )) {
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("ZwQueryKey returned %x", Status) );
|
||
} else {
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("subkeys: %d", FullKeyInformation.SubKeys) );
|
||
|
||
InformationSize =
|
||
sizeof( KEY_BASIC_INFORMATION ) +
|
||
FullKeyInformation.MaxNameLen * sizeof( WCHAR ) +
|
||
sizeof( UNICODE_NULL );
|
||
|
||
KeyInformation =
|
||
(PKEY_BASIC_INFORMATION)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
InformationSize,
|
||
POOLTAG_KEY_INFORMATION );
|
||
}
|
||
|
||
if (NULL == KeyInformation) {
|
||
ZwClose( EnumPathKey );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Perform the enumeration.
|
||
//
|
||
|
||
for (Index =0; Index < FullKeyInformation.SubKeys; Index++) {
|
||
if (NT_SUCCESS( Status =
|
||
ZwEnumerateKey(
|
||
EnumPathKey,
|
||
Index,
|
||
KeyBasicInformation,
|
||
KeyInformation,
|
||
InformationSize,
|
||
&ReturnedSize ) )) {
|
||
|
||
//
|
||
// NULL terminate the key name.
|
||
//
|
||
|
||
KeyInformation->Name[ KeyInformation->NameLength / sizeof( WCHAR ) ] = UNICODE_NULL;
|
||
RtlInitUnicodeString(
|
||
&KeyName,
|
||
KeyInformation->Name );
|
||
|
||
//
|
||
// Call the callback.
|
||
//
|
||
|
||
Status =
|
||
EnumCallback( EnumPathKey, &KeyName, EnumContext );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
ExFreePool( KeyInformation );
|
||
ZwClose( EnumPathKey );
|
||
|
||
return Status ;
|
||
}
|
||
|
||
VOID
|
||
ClearDeviceReferenceMarks(
|
||
IN PFDO_EXTENSION FdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Walks the device reference list, clearing all reference marks.
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
|
||
PAGED_CODE();
|
||
|
||
DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
DeviceReference->Referenced = FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
RemoveDeviceAssociations(
|
||
IN PDEVICE_REFERENCE DeviceReference
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Removes the device association structures attached to the device
|
||
reference.
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Arguments:
|
||
IN PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference structure
|
||
|
||
Return:
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_ASSOCIATION DeviceAssociation, NextAssociation;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (DeviceAssociation =
|
||
(PDEVICE_ASSOCIATION) DeviceReference->DeviceAssociations.Flink;
|
||
DeviceAssociation !=
|
||
(PDEVICE_ASSOCIATION) &DeviceReference->DeviceAssociations;
|
||
DeviceAssociation = NextAssociation) {
|
||
|
||
NextAssociation =
|
||
(PDEVICE_ASSOCIATION) DeviceAssociation->ListEntry.Flink;
|
||
|
||
if (DeviceAssociation->linkName.Buffer) {
|
||
|
||
//
|
||
// Remove device interface association...
|
||
//
|
||
|
||
IoSetDeviceInterfaceState( &DeviceAssociation->linkName, FALSE);
|
||
|
||
//
|
||
// and free the symbolic link.
|
||
//
|
||
|
||
ExFreePool( DeviceAssociation->linkName.Buffer );
|
||
DeviceAssociation->linkName.Buffer = NULL;
|
||
}
|
||
|
||
//
|
||
// unlink and free the association structure
|
||
//
|
||
|
||
RemoveEntryList( &DeviceAssociation->ListEntry );
|
||
ExFreePool( DeviceAssociation );
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
RemoveUnreferencedDevices(
|
||
IN PFDO_EXTENSION FdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Removes device references from the list which are not marked.
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to the FDO device extension
|
||
|
||
Return:
|
||
No return value.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_REFERENCE DeviceReference, NextReference;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Scan the device reference list looking for unmarked entries
|
||
//
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = NextReference) {
|
||
|
||
NextReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink;
|
||
|
||
if (!DeviceReference->Referenced) {
|
||
PIRP Irp;
|
||
|
||
//
|
||
// Remove the links, this will make the device invisible from
|
||
// user mode.
|
||
//
|
||
RemoveDeviceAssociations( DeviceReference );
|
||
|
||
//
|
||
// Any IRP (IRP_MJ_CREATE) on the list is completed with
|
||
// STATUS_OBJECT_NAME_NOT_FOUND as this device referenece is
|
||
// no longer active.
|
||
//
|
||
|
||
while (!IsListEmpty( &DeviceReference->IoQueue )) {
|
||
PLIST_ENTRY ListEntry;
|
||
|
||
ListEntry = RemoveHeadList( &DeviceReference->IoQueue );
|
||
Irp =
|
||
CONTAINING_RECORD( ListEntry, IRP, Tail.Overlay.ListEntry );
|
||
Irp->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
}
|
||
|
||
//
|
||
// If there is an associated device object, this
|
||
// reference structure can not be removed until
|
||
// all references to the device object are released.
|
||
//
|
||
|
||
if (!DeviceReference->PhysicalDeviceObject) {
|
||
|
||
//
|
||
// If the bus reference string was allocated, clean it up.
|
||
//
|
||
|
||
if (DeviceReference->BusReferenceString) {
|
||
ExFreePool( DeviceReference->BusReferenceString );
|
||
DeviceReference->BusReferenceString = NULL;
|
||
}
|
||
|
||
if (DeviceReference->DeviceGuidString) {
|
||
ExFreePool( DeviceReference->DeviceGuidString );
|
||
DeviceReference->DeviceGuidString = NULL;
|
||
}
|
||
|
||
//
|
||
// There is no physical device object, it is safe
|
||
// to remove the device reference.
|
||
//
|
||
RemoveEntryList( &DeviceReference->ListEntry );
|
||
ExFreePool( DeviceReference );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RegisterDeviceAssociation(
|
||
IN PFDO_EXTENSION FdoExtension,
|
||
IN PDEVICE_REFERENCE DeviceReference,
|
||
IN PDEVICE_ASSOCIATION DeviceAssociation
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Registers the device interface association with Plug-N-Play
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to the FDO extension
|
||
|
||
IN PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference structure
|
||
|
||
IN PDEVICE_ASSOCIATION DeviceAssociation -
|
||
pointer to the device association structure
|
||
|
||
Return:
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING BusReferenceString;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Register the device interface association
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&BusReferenceString,
|
||
DeviceReference->BusReferenceString );
|
||
|
||
Status =
|
||
IoRegisterDeviceInterface(
|
||
FdoExtension->PhysicalDeviceObject,
|
||
&DeviceAssociation->InterfaceId,
|
||
&BusReferenceString,
|
||
&DeviceAssociation->linkName );
|
||
|
||
//
|
||
// Set up the device interface association (e.g. symbolic link).
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("linkName = %S", DeviceAssociation->linkName.Buffer) );
|
||
|
||
Status =
|
||
IoSetDeviceInterfaceState(
|
||
&DeviceAssociation->linkName,
|
||
TRUE );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
if (DeviceAssociation->linkName.Buffer) {
|
||
ExFreePool( DeviceAssociation->linkName.Buffer );
|
||
DeviceAssociation->linkName.Buffer = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateDeviceAssociation(
|
||
IN HANDLE InterfaceKey,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN PVOID EnumContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Creates a device association structure, this is stored per
|
||
InterfaceKey/Reference String combination -- this function is
|
||
called as a result of enumeration of the interface branch of a
|
||
device.
|
||
|
||
Arguments:
|
||
IN HANDLE InterfaceKey -
|
||
Handle to the interface branch in the registry
|
||
|
||
IN PUNICODE_STRING KeyName -
|
||
Enumerated key name
|
||
|
||
IN PVOID EnumContext -
|
||
Context pointer
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error return
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
GUID InterfaceId;
|
||
PCREATE_ASSOCIATION_CONTEXT CreateAssociationContext = EnumContext;
|
||
PDEVICE_ASSOCIATION DeviceAssociation;
|
||
|
||
PAGED_CODE();
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("CreateDeviceAssociation") );
|
||
|
||
//
|
||
// Convert to GUID and scan for this interface ID
|
||
//
|
||
|
||
RtlGUIDFromString( KeyName, &InterfaceId );
|
||
|
||
for (DeviceAssociation =
|
||
(PDEVICE_ASSOCIATION) CreateAssociationContext->DeviceReference->DeviceAssociations.Flink;
|
||
DeviceAssociation !=
|
||
(PDEVICE_ASSOCIATION) &CreateAssociationContext->DeviceReference->DeviceAssociations;
|
||
DeviceAssociation =
|
||
(PDEVICE_ASSOCIATION) DeviceAssociation->ListEntry.Flink) {
|
||
if (IsEqualGUIDAligned( &DeviceAssociation->InterfaceId, &InterfaceId )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("device association exists, return STATUS_SUCCESS") );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
|
||
DeviceAssociation =
|
||
(PDEVICE_ASSOCIATION)
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof( DEVICE_ASSOCIATION ),
|
||
POOLTAG_DEVICE_ASSOCIATION );
|
||
|
||
if (NULL == DeviceAssociation) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("out of memory while allocating device association") );
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory( DeviceAssociation, sizeof( DEVICE_ASSOCIATION ) );
|
||
DeviceAssociation->InterfaceId = InterfaceId;
|
||
|
||
//
|
||
// Register the interface association
|
||
//
|
||
|
||
Status =
|
||
RegisterDeviceAssociation(
|
||
CreateAssociationContext->FdoExtension,
|
||
CreateAssociationContext->DeviceReference,
|
||
DeviceAssociation );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
//
|
||
// everything was successful with this association,
|
||
// add to the list.
|
||
//
|
||
|
||
InsertTailList(
|
||
&CreateAssociationContext->DeviceReference->DeviceAssociations,
|
||
&DeviceAssociation->ListEntry );
|
||
} else {
|
||
//
|
||
// A failure occured, remove this association structure.
|
||
//
|
||
|
||
ExFreePool( DeviceAssociation );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateDeviceReference(
|
||
IN HANDLE DeviceReferenceKey,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN PVOID EnumContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
If the device reference does not already exist on this bus, creates a
|
||
device reference structure and initializes it with the given device
|
||
GUID and reference string. If the reference already exists, the reference
|
||
is marked.
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Arguments:
|
||
IN HANDLE DeviceReferenceKey -
|
||
handle to the parent key
|
||
(HKLM\CCS\Services\swenum\devices\{device-guid})
|
||
|
||
IN PUNICODE_STRING KeyName -
|
||
string containing key name (reference string)
|
||
|
||
IN PVOID EnumContext -
|
||
enumeration context (PCREATE_DEVICE_ASSOCATION)
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate STATUS return.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Created;
|
||
NTSTATUS Status;
|
||
PCREATE_ASSOCIATION_CONTEXT CreateAssociationContext = EnumContext;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PFDO_EXTENSION FdoExtension;
|
||
PWCHAR BusReferenceString;
|
||
ULONG BusReferenceStringSize;
|
||
ULONG InformationSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("CreateDeviceReference") );
|
||
|
||
FdoExtension = CreateAssociationContext->FdoExtension;
|
||
|
||
//
|
||
// Scan the device reference list looking for a matching on
|
||
// the device-guid reference-string pair.
|
||
//
|
||
BusReferenceStringSize =
|
||
(2 + wcslen( CreateAssociationContext->DeviceGuidString->Buffer ) +
|
||
wcslen( KeyName->Buffer )) * sizeof( WCHAR );
|
||
|
||
BusReferenceString =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
BusReferenceStringSize,
|
||
POOLTAG_DEVICE_BUSREFERENCE );
|
||
|
||
if (NULL == BusReferenceString) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// The bus reference string is in the following format:
|
||
// {device-guid}&{reference-string}
|
||
//
|
||
|
||
if (FAILED( StringCbPrintf(
|
||
BusReferenceString,
|
||
BusReferenceStringSize,
|
||
BusReferenceStringFormat,
|
||
CreateAssociationContext->DeviceGuidString->Buffer,
|
||
KeyName->Buffer ) )) {
|
||
ExFreePool( BusReferenceString );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("CreateDeviceReference() scanning for: %S", BusReferenceString) );
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
|
||
if ((DeviceReference->BusReferenceString) &&
|
||
(0 ==
|
||
_wcsicmp(
|
||
BusReferenceString,
|
||
DeviceReference->BusReferenceString ))) {
|
||
//
|
||
// Already referenced this device, just set the flag.
|
||
//
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("marking device reference" ) );
|
||
DeviceReference->Referenced = TRUE;
|
||
Created = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the device reference was not found, create a new one.
|
||
//
|
||
|
||
if (DeviceReference ==
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList) {
|
||
|
||
Created = TRUE;
|
||
|
||
//
|
||
// Allocate the device reference structure.
|
||
//
|
||
|
||
InformationSize =
|
||
FIELD_OFFSET( DEVICE_REFERENCE, DeviceReferenceString ) +
|
||
KeyName->Length + sizeof( UNICODE_NULL );
|
||
|
||
//
|
||
// Allocate the device reference structure, copy the GUID string
|
||
// and initialize other members.
|
||
//
|
||
|
||
if (NULL ==
|
||
(DeviceReference =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
InformationSize,
|
||
POOLTAG_DEVICE_REFERENCE ))) {
|
||
ExFreePool( BusReferenceString );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
DeviceReference,
|
||
FIELD_OFFSET( DEVICE_REFERENCE, DeviceReferenceString ) );
|
||
RtlCopyMemory(
|
||
DeviceReference->DeviceReferenceString,
|
||
KeyName->Buffer,
|
||
KeyName->Length + sizeof( UNICODE_NULL ) );
|
||
|
||
//
|
||
// Allocate the storage for the device GUID string (used for the Bus ID)
|
||
//
|
||
|
||
if (NULL ==
|
||
(DeviceReference->DeviceGuidString =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
CreateAssociationContext->DeviceGuidString->Length +
|
||
sizeof( UNICODE_NULL ),
|
||
POOLTAG_DEVICE_ID ))) {
|
||
ExFreePool( DeviceReference );
|
||
ExFreePool( BusReferenceString );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
RtlCopyMemory(
|
||
DeviceReference->DeviceGuidString,
|
||
CreateAssociationContext->DeviceGuidString->Buffer,
|
||
CreateAssociationContext->DeviceGuidString->Length +
|
||
sizeof( UNICODE_NULL ) );
|
||
RtlGUIDFromString(
|
||
CreateAssociationContext->DeviceGuidString,
|
||
&DeviceReference->DeviceId );
|
||
}
|
||
|
||
InitializeListHead( &DeviceReference->IoQueue );
|
||
DeviceReference->Referenced = TRUE;
|
||
|
||
//
|
||
// Initialize the timeout period and set the initial idle start time.
|
||
//
|
||
|
||
DeviceReference->TimeoutPeriod.QuadPart =
|
||
SWEEPER_TIMER_FREQUENCY_IN_SECS * FdoExtension->CounterFrequency.QuadPart;
|
||
|
||
DeviceReference->IdleStartTime = KeQueryPerformanceCounter( NULL );
|
||
|
||
//
|
||
// Store the pointer to the bus reference string.
|
||
//
|
||
|
||
DeviceReference->BusReferenceString = BusReferenceString;
|
||
|
||
//
|
||
// Prepare the list of device associations
|
||
//
|
||
|
||
InitializeListHead( &DeviceReference->DeviceAssociations );
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("created device reference: %S",
|
||
DeviceReference->BusReferenceString) );
|
||
} else {
|
||
//
|
||
// Free the BusReferenceString, it is no longer needed because
|
||
// we found a match.
|
||
//
|
||
|
||
ExFreePool( BusReferenceString );
|
||
BusReferenceString = NULL;
|
||
}
|
||
|
||
//
|
||
// Enumerate the device interface guids associated with this device.
|
||
//
|
||
|
||
CreateAssociationContext->DeviceReference = DeviceReference;
|
||
CreateAssociationContext->DeviceReferenceString = KeyName;
|
||
|
||
Status =
|
||
EnumerateRegistrySubKeys(
|
||
DeviceReferenceKey,
|
||
KeyName->Buffer,
|
||
CreateDeviceAssociation,
|
||
CreateAssociationContext );
|
||
|
||
//
|
||
// If there are no associations, we have an invalid state.
|
||
//
|
||
|
||
if (IsListEmpty( &DeviceReference->DeviceAssociations )) {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("no associations, removing the device reference") );
|
||
if (DeviceReference->DeviceGuidString) {
|
||
ExFreePool( DeviceReference->DeviceGuidString );
|
||
DeviceReference->DeviceGuidString = NULL;
|
||
}
|
||
if (DeviceReference->BusReferenceString) {
|
||
ExFreePool( DeviceReference->BusReferenceString );
|
||
DeviceReference->BusReferenceString = NULL;
|
||
}
|
||
ExFreePool( DeviceReference );
|
||
Status = STATUS_INVALID_DEVICE_STATE;
|
||
|
||
} else if (Created) {
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
//
|
||
// Add this device reference to the list.
|
||
//
|
||
|
||
InsertTailList(
|
||
&FdoExtension->DeviceReferenceList,
|
||
&DeviceReference->ListEntry );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Walk the list and remove any device association stragglers.
|
||
//
|
||
|
||
RemoveDeviceAssociations( DeviceReference );
|
||
if (DeviceReference->BusReferenceString) {
|
||
ExFreePool( DeviceReference->BusReferenceString );
|
||
DeviceReference->BusReferenceString = NULL;
|
||
}
|
||
|
||
if (DeviceReference->DeviceGuidString) {
|
||
ExFreePool( DeviceReference->DeviceGuidString );
|
||
DeviceReference->DeviceGuidString = NULL;
|
||
}
|
||
ExFreePool( DeviceReference );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
EnumerateDeviceReferences(
|
||
IN HANDLE DeviceListKey,
|
||
IN PUNICODE_STRING KeyName,
|
||
IN PVOID EnumContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
If the device reference does not already exist on this bus, creates a
|
||
device reference structure and initializes it with the given device
|
||
GUID. If the reference already exists, the reference is marked.
|
||
|
||
N.B.:
|
||
This routine requires that the DeviceListMutex has been acquired.
|
||
|
||
Arguments:
|
||
IN HANDLE DeviceListKey -
|
||
handle to the parent key (HKLM\CCS\Services\swenum\devices)
|
||
|
||
IN PUNICODE_STRING KeyName -
|
||
string containing key name (device GUID string)
|
||
|
||
IN PVOID EnumContext -
|
||
enumeration context (FdoExtension)
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate STATUS return.
|
||
|
||
--*/
|
||
{
|
||
CREATE_ASSOCIATION_CONTEXT CreateAssociationContext;
|
||
|
||
//
|
||
// The association context is built upon during the traversal
|
||
// of the devices listed in the registry. The initial state
|
||
// includes this FDO and the device GUID string.
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
&CreateAssociationContext,
|
||
sizeof( CREATE_ASSOCIATION_CONTEXT ) );
|
||
|
||
CreateAssociationContext.FdoExtension = EnumContext;
|
||
CreateAssociationContext.DeviceGuidString = KeyName;
|
||
|
||
//
|
||
// Enumerate the device guids from the registry.
|
||
//
|
||
|
||
return
|
||
EnumerateRegistrySubKeys(
|
||
DeviceListKey,
|
||
KeyName->Buffer,
|
||
CreateDeviceReference,
|
||
&CreateAssociationContext );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ScanBus(
|
||
IN PDEVICE_OBJECT FunctionalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Scans the "software bus" looking for new or removed entries in
|
||
the registry.
|
||
|
||
NOTE:
|
||
|
||
This function is always called in the context of the system
|
||
process so that registry handles are tucked away safely from
|
||
rogue user-mode applications.
|
||
|
||
From user-mode, the IOCTL that initiates an installation, removal, or
|
||
scan always results in a worker in the system process to do the actual
|
||
work.
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to the FDO device extension
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE DeviceInterfacesKey;
|
||
NTSTATUS Status;
|
||
PFDO_EXTENSION FdoExtension;
|
||
|
||
PAGED_CODE();
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("ScanBus") );
|
||
|
||
FdoExtension = *(PFDO_EXTENSION *) FunctionalDeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Open driver registry path
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status =
|
||
OpenDeviceInterfacesKey(
|
||
&DeviceInterfacesKey,
|
||
&FdoExtension->BaseRegistryPath ) )) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Clear reference marks in the reference list.
|
||
//
|
||
|
||
ClearDeviceReferenceMarks( FdoExtension );
|
||
|
||
//
|
||
// Enumerate the device guids from the registry.
|
||
//
|
||
|
||
Status =
|
||
EnumerateRegistrySubKeys(
|
||
DeviceInterfacesKey,
|
||
NULL,
|
||
EnumerateDeviceReferences,
|
||
FdoExtension );
|
||
|
||
//
|
||
// This removes the device reference structure for each
|
||
// unreferenced device.
|
||
//
|
||
|
||
RemoveUnreferencedDevices( FdoExtension );
|
||
|
||
ZwClose( DeviceInterfacesKey );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreatePdo(
|
||
IN PFDO_EXTENSION FdoExtension,
|
||
IN PDEVICE_REFERENCE DeviceReference,
|
||
OUT PDEVICE_OBJECT *DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Creates a PDO for the given device reference.
|
||
|
||
Arguments:
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to FDO device extension
|
||
|
||
IN PDEVICE_REFERENCE DeviceReference -
|
||
pointer to the device reference structure
|
||
|
||
OUT PDEVICE_OBJECT *DeviceObject -
|
||
pointer to receive the device object
|
||
|
||
Return:
|
||
STATUS_SUCCESS else an appropriate error return
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PDRIVER_OBJECT DriverObject;
|
||
PDEVICE_OBJECT FunctionalDeviceObject;
|
||
PDEVICE_OBJECT PhysicalDeviceObject;
|
||
PPDO_EXTENSION PdoExtension;
|
||
WCHAR DeviceName[ 64 ];
|
||
UNICODE_STRING DeviceNameString;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We've been asked to create a new PDO a device. First get
|
||
// a pointer to our driver object.
|
||
//
|
||
|
||
FunctionalDeviceObject = FdoExtension->FunctionalDeviceObject;
|
||
DriverObject = FunctionalDeviceObject->DriverObject;
|
||
|
||
DeviceReference->State = ReferenceAdded;
|
||
|
||
if (FAILED( StringCbPrintf(
|
||
DeviceName,
|
||
sizeof( DeviceName ),
|
||
DeviceReferencePrefix,
|
||
InterlockedIncrement( &UniqueId ) ) )) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlInitUnicodeString( &DeviceNameString, DeviceName );
|
||
|
||
//
|
||
// Create the physical device object for this device.
|
||
//
|
||
|
||
Status = IoCreateDevice(
|
||
DriverObject, // our driver object
|
||
sizeof( PPDO_EXTENSION ), // size of our extension,
|
||
&DeviceNameString, // the name of our PDO
|
||
FILE_DEVICE_UNKNOWN, // device type
|
||
0, // device characteristics
|
||
FALSE, // not exclusive
|
||
&PhysicalDeviceObject // store new device object here
|
||
);
|
||
|
||
if( !NT_SUCCESS( Status )){
|
||
|
||
return Status;
|
||
}
|
||
|
||
PdoExtension =
|
||
ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof( PDO_EXTENSION ),
|
||
POOLTAG_DEVICE_PDOEXTENSION );
|
||
|
||
if (NULL == PdoExtension) {
|
||
IoDeleteDevice( PhysicalDeviceObject );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
*(PPDO_EXTENSION *) PhysicalDeviceObject->DeviceExtension = PdoExtension;
|
||
RtlZeroMemory( PdoExtension, sizeof( PDO_EXTENSION ) );
|
||
|
||
//
|
||
// We have our physical device object, initialize it.
|
||
//
|
||
|
||
PdoExtension->ExtensionType = ExtensionTypePdo;
|
||
PdoExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
||
PdoExtension->DeviceReference = DeviceReference;
|
||
PdoExtension->BusDeviceExtension = FdoExtension;
|
||
|
||
//
|
||
// This device reference is now active.
|
||
//
|
||
|
||
DeviceReference->SweeperMarker = SweeperDeviceActive;
|
||
|
||
//
|
||
// Short initial timeout period, waiting for device load.
|
||
//
|
||
DeviceReference->IdleStartTime = KeQueryPerformanceCounter( NULL );
|
||
#if (DEBUG_LOAD_TIME)
|
||
DeviceReference->LoadTime = DeviceReference->IdleStartTime;
|
||
#endif
|
||
DeviceReference->TimeoutRemaining.QuadPart =
|
||
FdoExtension->CounterFrequency.QuadPart *
|
||
SWEEPER_TIMER_FREQUENCY_IN_SECS * 2L;
|
||
|
||
//
|
||
// Clear the device initializing flag.
|
||
//
|
||
|
||
PhysicalDeviceObject->Flags |= DO_POWER_PAGABLE;
|
||
PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
//
|
||
// Try to remove this PDO later if there is no response
|
||
// from the device.
|
||
//
|
||
|
||
if (!InterlockedExchange( &FdoExtension->TimerScheduled, TRUE )) {
|
||
|
||
LARGE_INTEGER Freq;
|
||
|
||
Freq.QuadPart = SWEEPER_TIMER_FREQUENCY;
|
||
KeSetTimer(
|
||
&FdoExtension->SweeperTimer,
|
||
Freq,
|
||
&FdoExtension->SweeperDpc );
|
||
}
|
||
|
||
*DeviceObject = PhysicalDeviceObject;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
QueryId(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN BUS_QUERY_ID_TYPE IdType,
|
||
IN OUT PWSTR *BusQueryId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Processes the PnP IRP_MN_QUERY_ID for the PDO.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension -
|
||
pointer to the PDO device extension
|
||
|
||
IN BUS_QUERY_ID_TYPE IdType -
|
||
query ID type
|
||
|
||
IN OUT PWSTR *BusQueryId -
|
||
pointer to receive the bus query string
|
||
|
||
Return:
|
||
STATUS_SUCCESS, STATUS_INSUFFICIENT_RESOURCES if pool allocation failure
|
||
otherwise STATUS_NOT_SUPPORTED if an invalid ID type is provided.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PWSTR IdString;
|
||
|
||
PAGED_CODE();
|
||
|
||
switch( IdType ) {
|
||
|
||
case BusQueryHardwareIDs:
|
||
case BusQueryDeviceID:
|
||
|
||
//
|
||
// Caller wants the bus ID of this device.
|
||
//
|
||
|
||
//
|
||
// Note that the returned string is a double NULL-terminated multi-sz.
|
||
// Plug and Play only requires this for BusQueryHardwareIDs (also
|
||
// BusQueryCompatibleIDs, which we do not support) but having an extra
|
||
// NULL-terminating character for BusQueryDeviceID doesn't hurt.
|
||
//
|
||
|
||
IdString = BuildBusId( PdoExtension );
|
||
break;
|
||
|
||
case BusQueryInstanceID:
|
||
|
||
//
|
||
// Caller wants the instance ID of this device.
|
||
//
|
||
|
||
IdString = BuildInstanceId( PdoExtension );
|
||
break;
|
||
|
||
default:
|
||
|
||
return STATUS_NOT_SUPPORTED;
|
||
|
||
}
|
||
|
||
if (IdString != NULL) {
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("QueryId returns: %S", IdString) );
|
||
*BusQueryId = IdString;
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("QueryId returning failure.") );
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
PWSTR
|
||
BuildBusId(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Builds the bus identifier string given the PDO extension.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
NULL or a pointer to the bus ID string.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWSTR strId;
|
||
ULONG length;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Allocate the ID string. The initial count of two includes the separator
|
||
// and the trailing UNICODE_NULL.
|
||
//
|
||
|
||
length =
|
||
(2 + wcslen( PdoExtension->DeviceReference->DeviceGuidString ) +
|
||
wcslen( PdoExtension->BusDeviceExtension->BusPrefix )) * sizeof( WCHAR ) +
|
||
sizeof( UNICODE_NULL );
|
||
strId =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
length,
|
||
POOLTAG_DEVICE_BUSID );
|
||
|
||
if ( strId != NULL ) {
|
||
|
||
//
|
||
// Form the multi-sz string and return it.
|
||
//
|
||
|
||
if (FAILED( StringCbPrintf(
|
||
strId,
|
||
length,
|
||
BusIdFormat,
|
||
PdoExtension->BusDeviceExtension->BusPrefix,
|
||
PdoExtension->DeviceReference->DeviceGuidString,
|
||
L'\0' ) )) {
|
||
ExFreePool( strId );
|
||
strId = NULL;
|
||
}
|
||
}
|
||
|
||
return strId;
|
||
}
|
||
|
||
|
||
PWSTR
|
||
BuildInstanceId(
|
||
IN PPDO_EXTENSION PdoExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Builds the instance identifier string given the PDO extension.
|
||
|
||
Arguments:
|
||
IN PPDO_EXTENSION PdoExtension
|
||
pointer to the PDO extension
|
||
|
||
Return:
|
||
NULL or a pointer to the instance ID string.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWSTR strId;
|
||
ULONG length;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Allocate the ID string.
|
||
//
|
||
|
||
length =
|
||
(wcslen( PdoExtension->DeviceReference->DeviceReferenceString ) *
|
||
sizeof( WCHAR )) +
|
||
sizeof( UNICODE_NULL );
|
||
|
||
strId =
|
||
ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
length,
|
||
POOLTAG_DEVICE_INSTANCEID );
|
||
|
||
if ( strId != NULL ) {
|
||
|
||
//
|
||
// Form the string and return it.
|
||
//
|
||
|
||
if (FAILED( StringCbCopy(
|
||
strId,
|
||
length,
|
||
PdoExtension->DeviceReference->DeviceReferenceString ) )) {
|
||
ExFreePool( strId );
|
||
strId = NULL;
|
||
}
|
||
}
|
||
|
||
return strId;
|
||
}
|
||
|
||
|
||
VOID
|
||
KspInstallBusEnumInterface(
|
||
IN PWORKER_CONTEXT WorkerContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This is the internal routine which does the actual work of modifying
|
||
the registry and enumerating the new child interface.
|
||
|
||
NOTE:
|
||
|
||
This function is always called in the context of the system
|
||
process so that registry handles are tucked away safely from
|
||
rogue user-mode applications.
|
||
|
||
Arguments:
|
||
IN PWORKER_CONTEXT WorkerContext -
|
||
contains a pointer to the context for the worker
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PFDO_EXTENSION FdoExtension;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PIRP Irp;
|
||
PDEVICE_REFERENCE DeviceReference;
|
||
PSWENUM_INSTALL_INTERFACE SwEnumInstallInterface;
|
||
NTSTATUS Status;
|
||
ULONG NullLocation;
|
||
|
||
Irp = WorkerContext->Irp;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof( SWENUM_INSTALL_INTERFACE )) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
FdoExtension = *(PFDO_EXTENSION *) irpSp->DeviceObject->DeviceExtension;
|
||
SwEnumInstallInterface =
|
||
(PSWENUM_INSTALL_INTERFACE) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Make sure that the string is UNICODE_NULL terminated. Note that the
|
||
// first two members of this structure are GUIDs and therefore WCHAR
|
||
// aligned.
|
||
//
|
||
|
||
//
|
||
// N.B.
|
||
//
|
||
// The ReferenceString member of SWENUM_INSTALL_INTERFACE is
|
||
// defined as WCHAR[1]. There is always room for the UNICODE_NULL.
|
||
//
|
||
|
||
NullLocation =
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength >> 1;
|
||
((PWCHAR) Irp->AssociatedIrp.SystemBuffer)[ NullLocation - 1 ] =
|
||
UNICODE_NULL;
|
||
|
||
//
|
||
// Take the list mutex
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
//
|
||
// Install the interface
|
||
//
|
||
Status =
|
||
InstallInterface(
|
||
SwEnumInstallInterface,
|
||
&FdoExtension->BaseRegistryPath );
|
||
if (NT_SUCCESS( Status )) {
|
||
//
|
||
// If successful, force the re-enumeration of the bus.
|
||
//
|
||
ScanBus( irpSp->DeviceObject );
|
||
|
||
//
|
||
// Walk the device reference scanning for our device.
|
||
//
|
||
|
||
Status = STATUS_NOT_FOUND;
|
||
|
||
for (DeviceReference =
|
||
(PDEVICE_REFERENCE) FdoExtension->DeviceReferenceList.Flink;
|
||
DeviceReference !=
|
||
(PDEVICE_REFERENCE) &FdoExtension->DeviceReferenceList;
|
||
DeviceReference = (PDEVICE_REFERENCE) DeviceReference->ListEntry.Flink) {
|
||
|
||
//
|
||
// Search for the new device reference.
|
||
//
|
||
|
||
if (IsEqualGUIDAligned(
|
||
&DeviceReference->DeviceId,
|
||
&SwEnumInstallInterface->DeviceId )) {
|
||
if (DeviceReference->BusReferenceString &&
|
||
(0 ==
|
||
_wcsicmp(
|
||
DeviceReference->DeviceReferenceString,
|
||
SwEnumInstallInterface->ReferenceString ))) {
|
||
|
||
//
|
||
// Found the reference.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// If the PDO does not already exist, then create it and mark
|
||
// it as "FailedInstall". This prevents other creates from
|
||
// blocking on this device until we actually complete the
|
||
// installation operation.
|
||
//
|
||
|
||
if (!DeviceReference->PhysicalDeviceObject) {
|
||
|
||
//
|
||
// The device has not been instantiated.
|
||
//
|
||
Status =
|
||
CreatePdo(
|
||
FdoExtension,
|
||
DeviceReference,
|
||
&DeviceReference->PhysicalDeviceObject );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("KsInstallBusEnumInterface: unable to create PDO (%8x)", Status) );
|
||
|
||
} else {
|
||
_DbgPrintF(
|
||
DEBUGLVL_BLAB,
|
||
("KsInstallBusEnumInterface: created PDO (%8x)",
|
||
DeviceReference->PhysicalDeviceObject) );
|
||
}
|
||
}
|
||
|
||
}
|
||
IoInvalidateDeviceRelations(
|
||
FdoExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
}
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
WorkerContext->Status = Status;
|
||
KeSetEvent( &WorkerContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
||
}
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsInstallBusEnumInterface(
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Services an I/O request for the parent device to install an interface
|
||
on the bus. The Irp->AssociatedIrp.SystemBuffer is assumed to have a
|
||
SWENUM_INSTALL_INTERFACE structure:
|
||
|
||
typedef struct _SWENUM_INSTALL_INTERFACE {
|
||
GUID DeviceId;
|
||
GUID InterfaceId;
|
||
WCHAR ReferenceString[1];
|
||
|
||
} SWENUM_INSTALL_INTERFACE, *PSWENUM_INSTALL_INTERFACE;
|
||
|
||
|
||
where the DeviceId, InterfaceId and ReferenceString specify the specific
|
||
device and interface with which to access this new interface. When the
|
||
interface as registered with Plug and Play for the interface GUID and
|
||
associated reference string is accessed the first time via IRP_MJ_CREATE,
|
||
the device will be enumerated using the format of
|
||
bus-identifier-prefix\device-id-GUID-string.
|
||
|
||
Arguments:
|
||
IN PIRP Irp -
|
||
pointer to I/O request containing SWENUM_INSTALL_INTERFACE structure
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code
|
||
|
||
--*/
|
||
|
||
{
|
||
WORK_QUEUE_ITEM InstallWorker;
|
||
WORKER_CONTEXT WorkerContext;
|
||
|
||
//
|
||
// Do the processing of the installation in a worker item so that
|
||
// registry handles are managed in the context of the system process.
|
||
//
|
||
|
||
#if !defined( WIN9X_KS )
|
||
if (!SeSinglePrivilegeCheck(
|
||
RtlConvertLongToLuid( SE_LOAD_DRIVER_PRIVILEGE ),
|
||
ExGetPreviousMode() )) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
#endif
|
||
|
||
KeInitializeEvent(
|
||
&WorkerContext.CompletionEvent, NotificationEvent, FALSE );
|
||
WorkerContext.Irp = Irp;
|
||
ExInitializeWorkItem(
|
||
&InstallWorker, KspInstallBusEnumInterface, &WorkerContext );
|
||
ExQueueWorkItem( &InstallWorker, DelayedWorkQueue );
|
||
KeWaitForSingleObject(
|
||
&WorkerContext.CompletionEvent, Executive, KernelMode, FALSE, NULL );
|
||
|
||
return WorkerContext.Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
InstallInterface(
|
||
IN PSWENUM_INSTALL_INTERFACE Interface,
|
||
IN PUNICODE_STRING BaseRegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Creates the registry key for the interface in the SWENUM\Devices.
|
||
|
||
NOTE:
|
||
|
||
This function is always called in the context of the system
|
||
process so that registry handles are tucked away safely from
|
||
rogue user-mode applications.
|
||
|
||
From user-mode, the IOCTL that initiates an installation, removal, or
|
||
scan always results in a worker in the system process to do the actual
|
||
work.
|
||
|
||
Arguments:
|
||
IN PSWENUM_INSTALL_INTERFACE Interface -
|
||
|
||
IN PUNICODE_STRING BaseRegistryPath -
|
||
Return:
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE DeviceIdKey, DeviceInterfacesKey,
|
||
InterfaceKey, ReferenceStringKey;
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
UNICODE_STRING DeviceIdString, InterfaceIdString,
|
||
ReferenceString;
|
||
PWCHAR Ptr;
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("InstallInterface") );
|
||
|
||
//
|
||
// Validate interface reference string
|
||
//
|
||
|
||
if (Ptr = Interface->ReferenceString) {
|
||
for (; *Ptr; *Ptr++) {
|
||
if ((*Ptr <= L' ') || (*Ptr > (WCHAR)0x7F) || (*Ptr == L',') || (*Ptr == L'\\') || (*Ptr == L'/')) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Open driver registry path
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status =
|
||
OpenDeviceInterfacesKey(
|
||
&DeviceInterfacesKey,
|
||
BaseRegistryPath ) )) {
|
||
return Status;
|
||
}
|
||
|
||
Status =
|
||
RtlStringFromGUID(
|
||
&Interface->DeviceId,
|
||
&DeviceIdString );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to create device ID string: %08x", Status) );
|
||
ZwClose( DeviceInterfacesKey );
|
||
return Status;
|
||
}
|
||
|
||
Status =
|
||
RtlStringFromGUID(
|
||
&Interface->InterfaceId,
|
||
&InterfaceIdString );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to create interface ID string: %08x", Status) );
|
||
RtlFreeUnicodeString( &DeviceIdString );
|
||
ZwClose( DeviceInterfacesKey );
|
||
return Status;
|
||
}
|
||
|
||
DeviceIdKey =
|
||
ReferenceStringKey =
|
||
InterfaceKey =
|
||
INVALID_HANDLE_VALUE;
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&DeviceIdString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
DeviceInterfacesKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwCreateKey(
|
||
&DeviceIdKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes,
|
||
0, // IN ULONG TitleIndex
|
||
NULL, // IN PUNICODE_STRING Class
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL ); // OUT PULONG Disposition
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
RtlInitUnicodeString(
|
||
&ReferenceString,
|
||
Interface->ReferenceString );
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&ReferenceString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
DeviceIdKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwCreateKey(
|
||
&ReferenceStringKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes,
|
||
0, // IN ULONG TitleIndex
|
||
NULL, // IN PUNICODE_STRING Class
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL ); // OUT PULONG Disposition
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&InterfaceIdString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ReferenceStringKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwCreateKey(
|
||
&InterfaceKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes,
|
||
0, // IN ULONG TitleIndex
|
||
NULL, // IN PUNICODE_STRING Class
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL ); // OUT PULONG Disposition
|
||
}
|
||
|
||
if (InterfaceKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( InterfaceKey );
|
||
}
|
||
|
||
if (ReferenceStringKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( ReferenceStringKey );
|
||
}
|
||
|
||
if (DeviceIdKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( DeviceIdKey );
|
||
}
|
||
|
||
RtlFreeUnicodeString( &DeviceIdString );
|
||
RtlFreeUnicodeString( &InterfaceIdString );
|
||
|
||
ZwClose( DeviceInterfacesKey );
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("InstallInterface returning %08x", Status) );
|
||
return Status;
|
||
}
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsGetBusEnumParentFDOFromChildPDO(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
OUT PDEVICE_OBJECT *FunctionalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Retrieves the parent's functional device object (FDO) given a
|
||
child physical device object (PDO).
|
||
|
||
Arguments:
|
||
IN PDEVICE_OBJECT DeviceObject -
|
||
Child device object
|
||
|
||
OUT PDEVICE_OBJECT *FunctionalDeviceObject -
|
||
Pointer to receive parent's FDO
|
||
|
||
Return:
|
||
STATUS_SUCCESS or STATUS_INVALID_PARAMETER
|
||
|
||
--*/
|
||
|
||
{
|
||
PPDO_EXTENSION PdoExtension;
|
||
|
||
PAGED_CODE();
|
||
PdoExtension = *(PPDO_EXTENSION *) DeviceObject->DeviceExtension;
|
||
if (!PdoExtension) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
if (ExtensionTypePdo != PdoExtension->ExtensionType) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
*FunctionalDeviceObject =
|
||
PdoExtension->BusDeviceExtension->FunctionalDeviceObject;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
KspRemoveBusEnumInterface(
|
||
IN PWORKER_CONTEXT WorkerContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
This is the internal routine which does the actual work of modifying
|
||
the registry.
|
||
|
||
NOTE:
|
||
|
||
This function is always called in the context of the system
|
||
process so that registry handles are tucked away safely from
|
||
rogue user-mode applications.
|
||
|
||
Arguments:
|
||
IN PWORKER_CONTEXT WorkerContext -
|
||
contains a pointer to the context for the worker
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PFDO_EXTENSION FdoExtension;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PIRP Irp;
|
||
PSWENUM_INSTALL_INTERFACE SwEnumInstallInterface;
|
||
NTSTATUS Status;
|
||
ULONG NullLocation;
|
||
|
||
Irp = WorkerContext->Irp;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof( SWENUM_INSTALL_INTERFACE )) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
FdoExtension = *(PFDO_EXTENSION *) irpSp->DeviceObject->DeviceExtension;
|
||
SwEnumInstallInterface =
|
||
(PSWENUM_INSTALL_INTERFACE) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Make sure that the string is UNICODE_NULL terminated. Note that the
|
||
// first two members of this structure are GUIDs and therefore WCHAR
|
||
// aligned.
|
||
//
|
||
|
||
//
|
||
// N.B.
|
||
//
|
||
// The ReferenceString member of SWENUM_INSTALL_INTERFACE is
|
||
// defined as WCHAR[1]. There is always room for the UNICODE_NULL.
|
||
//
|
||
|
||
NullLocation =
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength >> 1;
|
||
((PWCHAR) Irp->AssociatedIrp.SystemBuffer)[ NullLocation - 1 ] =
|
||
UNICODE_NULL;
|
||
|
||
//
|
||
// Take the list mutex
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
|
||
//
|
||
// Remove the interface
|
||
//
|
||
Status =
|
||
RemoveInterface(
|
||
SwEnumInstallInterface,
|
||
&FdoExtension->BaseRegistryPath );
|
||
if (NT_SUCCESS( Status )) {
|
||
//
|
||
// If successful, force the re-enumeration of the bus.
|
||
//
|
||
ScanBus( irpSp->DeviceObject );
|
||
|
||
IoInvalidateDeviceRelations(
|
||
FdoExtension->PhysicalDeviceObject,
|
||
BusRelations );
|
||
}
|
||
ExReleaseFastMutexUnsafe( &FdoExtension->DeviceListMutex );
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
WorkerContext->Status = Status;
|
||
KeSetEvent( &WorkerContext->CompletionEvent, IO_NO_INCREMENT, FALSE );
|
||
}
|
||
|
||
|
||
KSDDKAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
KsRemoveBusEnumInterface(
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Services an I/O request for the parent device to remove an interface
|
||
on the bus. The Irp->AssociatedIrp.SystemBuffer is assumed to have a
|
||
SWENUM_INSTALL_INTERFACE structure:
|
||
|
||
typedef struct _SWENUM_INSTALL_INTERFACE {
|
||
GUID DeviceId;
|
||
GUID InterfaceId;
|
||
WCHAR ReferenceString[1];
|
||
|
||
} SWENUM_INSTALL_INTERFACE, *PSWENUM_INSTALL_INTERFACE;
|
||
|
||
|
||
where the DeviceId, InterfaceId and ReferenceString specify the specific
|
||
device and interface to be removed.
|
||
|
||
Arguments:
|
||
IN PIRP Irp -
|
||
pointer to I/O request containing SWENUM_INSTALL_INTERFACE structure
|
||
|
||
Return:
|
||
STATUS_SUCCESS or an appropriate error code
|
||
|
||
--*/
|
||
|
||
{
|
||
WORK_QUEUE_ITEM InstallWorker;
|
||
WORKER_CONTEXT WorkerContext;
|
||
|
||
//
|
||
// Do the processing of the uninstallation in a worker item so that
|
||
// registry handles are managed in the context of the system process.
|
||
//
|
||
|
||
#if !defined( WIN9X_KS )
|
||
if (!SeSinglePrivilegeCheck(
|
||
RtlConvertLongToLuid( SE_LOAD_DRIVER_PRIVILEGE ),
|
||
ExGetPreviousMode() )) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
#endif
|
||
|
||
KeInitializeEvent(
|
||
&WorkerContext.CompletionEvent, NotificationEvent, FALSE );
|
||
WorkerContext.Irp = Irp;
|
||
ExInitializeWorkItem(
|
||
&InstallWorker, KspRemoveBusEnumInterface, &WorkerContext );
|
||
ExQueueWorkItem( &InstallWorker, DelayedWorkQueue );
|
||
KeWaitForSingleObject(
|
||
&WorkerContext.CompletionEvent, Executive, KernelMode, FALSE, NULL );
|
||
|
||
return WorkerContext.Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RemoveInterface(
|
||
IN PSWENUM_INSTALL_INTERFACE Interface,
|
||
IN PUNICODE_STRING BaseRegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
Removes the registry key for the interface in the SWENUM\Devices.
|
||
|
||
NOTE:
|
||
|
||
This function is always called in the context of the system
|
||
process so that registry handles are tucked away safely from
|
||
rogue user-mode applications.
|
||
|
||
From user-mode, the IOCTL that initiates an installation, removal, or
|
||
scan always results in a worker in the system process to do the actual
|
||
work.
|
||
|
||
Arguments:
|
||
IN PSWENUM_INSTALL_INTERFACE Interface -
|
||
|
||
IN PUNICODE_STRING BaseRegistryPath -
|
||
Return:
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE DeviceIdKey, DeviceInterfacesKey,
|
||
InterfaceKey, ReferenceStringKey;
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
UNICODE_STRING DeviceIdString, InterfaceIdString,
|
||
ReferenceString;
|
||
PWCHAR Ptr;
|
||
KEY_FULL_INFORMATION FullKeyInformation;
|
||
ULONG ReturnedSize;
|
||
|
||
_DbgPrintF( DEBUGLVL_BLAB, ("RemoveInterface") );
|
||
|
||
//
|
||
// Validate interface reference string
|
||
//
|
||
|
||
if (Ptr = Interface->ReferenceString) {
|
||
for (; *Ptr; *Ptr++) {
|
||
if ((*Ptr <= L' ') || (*Ptr > (WCHAR)0x7F) || (*Ptr == L',') || (*Ptr == L'\\') || (*Ptr == L'/')) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Open driver registry path
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status =
|
||
OpenDeviceInterfacesKey(
|
||
&DeviceInterfacesKey,
|
||
BaseRegistryPath ) )) {
|
||
return Status;
|
||
}
|
||
|
||
Status =
|
||
RtlStringFromGUID(
|
||
&Interface->DeviceId,
|
||
&DeviceIdString );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to create device ID string: %08x", Status) );
|
||
ZwClose( DeviceInterfacesKey );
|
||
return Status;
|
||
}
|
||
|
||
Status =
|
||
RtlStringFromGUID(
|
||
&Interface->InterfaceId,
|
||
&InterfaceIdString );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to create interface ID string: %08x", Status) );
|
||
RtlFreeUnicodeString( &DeviceIdString );
|
||
ZwClose( DeviceInterfacesKey );
|
||
return Status;
|
||
}
|
||
|
||
DeviceIdKey =
|
||
ReferenceStringKey =
|
||
InterfaceKey =
|
||
INVALID_HANDLE_VALUE;
|
||
|
||
//
|
||
// Open device ID key
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&DeviceIdString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
DeviceInterfacesKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwOpenKey(
|
||
&DeviceIdKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to open device key: %08x", Status) );
|
||
} else {
|
||
|
||
//
|
||
// Open reference string key
|
||
//
|
||
|
||
RtlInitUnicodeString(
|
||
&ReferenceString,
|
||
Interface->ReferenceString );
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&ReferenceString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
DeviceIdKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwOpenKey(
|
||
&ReferenceStringKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to open reference string key: %08x", Status) );
|
||
} else {
|
||
|
||
//
|
||
// Open interface ID key
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&InterfaceIdString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ReferenceStringKey,
|
||
(PSECURITY_DESCRIPTOR) NULL );
|
||
|
||
Status =
|
||
ZwOpenKey(
|
||
&InterfaceKey,
|
||
KEY_WRITE,
|
||
&ObjectAttributes );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
_DbgPrintF(
|
||
DEBUGLVL_VERBOSE,
|
||
("failed to open interface ID key: %08x", Status) );
|
||
} else {
|
||
|
||
//
|
||
// Remove the interface from the bus
|
||
//
|
||
|
||
ZwDeleteKey( InterfaceKey );
|
||
InterfaceKey = INVALID_HANDLE_VALUE;
|
||
|
||
}
|
||
|
||
Status =
|
||
ZwQueryKey(
|
||
ReferenceStringKey,
|
||
KeyFullInformation,
|
||
&FullKeyInformation,
|
||
sizeof ( FullKeyInformation ),
|
||
&ReturnedSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
if (FullKeyInformation.SubKeys == 0) {
|
||
ZwDeleteKey( ReferenceStringKey );
|
||
ReferenceStringKey = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
Status =
|
||
ZwQueryKey(
|
||
DeviceIdKey,
|
||
KeyFullInformation,
|
||
&FullKeyInformation,
|
||
sizeof ( FullKeyInformation ),
|
||
&ReturnedSize
|
||
);
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
if (FullKeyInformation.SubKeys == 0) {
|
||
ZwDeleteKey( DeviceIdKey );
|
||
DeviceIdKey = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (InterfaceKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( InterfaceKey );
|
||
}
|
||
|
||
if (ReferenceStringKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( ReferenceStringKey );
|
||
}
|
||
|
||
if (DeviceIdKey != INVALID_HANDLE_VALUE) {
|
||
ZwClose( DeviceIdKey );
|
||
}
|
||
|
||
RtlFreeUnicodeString( &DeviceIdString );
|
||
RtlFreeUnicodeString( &InterfaceIdString );
|
||
|
||
ZwClose( DeviceInterfacesKey );
|
||
|
||
_DbgPrintF( DEBUGLVL_VERBOSE, ("RemoveInterface returning %08x", Status) );
|
||
return Status;
|
||
}
|
||
|
||
|
||
#if 0
|
||
|
||
VOID
|
||
LogErrorWithStrings(
|
||
IN PFDO_EXTENSION FdoExtenstion,
|
||
IN ULONG ErrorCode,
|
||
IN ULONG UniqueId,
|
||
IN PUNICODE_STRING String1,
|
||
IN PUNICODE_STRING String2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
This function logs an error and includes the strings provided.
|
||
|
||
Arguments:
|
||
|
||
IN PFDO_EXTENSION FdoExtension -
|
||
pointer to the FDO extension
|
||
|
||
IN ULONG ErrorCode -
|
||
error code
|
||
|
||
IN ULONG UniqueId -
|
||
unique Id for this error
|
||
|
||
IN PUNICODE_STRING String1 -
|
||
The first string to be inserted.
|
||
|
||
IN PUNICODE_STRING String2 -
|
||
The second string to be inserted.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG length;
|
||
PCHAR dumpData;
|
||
PIO_ERROR_LOG_PACKET IoErrorPacket;
|
||
|
||
length = String1->Length + sizeof(IO_ERROR_LOG_PACKET) + 4;
|
||
|
||
if (String2) {
|
||
length += String2->Length;
|
||
}
|
||
|
||
if (length > ERROR_LOG_MAXIMUM_SIZE) {
|
||
|
||
//
|
||
// Don't have code to truncate strings so don't log this.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
IoErrorPacket = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(DeviceExtension->DeviceObject,
|
||
(UCHAR) length);
|
||
if (IoErrorPacket) {
|
||
IoErrorPacket->ErrorCode = ErrorCode;
|
||
IoErrorPacket->SequenceNumber = DeviceExtension->SequenceNumber++;
|
||
IoErrorPacket->MajorFunctionCode = 0;
|
||
IoErrorPacket->RetryCount = (UCHAR) 0;
|
||
IoErrorPacket->UniqueErrorValue = UniqueId;
|
||
IoErrorPacket->FinalStatus = STATUS_SUCCESS;
|
||
IoErrorPacket->NumberOfStrings = 1;
|
||
IoErrorPacket->StringOffset = (USHORT) ((PUCHAR)&IoErrorPacket->DumpData[0] - (PUCHAR)IoErrorPacket);
|
||
IoErrorPacket->DumpDataSize = (USHORT) (length - sizeof(IO_ERROR_LOG_PACKET));
|
||
IoErrorPacket->DumpDataSize /= sizeof(ULONG);
|
||
dumpData = (PUCHAR) &IoErrorPacket->DumpData[0];
|
||
|
||
RtlCopyMemory(dumpData, String1->Buffer, String1->Length);
|
||
|
||
dumpData += String1->Length;
|
||
if (String2) {
|
||
*dumpData++ = '\\';
|
||
*dumpData++ = '\0';
|
||
|
||
RtlCopyMemory(dumpData, String2->Buffer, String2->Length);
|
||
dumpData += String2->Length;
|
||
}
|
||
*dumpData++ = '\0';
|
||
*dumpData++ = '\0';
|
||
|
||
IoWriteErrorLogEntry(IoErrorPacket);
|
||
}
|
||
|
||
return;
|
||
}
|
||
#endif
|