You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7666 lines
220 KiB
7666 lines
220 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
codinit.c
|
|
|
|
Abstract:
|
|
|
|
This is the WDM streaming class driver. This module contains code related
|
|
to driver initialization.
|
|
|
|
Author:
|
|
|
|
billpa
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "codcls.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, StreamClassRegisterAdapter)
|
|
|
|
#if ENABLE_MULTIPLE_FILTER_TYPES
|
|
//#pragma alloc_text(PAGE, StreamClassRegisterNameExtensions)
|
|
#endif
|
|
|
|
#pragma alloc_text(PAGE, StreamClassPnPAddDevice)
|
|
#pragma alloc_text(PAGE, StreamClassPnPAddDeviceWorker)
|
|
#pragma alloc_text(PAGE, StreamClassPnP)
|
|
#pragma alloc_text(PAGE, SCStartWorker)
|
|
#pragma alloc_text(PAGE, SCUninitializeMinidriver)
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, SCFreeAllResources)
|
|
#pragma alloc_text(PAGE, SCInitializeCallback)
|
|
#pragma alloc_text(PAGE, SCStreamInfoCallback)
|
|
#pragma alloc_text(PAGE, SCUninitializeCallback)
|
|
#pragma alloc_text(PAGE, SCUnknownPNPCallback)
|
|
#pragma alloc_text(PAGE, SCUnknownPowerCallback)
|
|
#pragma alloc_text(PAGE, SciQuerySystemPowerHiberCallback)
|
|
#pragma alloc_text(PAGE, SCInsertStreamInfo)
|
|
#pragma alloc_text(PAGE, SCPowerCallback)
|
|
#pragma alloc_text(PAGE, SCCreateSymbolicLinks)
|
|
#pragma alloc_text(PAGE, SCDestroySymbolicLinks)
|
|
#pragma alloc_text(PAGE, SCCreateChildPdo)
|
|
#pragma alloc_text(PAGE, SCEnumerateChildren)
|
|
#pragma alloc_text(PAGE, SCEnumGetCaps)
|
|
#pragma alloc_text(PAGE, SCQueryEnumId)
|
|
#pragma alloc_text(PAGE, StreamClassForwardUnsupported)
|
|
#pragma alloc_text(PAGE, SCPowerCompletionWorker)
|
|
#pragma alloc_text(PAGE, SCSendSurpriseNotification)
|
|
#endif
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg("PAGECONST")
|
|
#endif
|
|
|
|
static const WCHAR EnumString[] = L"Enum";
|
|
static const WCHAR PnpIdString[] = L"PnpId";
|
|
|
|
//
|
|
// To make the bus enumeration code more readable:
|
|
//
|
|
// STREAM_BUSENUM_STRING: The prepended enumerator string
|
|
// STREAM_BUSENUM_STRING_LENGTH: The length of the above string in WCHARS
|
|
// STREAM_BUSENUM_SEPARATOR_LENGTH: The length of the separator "#" string
|
|
// in WCHARS
|
|
//
|
|
#define STREAM_BUSENUM_STRING L"Stream\\"
|
|
#define STREAM_BUSENUM_STRING_LENGTH \
|
|
((sizeof (STREAM_BUSENUM_STRING) - 1) / sizeof (WCHAR))
|
|
#define STREAM_BUSENUM_SEPARATOR_LENGTH 1
|
|
|
|
// CleanUp - the following three strings should go away
|
|
|
|
static const WCHAR ClsIdString[] = L"CLSID";
|
|
static const WCHAR DriverDescString[] = L"DriverDesc";
|
|
static const WCHAR FriendlyNameString[] = L"FriendlyName";
|
|
|
|
static const WCHAR DeviceTypeName[] = L"GLOBAL";
|
|
|
|
static const DEFINE_KSCREATE_DISPATCH_TABLE(CreateItems)
|
|
{
|
|
DEFINE_KSCREATE_ITEM(
|
|
FilterDispatchGlobalCreate,
|
|
DeviceTypeName,
|
|
NULL),
|
|
};
|
|
|
|
//
|
|
// list anchor for global minidriver info.
|
|
//
|
|
|
|
DEFINE_KSPIN_INTERFACE_TABLE(PinInterfaces)
|
|
{
|
|
DEFINE_KSPIN_INTERFACE_ITEM(
|
|
KSINTERFACESETID_Standard,
|
|
KSINTERFACE_STANDARD_STREAMING),
|
|
};
|
|
|
|
DEFINE_KSPIN_MEDIUM_TABLE(PinMediums)
|
|
{
|
|
DEFINE_KSPIN_MEDIUM_ITEM(
|
|
KSMEDIUMSETID_Standard,
|
|
KSMEDIUM_TYPE_ANYINSTANCE),
|
|
};
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg()
|
|
#endif
|
|
|
|
NTSTATUS
|
|
StreamClassRegisterAdapter(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN PHW_INITIALIZATION_DATA HwInitializationData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers a new streaming minidriver.
|
|
|
|
Arguments:
|
|
Argument1 - Pointer to driver object created by system.
|
|
Argument2 - Pointer to a UNICODE string of the registry path created
|
|
by system.
|
|
HwInitializationData - Minidriver initialization structure.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS if successful
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PDRIVER_OBJECT driverObject = Argument1;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
PMINIDRIVER_INFORMATION pMinidriverInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassInitialize: enter\n"));
|
|
|
|
//
|
|
// Check that the length of this structure is what the
|
|
// port driver expects it to be. This is effectively a
|
|
// version check.
|
|
//
|
|
#if ENABLE_MULTIPLE_FILTER_TYPES
|
|
//
|
|
// we split the ULONG HwInitializationDataSize into two ushorts, one for
|
|
// SizeOfThisPacket, another for StreamClassVersion which must be 0x0200 to
|
|
// indicate the two reserved fields now NumNameExtesnions and NameExtensionArray,
|
|
// contain valid information.
|
|
//
|
|
|
|
if (HwInitializationData->SizeOfThisPacket != sizeof(HW_INITIALIZATION_DATA) ||
|
|
( HwInitializationData->StreamClassVersion != 0 &&
|
|
HwInitializationData->StreamClassVersion != STREAM_CLASS_VERSION_20)) {
|
|
|
|
DebugPrint((DebugLevelFatal, "StreamClassInitialize: Minidriver wrong version\n"));
|
|
SCLogError((PDEVICE_OBJECT) driverObject, 0, CODCLASS_CLASS_MINIDRIVER_MISMATCH, 0x1002);
|
|
ASSERT( 0 );
|
|
return (STATUS_REVISION_MISMATCH);
|
|
}
|
|
|
|
#else // ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
if (HwInitializationData->HwInitializationDataSize < sizeof(HW_INITIALIZATION_DATA)) {
|
|
DebugPrint((DebugLevelFatal, "StreamClassInitialize: Minidriver wrong version\n"));
|
|
SCLogError((PDEVICE_OBJECT) driverObject, 0, CODCLASS_CLASS_MINIDRIVER_MISMATCH, 0x1002);
|
|
ASSERT( 0 );
|
|
return (STATUS_REVISION_MISMATCH);
|
|
}
|
|
#endif // ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
//
|
|
// Check that each required entry is not NULL.
|
|
//
|
|
|
|
if ((!HwInitializationData->HwReceivePacket) ||
|
|
(!HwInitializationData->HwRequestTimeoutHandler)) {
|
|
DebugPrint((DebugLevelFatal,
|
|
"StreamClassInitialize: Minidriver driver missing required entry\n"));
|
|
SCLogError((PDEVICE_OBJECT) driverObject, 0, CODCLASS_MINIDRIVER_MISSING_ENTRIES, 0x1003);
|
|
return (STATUS_REVISION_MISMATCH);
|
|
}
|
|
//
|
|
// set up dummy routines for each unsupported function
|
|
//
|
|
|
|
if (!HwInitializationData->HwCancelPacket) {
|
|
HwInitializationData->HwCancelPacket = SCDummyMinidriverRoutine;
|
|
}
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
|
|
driverObject->MajorFunction[IRP_MJ_PNP] = StreamClassPnP;
|
|
driverObject->MajorFunction[IRP_MJ_POWER] = StreamClassPower;
|
|
driverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = StreamClassForwardUnsupported;
|
|
// TODO: remove this once KS can multiplex cleanup Irps
|
|
driverObject->MajorFunction[IRP_MJ_CLEANUP] = StreamClassCleanup;
|
|
driverObject->DriverUnload = KsNullDriverUnload;
|
|
driverObject->DriverExtension->AddDevice = StreamClassPnPAddDevice;
|
|
|
|
//
|
|
// set ioctl interface
|
|
//
|
|
driverObject->MajorFunction[IRP_MJ_CREATE] = StreamClassPassThroughIrp;
|
|
driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
|
|
StreamClassPassThroughIrp;
|
|
driverObject->MajorFunction[IRP_MJ_CLOSE] = StreamClassPassThroughIrp;
|
|
driverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] =
|
|
StreamClassPassThroughIrp;
|
|
|
|
//
|
|
// Allocate a driver object extension to contain the minidriver's
|
|
// vectors.
|
|
//
|
|
|
|
Status = IoAllocateDriverObjectExtension(driverObject,
|
|
(PVOID) StreamClassPnP,
|
|
sizeof(MINIDRIVER_INFORMATION),
|
|
&pMinidriverInfo);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugPrint((DebugLevelError,
|
|
"StreamClassInitialize: No pool for global info"));
|
|
SCLogError((PDEVICE_OBJECT) driverObject, 0, CODCLASS_NO_GLOBAL_INFO_POOL, 0x1004);
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
RtlZeroMemory(pMinidriverInfo, sizeof(MINIDRIVER_INFORMATION));
|
|
|
|
RtlCopyMemory(pMinidriverInfo, HwInitializationData,
|
|
sizeof(HW_INITIALIZATION_DATA));
|
|
|
|
#if ENABLE_MULTIPLE_FILTER_TYPES
|
|
if ( HwInitializationData->StreamClassVersion != STREAM_CLASS_VERSION_20 ) {
|
|
//
|
|
// name extension not supplied.
|
|
//
|
|
pMinidriverInfo->HwInitData.NumNameExtensions = 0;
|
|
pMinidriverInfo->HwInitData.NameExtensionArray = NULL;
|
|
}
|
|
|
|
else {
|
|
//
|
|
// ver20, should have filter extension size
|
|
//
|
|
if ( 0 == pMinidriverInfo->HwInitData.FilterInstanceExtensionSize ) {
|
|
DebugPrint((DebugLevelWarning, "Version 20 driver should not "
|
|
" have FilterInstanceExtensionSize 0" ));
|
|
pMinidriverInfo->HwInitData.FilterInstanceExtensionSize = 4;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// initialize the control event for this driver
|
|
//
|
|
|
|
KeInitializeEvent(&pMinidriverInfo->ControlEvent,
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassPassThroughIrp (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pass through all Irps before being multiplexed through KS. If the device
|
|
cannot handle the request right now (the device is in a low power state
|
|
like D3), queue the Irp and complete it later.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
The device object
|
|
|
|
Irp -
|
|
The Irp in question
|
|
|
|
Return Value:
|
|
|
|
Either STATUS_PENDING or per the KS multiplex
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)
|
|
DeviceObject -> DeviceExtension;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Block user mode requests here in D3. Queue kernel mode ones.
|
|
//
|
|
if (Irp -> RequestorMode == UserMode) {
|
|
|
|
//
|
|
// Only do this rigmarole if we look to be outside D0.
|
|
//
|
|
if (DeviceExtension -> CurrentPowerState != PowerDeviceD0) {
|
|
|
|
//
|
|
// Handle PowerDownUnopened cases specially since they don't
|
|
// actually go into D0 until an instance is opened. We cannot
|
|
// block an open request in that case.
|
|
//
|
|
if (DeviceExtension -> RegistryFlags &
|
|
DEVICE_REG_FL_POWER_DOWN_CLOSED) {
|
|
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock (&DeviceExtension -> PowerLock, &OldIrql);
|
|
|
|
if (DeviceExtension -> CurrentSystemState ==
|
|
PowerSystemWorking &&
|
|
DeviceExtension -> CurrentPowerState !=
|
|
PowerDeviceD0) {
|
|
|
|
KeReleaseSpinLock (&DeviceExtension -> PowerLock, OldIrql);
|
|
|
|
//
|
|
// If we got here, the Irp must pass through as transition
|
|
// to D0 is keyed off it.
|
|
//
|
|
return KsDispatchIrp (DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock (&DeviceExtension -> PowerLock, OldIrql);
|
|
|
|
//
|
|
// At this point, we're not sleeping and not in SystemWorking.
|
|
// We're safe to block. Yes -- this might be an open -- and
|
|
// yes -- we might transition to SystemWorking before the
|
|
// KeWaitForSingleObject; however -- if that's the case,
|
|
// this **Notification** event will be signalled by that
|
|
// transition and we don't block the D0 key Irp.
|
|
//
|
|
|
|
}
|
|
|
|
ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
|
|
|
|
//
|
|
// At this point, it appeared that we weren't in D0. Block this
|
|
// thread until the device actually wakes. It doesn't matter if
|
|
// a state transition happened between the time we check and now
|
|
// since this is a notification event.
|
|
//
|
|
KeWaitForSingleObject (
|
|
&DeviceExtension -> BlockPoweredDownEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
return KsDispatchIrp (DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
//
|
|
// If we're in a low power state, queue the Irp and redispatch it later.
|
|
//
|
|
if (DeviceExtension -> CurrentPowerState != PowerDeviceD0) {
|
|
//
|
|
// Guard against PM changes while we're queueing the Irp. I don't
|
|
// want to get pre-empted before adding it to the queue, redispatch
|
|
// a bunch of Irps, and THEN have this one queued only to be lost
|
|
// until the next power transition.
|
|
//
|
|
// As an optimization, only grab the spinlock when it looks like we
|
|
// care. I don't want to spinlock on every Irp.
|
|
//
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock (&DeviceExtension -> PowerLock, &OldIrql);
|
|
|
|
//
|
|
// DEVICE_REG_FL_POWER_DOWN_CLOSED devices will not power up until
|
|
// an open happens and they power down when not opened. We cannot
|
|
// queue creates on them unless they are not in D0 due to an actual
|
|
// S-state transition. This is guarded against racing with an
|
|
// S-state transition by the PowerLock spinlock.
|
|
//
|
|
// NOTE: this will implicitly only allow creates to pass in non-D0
|
|
// for these power down closed devices because the only way we are
|
|
// in D3 / SystemWorking for these devices is when there are no opens
|
|
// currently on the device. Any Irp that comes through here at that
|
|
// time will be a create.
|
|
//
|
|
if (DeviceExtension -> CurrentPowerState != PowerDeviceD0 &&
|
|
!((DeviceExtension -> RegistryFlags &
|
|
DEVICE_REG_FL_POWER_DOWN_CLOSED) &&
|
|
DeviceExtension -> CurrentSystemState == PowerSystemWorking)) {
|
|
|
|
IoMarkIrpPending (Irp);
|
|
|
|
KsAddIrpToCancelableQueue (
|
|
&DeviceExtension -> PendedIrps,
|
|
&DeviceExtension -> PendedIrpsLock,
|
|
Irp,
|
|
KsListEntryTail,
|
|
NULL
|
|
);
|
|
|
|
KeReleaseSpinLock (&DeviceExtension -> PowerLock, OldIrql);
|
|
|
|
return STATUS_PENDING;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock (&DeviceExtension -> PowerLock, OldIrql);
|
|
|
|
}
|
|
|
|
return KsDispatchIrp (DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
void
|
|
SCRedispatchPendedIrps (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN BOOLEAN FailRequests
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Redispatch any Irps that were queued as a result of the device being
|
|
unavailable.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension -
|
|
The device extension
|
|
|
|
FailRequests -
|
|
Indication of whether to fail the requests or redispatch them
|
|
to the device.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIRP Irp;
|
|
|
|
//
|
|
// If we redispatch for any reason, allow Irps through.
|
|
//
|
|
KeSetEvent (
|
|
&DeviceExtension -> BlockPoweredDownEvent,
|
|
IO_NO_INCREMENT,
|
|
FALSE
|
|
);
|
|
|
|
Irp = KsRemoveIrpFromCancelableQueue (
|
|
&DeviceExtension -> PendedIrps,
|
|
&DeviceExtension -> PendedIrpsLock,
|
|
KsListEntryHead,
|
|
KsAcquireAndRemove
|
|
);
|
|
|
|
while (Irp) {
|
|
//
|
|
// If we were to fail the requests instead of redispatching, do
|
|
// this for everything but close Irps.
|
|
//
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
if (FailRequests &&
|
|
IrpSp -> MajorFunction != IRP_MJ_CLOSE) {
|
|
|
|
Irp -> IoStatus.Status = STATUS_DEVICE_BUSY;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
}
|
|
else {
|
|
KsDispatchIrp (DeviceExtension -> DeviceObject, Irp);
|
|
}
|
|
|
|
Irp = KsRemoveIrpFromCancelableQueue (
|
|
&DeviceExtension -> PendedIrps,
|
|
&DeviceExtension -> PendedIrpsLock,
|
|
KsListEntryHead,
|
|
KsAcquireAndRemove
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
SCSetCurrentDPowerState (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN DEVICE_POWER_STATE PowerState
|
|
)
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock (&DeviceExtension->PowerLock, &OldIrql);
|
|
//
|
|
// On any transition out of D0, block user mode requests until we're back
|
|
// in D0.
|
|
//
|
|
if (PowerState != PowerDeviceD0) {
|
|
KeResetEvent (&DeviceExtension->BlockPoweredDownEvent);
|
|
}
|
|
DeviceExtension->CurrentPowerState = PowerState;
|
|
KeReleaseSpinLock (&DeviceExtension->PowerLock, OldIrql);
|
|
}
|
|
|
|
void
|
|
SCSetCurrentSPowerState (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN SYSTEM_POWER_STATE PowerState
|
|
)
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock (&DeviceExtension->PowerLock, &OldIrql);
|
|
DeviceExtension->CurrentSystemState = PowerState;
|
|
KeReleaseSpinLock (&DeviceExtension->PowerLock, OldIrql);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassPnPAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new instance of the streaming minidriver
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to our driver object
|
|
|
|
PhysicalDeviceObject - Pointer to Device Object created by parent
|
|
|
|
Return Value:
|
|
|
|
Returns status of the worker routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// call the worker routine and return its status
|
|
//
|
|
|
|
return (StreamClassPnPAddDeviceWorker(DriverObject,
|
|
PhysicalDeviceObject,
|
|
&DeviceExtension));
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassPnPAddDeviceWorker(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN OUT PDEVICE_EXTENSION * ReturnedDeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the worker for processing the PNP add device call.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to our driver object
|
|
|
|
PhysicalDeviceObject - Pointer to Device Object created by parent
|
|
|
|
ReturnedDeviceExtension - pointer to the minidriver's extension
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMINIDRIVER_INFORMATION pMinidriverInfo;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS Status;
|
|
PDEVICE_OBJECT DeviceObject,
|
|
AttachedPdo;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((DebugLevelVerbose, "StreamClassAddDevice: enter\n"));
|
|
|
|
pMinidriverInfo = IoGetDriverObjectExtension(DriverObject,
|
|
(PVOID) StreamClassPnP);
|
|
|
|
|
|
if (pMinidriverInfo == NULL) {
|
|
DebugPrint((DebugLevelError,
|
|
"StreamClassAddDevice: No minidriver info"));
|
|
|
|
SCLogError((PDEVICE_OBJECT) DriverObject, 0, CODCLASS_NO_MINIDRIVER_INFO, 0x1004);
|
|
return (STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
//
|
|
// bump the add count in the minidriver object
|
|
//
|
|
|
|
pMinidriverInfo->OpenCount++;
|
|
|
|
//
|
|
// Create our device object with a our STREAM specific device extension
|
|
// No need to name it thanks to Plug N Play.
|
|
//
|
|
|
|
Status = IoCreateDevice(
|
|
DriverObject,
|
|
sizeof(DEVICE_EXTENSION) +
|
|
pMinidriverInfo->HwInitData.DeviceExtensionSize,
|
|
NULL,
|
|
FILE_DEVICE_KS,
|
|
FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&DeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return (Status);
|
|
|
|
}
|
|
//
|
|
// Attach ourself into the driver stack on top of our parent.
|
|
//
|
|
|
|
AttachedPdo = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject);
|
|
|
|
if (!(AttachedPdo)) {
|
|
|
|
DEBUG_BREAKPOINT();
|
|
DebugPrint((DebugLevelFatal, "StreamClassAddDevice: could not attach"));
|
|
IoDeleteDevice(DeviceObject);
|
|
return (Status);
|
|
|
|
}
|
|
*ReturnedDeviceExtension = DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
(*ReturnedDeviceExtension)->Signature = SIGN_DEVICE_EXTENSION;
|
|
(*ReturnedDeviceExtension)->Signature2 = SIGN_DEVICE_EXTENSION;
|
|
|
|
//
|
|
// set the minidriver info in the device extension
|
|
//
|
|
|
|
DeviceExtension->AttachedPdo = AttachedPdo;
|
|
|
|
//
|
|
// set the I/O counter
|
|
//
|
|
|
|
DeviceExtension->OneBasedIoCount = 1;
|
|
|
|
DeviceExtension->DriverInfo = pMinidriverInfo;
|
|
|
|
//
|
|
// Initialize timer.
|
|
//
|
|
|
|
IoInitializeTimer(DeviceObject, StreamClassTickHandler, NULL);
|
|
|
|
///
|
|
/// move from start device, we could have child PDO if we start and stop
|
|
///
|
|
InitializeListHead(&DeviceExtension->Children);
|
|
|
|
//
|
|
// Moved from StartDevice. We use the control event at Remove_device
|
|
// which can come in before the device starts.
|
|
//
|
|
KeInitializeEvent(&DeviceExtension->ControlEvent,
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
|
|
//
|
|
// set the current power state to D0
|
|
//
|
|
|
|
DeviceExtension->CurrentPowerState = PowerDeviceD0;
|
|
DeviceExtension->CurrentSystemState = PowerSystemWorking;
|
|
|
|
//
|
|
// fill in the minidriver info pointer to the dev extension
|
|
//
|
|
|
|
DeviceExtension->MinidriverData = pMinidriverInfo;
|
|
|
|
//
|
|
// keep this handy
|
|
//
|
|
DeviceExtension->FilterExtensionSize =
|
|
pMinidriverInfo->HwInitData.FilterInstanceExtensionSize;
|
|
|
|
DeviceExtension->DeviceObject = DeviceObject;
|
|
DeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
|
DeviceExtension->HwDeviceExtension = (PVOID) (DeviceExtension + 1);
|
|
|
|
//
|
|
// Initialize the pended Irp list.
|
|
//
|
|
InitializeListHead (&DeviceExtension -> PendedIrps);
|
|
KeInitializeSpinLock (&DeviceExtension -> PendedIrpsLock);
|
|
KeInitializeSpinLock (&DeviceExtension -> PowerLock);
|
|
KeInitializeEvent (&DeviceExtension -> BlockPoweredDownEvent, NotificationEvent, TRUE);
|
|
|
|
//
|
|
// Mark this object as supporting direct I/O so that I/O system
|
|
// will supply mdls in read/write irps.
|
|
//
|
|
|
|
DeviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
{
|
|
PKSOBJECT_CREATE_ITEM pCreateItems;
|
|
PWCHAR *NameInfo;
|
|
ULONG i;
|
|
ULONG NumberOfFilterTypes;
|
|
PFILTER_TYPE_INFO FilterTypeInfo;
|
|
//
|
|
// build an on-the-fly table of name extensions (including "GLOBAL"),
|
|
// from the minidriver's table.
|
|
//
|
|
|
|
InitializeListHead( &DeviceExtension->FilterInstanceList );
|
|
|
|
NumberOfFilterTypes = pMinidriverInfo->HwInitData.NumNameExtensions;
|
|
DeviceExtension->NumberOfNameExtensions = NumberOfFilterTypes;
|
|
if ( 0 == NumberOfFilterTypes ) {
|
|
NumberOfFilterTypes = 1;
|
|
}
|
|
|
|
DebugPrint((DebugLevelVerbose,
|
|
"Sizeof(FILTER_TYPE_INFO)=%x\n",
|
|
sizeof(FILTER_TYPE_INFO)));
|
|
|
|
FilterTypeInfo = ExAllocatePool(NonPagedPool,
|
|
(sizeof(FILTER_TYPE_INFO) +
|
|
sizeof(KSOBJECT_CREATE_ITEM))*
|
|
NumberOfFilterTypes);
|
|
|
|
if (!(FilterTypeInfo)) {
|
|
|
|
DebugPrint((DebugLevelFatal,
|
|
"StreamClassAddDevice: could not alloc createitems"));
|
|
TRAP;
|
|
IoDetachDevice(DeviceExtension->AttachedPdo);
|
|
IoDeleteDevice(DeviceObject);
|
|
return (Status);
|
|
}
|
|
|
|
pCreateItems = (PKSOBJECT_CREATE_ITEM)(FilterTypeInfo+NumberOfFilterTypes);
|
|
|
|
DebugPrint((DebugLevelVerbose,
|
|
"FilterTypeInfo@%x,pCreateItems@%x\n",
|
|
FilterTypeInfo,pCreateItems ));
|
|
|
|
|
|
DeviceExtension->NumberOfFilterTypes = NumberOfFilterTypes;
|
|
DeviceExtension->FilterTypeInfos = FilterTypeInfo;
|
|
|
|
//
|
|
// first copy the single default create item.
|
|
//
|
|
ASSERT( sizeof(CreateItems) == sizeof(KSOBJECT_CREATE_ITEM));
|
|
|
|
RtlCopyMemory(pCreateItems, CreateItems, sizeof (KSOBJECT_CREATE_ITEM));
|
|
|
|
//
|
|
// now construct the rest of the table based on the minidriver's values.
|
|
//
|
|
|
|
NameInfo = pMinidriverInfo->HwInitData.NameExtensionArray;
|
|
|
|
for (i = 0;
|
|
i < DeviceExtension->NumberOfNameExtensions;
|
|
i++, NameInfo++) {
|
|
|
|
LONG StringLength;
|
|
|
|
StringLength = wcslen(*NameInfo)*sizeof(WCHAR);
|
|
|
|
pCreateItems[i].ObjectClass.Length = (USHORT)StringLength;
|
|
pCreateItems[i].ObjectClass.MaximumLength = (USHORT)(StringLength + sizeof(UNICODE_NULL));
|
|
pCreateItems[i].ObjectClass.Buffer = *NameInfo;
|
|
pCreateItems[i].Create = FilterDispatchGlobalCreate;
|
|
pCreateItems[i].Context = ULongToPtr(i);
|
|
pCreateItems[i].SecurityDescriptor = NULL;
|
|
pCreateItems[i].Flags = 0;
|
|
|
|
} // for # createitems
|
|
DeviceExtension->CreateItems = pCreateItems;
|
|
KsAllocateDeviceHeader(&DeviceExtension->ComObj.DeviceHeader,
|
|
i==0 ? 1: i,
|
|
(PKSOBJECT_CREATE_ITEM) pCreateItems);
|
|
|
|
}
|
|
|
|
//
|
|
// set the flag indicating whether we need to do synchronization.
|
|
//
|
|
|
|
DeviceExtension->NoSync =
|
|
pMinidriverInfo->HwInitData.TurnOffSynchronization;
|
|
|
|
//
|
|
// presuppose we will need synchronization.
|
|
//
|
|
|
|
#if DBG
|
|
DeviceExtension->SynchronizeExecution = SCDebugKeSynchronizeExecution;
|
|
#else
|
|
DeviceExtension->SynchronizeExecution = KeSynchronizeExecution;
|
|
#endif
|
|
|
|
//
|
|
// set the synchronized minidriver callin routine vectors
|
|
//
|
|
|
|
DeviceExtension->BeginMinidriverCallin = (PVOID) SCBeginSynchronizedMinidriverCallin;
|
|
DeviceExtension->EndMinidriverDeviceCallin = (PVOID) SCEndSynchronizedMinidriverDeviceCallin;
|
|
DeviceExtension->EndMinidriverStreamCallin = (PVOID) SCEndSynchronizedMinidriverStreamCallin;
|
|
|
|
if (DeviceExtension->NoSync) {
|
|
|
|
//
|
|
// we won't do synchronization, so use the dummy sync routine.
|
|
//
|
|
|
|
DeviceExtension->SynchronizeExecution = StreamClassSynchronizeExecution;
|
|
DeviceExtension->InterruptObject = (PVOID) DeviceExtension;
|
|
|
|
//
|
|
// set the unsynchronized minidriver callin routine vectors
|
|
//
|
|
|
|
|
|
DeviceExtension->BeginMinidriverCallin = (PVOID) SCBeginUnsynchronizedMinidriverCallin;
|
|
DeviceExtension->EndMinidriverDeviceCallin = (PVOID) SCEndUnsynchronizedMinidriverDeviceCallin;
|
|
DeviceExtension->EndMinidriverStreamCallin = (PVOID) SCEndUnsynchronizedMinidriverStreamCallin;
|
|
|
|
}
|
|
//
|
|
// read registry settings for this adapter
|
|
//
|
|
|
|
SCReadRegistryValues(DeviceExtension, PhysicalDeviceObject);
|
|
|
|
//
|
|
// if the device cannot be paged out when closed, turn off this feature
|
|
// for the whole driver
|
|
//
|
|
|
|
if (!(DeviceExtension->RegistryFlags & DEVICE_REG_FL_PAGE_CLOSED)) {
|
|
|
|
pMinidriverInfo->Flags |= DRIVER_FLAGS_NO_PAGEOUT;
|
|
}
|
|
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
DeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
|
|
DebugPrint((DebugLevelVerbose, "StreamClassAddDevice: leave\n"));
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassPnP(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the various Plug N Play messages
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PHW_INITIALIZATION_DATA HwInitData;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IrpStack,
|
|
NextStack;
|
|
BOOLEAN RequestIssued;
|
|
DEVICE_CAPABILITIES DeviceCapabilities;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// check to see if the device is a child
|
|
//
|
|
|
|
DebugPrint((DebugLevelVerbose, "'SCPNP:DevObj=%x,Irp=%x\n",DeviceObject, Irp ));
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_CHILD) {
|
|
|
|
PCHILD_DEVICE_EXTENSION ChildExtension = (PCHILD_DEVICE_EXTENSION) DeviceExtension;
|
|
|
|
switch (IrpStack->MinorFunction) {
|
|
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"Child PDO=%x forwards Query_Interface to Parent FDO=%x\n",
|
|
DeviceObject,
|
|
ChildExtension->ParentDeviceObject));
|
|
|
|
return (IoCallDriver(ChildExtension->ParentDeviceObject,
|
|
Irp));
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
DebugPrint((DebugLevelInfo,
|
|
"StartChild DevObj=%x Flags=%x\n"
|
|
,DeviceObject,
|
|
ChildExtension->Flags ));
|
|
ChildExtension->Flags &= ~DEVICE_FLAGS_CHILD_MARK_DELETE;
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_STOP_DEVICE:
|
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
if (IrpStack->Parameters.QueryDeviceRelations.Type ==
|
|
TargetDeviceRelation) {
|
|
|
|
PDEVICE_RELATIONS DeviceRelations = NULL;
|
|
|
|
DeviceRelations = ExAllocatePool(PagedPool, sizeof(*DeviceRelations));
|
|
|
|
if (DeviceRelations == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
//
|
|
// TargetDeviceRelation reported PDOs need to be ref'ed.
|
|
// PNP will deref this later.
|
|
//
|
|
ObReferenceObject(DeviceObject);
|
|
DeviceRelations->Count = 1;
|
|
DeviceRelations->Objects[0] = DeviceObject;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) DeviceRelations;
|
|
|
|
} else {
|
|
Status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
goto done;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
DEBUG_BREAKPOINT();
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"Child PDO %x receives REMOVE\n",
|
|
DeviceObject ));
|
|
|
|
//
|
|
// remove this extension from the list.
|
|
// This is true - pierre tells me that PNP won't reenter me. Verify
|
|
// that this is true on NT also.
|
|
//
|
|
//
|
|
// When a PDO first receives this msg, it is usually forwarded
|
|
// from FDO. We can't just delete this PDO, but mark it delete
|
|
// pending.
|
|
//
|
|
|
|
if ( !(ChildExtension->Flags & DEVICE_FLAGS_CHILD_MARK_DELETE )) {
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
RemoveEntryList(&ChildExtension->ChildExtensionList);
|
|
|
|
//
|
|
// free the device name string if it exists.
|
|
//
|
|
|
|
if (ChildExtension->DeviceName) {
|
|
|
|
ExFreePool(ChildExtension->DeviceName);
|
|
}
|
|
|
|
//
|
|
// delete the PDO
|
|
//
|
|
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
goto done;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
|
|
Status = SCEnumGetCaps(ChildExtension,
|
|
IrpStack->Parameters.DeviceCapabilities.Capabilities);
|
|
goto done;
|
|
|
|
case IRP_MN_QUERY_ID:
|
|
|
|
//
|
|
// process the ID query for the child devnode.
|
|
//
|
|
|
|
Status = SCQueryEnumId(DeviceObject,
|
|
IrpStack->Parameters.QueryId.IdType,
|
|
(PWSTR *) & (Irp->IoStatus.Information));
|
|
goto done;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
done:
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return (Status);
|
|
|
|
} // switch
|
|
} // if child
|
|
//
|
|
// this is not a child device. do adult processing
|
|
//
|
|
|
|
HwInitData = &(DeviceExtension->MinidriverData->HwInitData);
|
|
|
|
//
|
|
// show one more reference to driver.
|
|
//
|
|
|
|
SCReferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// show one more I/O pending
|
|
//
|
|
|
|
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
|
|
|
|
switch (IrpStack->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPNP: Start Device %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// reinitialize the minidriver's device extension. This is
|
|
// necessary as we may receive a start before a remove, such as in
|
|
// the case of a PNP rebalance.
|
|
//
|
|
|
|
RtlZeroMemory(DeviceExtension->HwDeviceExtension,
|
|
DeviceExtension->DriverInfo->HwInitData.DeviceExtensionSize);
|
|
|
|
//
|
|
// clear the inaccessible flag since we may have stopped the
|
|
// device previously.
|
|
//
|
|
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
//
|
|
// The START message gets passed to the PhysicalDeviceObject
|
|
// we were give in PnPAddDevice, so call 'er down first.
|
|
//
|
|
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// get the capabilities of our parent. This info is used for
|
|
// controlling the system power state.
|
|
//
|
|
|
|
Status = SCQueryCapabilities(DeviceExtension->AttachedPdo,
|
|
&DeviceCapabilities);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// copy the device state info into the device extension.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
RtlCopyMemory(&DeviceExtension->DeviceState[0],
|
|
&DeviceCapabilities.DeviceState[0],
|
|
sizeof(DeviceExtension->DeviceState));
|
|
|
|
} // if query succeeded
|
|
//
|
|
// call the worker routine to complete the start processing.
|
|
// this routine completes the IRP.
|
|
//
|
|
|
|
Status = SCStartWorker(Irp);
|
|
|
|
//
|
|
// dereference the minidriver which will page it out if possible.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (Status);
|
|
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPNP: Query Relations %x\n",
|
|
DeviceObject));
|
|
|
|
switch (IrpStack->Parameters.QueryDeviceRelations.Type) {
|
|
|
|
case TargetDeviceRelation:
|
|
|
|
//
|
|
// just call the next driver and fall thru, since we're being
|
|
// called for the FDO of a PDO for which we are not the parent.
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
break;
|
|
|
|
case BusRelations:
|
|
|
|
//
|
|
// invoke routine to enumerate any child devices
|
|
//
|
|
|
|
Status = SCEnumerateChildren(DeviceObject,
|
|
Irp);
|
|
break;
|
|
|
|
|
|
default:
|
|
//
|
|
// pass down unmodified irp. see bug 282915.
|
|
//
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
} // switch
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
//
|
|
// According to DDK, QUERY_STOP and QUERY_REMOVE
|
|
// requeire very different repsonses. It's not best to
|
|
// handle by the same code, if not erroneous.
|
|
//
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPNP: Query Stop %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// presuppose good status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Performace improvement chance: The ControlEvent should be init in AddDevice, so
|
|
// that we don't need a check here. This check is not an optimal
|
|
// fix for 283057. Refix it and the same in Query_Remove.
|
|
//
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) // bug 283057
|
|
{
|
|
//
|
|
// take the event to avoid race
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// Refer to DDK.
|
|
// We must fail a query_stop if any of the following is true.
|
|
// a. we are notified with IRP_MN_DEVICE_USAGE_NOTIFICATION
|
|
// that the device is in the path of a paging, hiberation
|
|
// or crash dump file.
|
|
// b. The device's hardware resources cannot be released.
|
|
//
|
|
// Assuming we are not in the paging path for a. For b, we will
|
|
// pass this Irp down to the mini driver to let it have a say.
|
|
// We will not reject the Query just because of outstanding opens.
|
|
//
|
|
|
|
//DeviceExtension->Flags |= DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
//
|
|
// calldown to next driver will be done in the callback.
|
|
//
|
|
//Status = SCSendUnknownCommand(Irp,
|
|
// DeviceExtension,
|
|
// SCPNPQueryCallback,
|
|
// &RequestIssued);
|
|
|
|
//
|
|
// However, to achieve the noble goal, as everything stands now, is opening
|
|
// a whole can of worms. I will keep this old behavior that existed
|
|
// since win98. The bug OSR4.1 #98132 said to be a regression is completely
|
|
// false. This code is in win98 and win2k. And I have set up win98 to repro
|
|
// this behavior to disapprove the regression claim.
|
|
//
|
|
|
|
if (DeviceExtension->NumberOfOpenInstances == 0) {
|
|
|
|
//
|
|
// if there are no open instances, there can be no outstanding
|
|
// I/O, so mark the device as going away.
|
|
//
|
|
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// call the worker routine to complete the query processing.
|
|
// this routine calls back the IRP.
|
|
//
|
|
|
|
Status = SCQueryWorker(DeviceObject, Irp);
|
|
|
|
} else {
|
|
|
|
//
|
|
// the device is open. fail the query.
|
|
//
|
|
|
|
Status = SCCompleteIrp(Irp, STATUS_DEVICE_BUSY, DeviceExtension);
|
|
|
|
}
|
|
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) // bug 283057
|
|
{
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (Status);
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPNP: Query Remove %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// presuppose good status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) // bug 283057
|
|
{
|
|
//
|
|
// take the event to avoid race
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// According DDK, if there are opens that can't be closed
|
|
// we must fail the query.
|
|
// So, if there are opened files, just fail the query.
|
|
//
|
|
if (DeviceExtension->NumberOfOpenInstances == 0) {
|
|
|
|
//
|
|
// if there are no open instances, there can be no outstanding
|
|
// I/O, so mark the device as going away.
|
|
//
|
|
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// call the worker routine to complete the query processing.
|
|
// this routine calls back the IRP.
|
|
//
|
|
|
|
Status = SCQueryWorker(DeviceObject, Irp);
|
|
|
|
} else {
|
|
|
|
//
|
|
// the device is open. fail the query.
|
|
//
|
|
|
|
Status = SCCompleteIrp(Irp, STATUS_DEVICE_BUSY, DeviceExtension);
|
|
|
|
}
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) // bug 283057
|
|
{
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (Status);
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
|
|
//
|
|
// clear the inaccessible flag and call'er down
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPnP: MN_CANCEL_REMOVE %x\n",
|
|
DeviceObject));
|
|
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
//
|
|
// call next driver
|
|
//
|
|
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// dereference the driver which will page out if possible.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (SCCompleteIrp(Irp, STATUS_SUCCESS, DeviceExtension));
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
|
|
//
|
|
// clear the inaccessible flag and call'er down
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPnP: MN_CANCEL_STOP %x\n",
|
|
DeviceObject));
|
|
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
//
|
|
// call next driver
|
|
//
|
|
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// dereference the driver which will page out if possible.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (SCCompleteIrp(Irp, STATUS_SUCCESS, DeviceExtension));
|
|
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPnP: MN_STOP_DEVICE %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// presuppose good status. if we have actually started the device,
|
|
// stop it now.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) {
|
|
|
|
//
|
|
// call routine to uninitialize minidriver
|
|
//
|
|
|
|
Status = SCUninitializeMinidriver(DeviceObject, Irp);
|
|
|
|
//
|
|
// now call the next driver in the stack with the IRP, which will
|
|
// determine the final status.
|
|
//
|
|
|
|
} // if started
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
}
|
|
|
|
//
|
|
// Fail everything that's been queued.
|
|
//
|
|
SCRedispatchPendedIrps (DeviceExtension, TRUE);
|
|
|
|
//
|
|
// call routine to complete the IRP
|
|
//
|
|
|
|
SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
//
|
|
// show one less reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (Status);
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPnP: MN_REMOVE_DEVICE %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// handle a "suprise" style removal if we have not been stopped.
|
|
// set success status in case we have already stopped.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if ( DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED ) {
|
|
|
|
SCSendSurpriseNotification(DeviceExtension, Irp);
|
|
|
|
Status = SCUninitializeMinidriver(DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
}
|
|
|
|
//
|
|
// Fail any pended Irps.
|
|
//
|
|
SCRedispatchPendedIrps (DeviceExtension, TRUE);
|
|
|
|
//
|
|
// call routine to complete the IRP
|
|
//
|
|
|
|
Status = SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// free the device header.
|
|
//
|
|
|
|
if ( NULL != DeviceExtension->ComObj.DeviceHeader ) {
|
|
KsFreeDeviceHeader(DeviceExtension->ComObj.DeviceHeader);
|
|
}
|
|
|
|
//
|
|
// take the event to avoid race
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
//
|
|
// detach from the PDO now if the opened file count is zero.
|
|
//
|
|
|
|
if (DeviceExtension->NumberOfOpenInstances == 0) {
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"SCPNP: detaching %x from %x\n",
|
|
DeviceObject,
|
|
DeviceExtension->AttachedPdo));
|
|
|
|
if ( NULL != DeviceExtension->AttachedPdo ) {
|
|
//
|
|
// detach could happen at close, check before leap.
|
|
// event is taken, check is safe.
|
|
//
|
|
IoDetachDevice(DeviceExtension->AttachedPdo);
|
|
DeviceExtension->AttachedPdo = NULL;
|
|
}
|
|
|
|
///
|
|
/// mark child pdos if any
|
|
///
|
|
{
|
|
PLIST_ENTRY Node;
|
|
PCHILD_DEVICE_EXTENSION ChildExtension;
|
|
|
|
while (!IsListEmpty( &DeviceExtension->Children )) {
|
|
Node = RemoveHeadList( &DeviceExtension->Children );
|
|
ChildExtension = CONTAINING_RECORD(Node,
|
|
CHILD_DEVICE_EXTENSION,
|
|
ChildExtensionList);
|
|
DebugPrint((DebugLevelInfo,
|
|
"Marking and delete childpdo Extension %p\n",
|
|
ChildExtension));
|
|
|
|
ChildExtension->Flags |= DEVICE_FLAGS_CHILD_MARK_DELETE;
|
|
IoDeleteDevice(ChildExtension->ChildDeviceObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// delete the device
|
|
//
|
|
|
|
// A dev could be stop and start. Free stuff allocated
|
|
// at AddDevice.
|
|
// FilterTypeInfos includes FilterTypeInfos CreateItems.
|
|
// Free these here at remove_device
|
|
if ( DeviceExtension->FilterTypeInfos ) {
|
|
ExFreePool( DeviceExtension->FilterTypeInfos );
|
|
DeviceExtension->FilterTypeInfos = NULL;
|
|
DeviceExtension->CreateItems = NULL;
|
|
}
|
|
|
|
IoDeleteDevice(DeviceExtension->DeviceObject);
|
|
}
|
|
return (Status);
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPnP: MN_SURPRISE_REMOVAL %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// handle a "suprise" style removal if we have not been stopped.
|
|
// set success status in case we have already stopped.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) {
|
|
|
|
SCSendSurpriseNotification(DeviceExtension, Irp);
|
|
Status = SCUninitializeMinidriver(DeviceObject, Irp);
|
|
}
|
|
|
|
//
|
|
// forward the surprise removal IRP to the next layer, regardless of
|
|
// our status.
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// call routine to complete the IRP
|
|
//
|
|
|
|
Status = SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// indicate that we received an "NT style" surprise removal
|
|
// notification
|
|
// so that we won't do the "memphis style" behavior on filter close.
|
|
//
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_SURPRISE_REMOVE_RECEIVED;
|
|
|
|
return (Status);
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamClassPNP: Query Caps\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// indicate that suprise removal is OK after calling request down
|
|
// to next level.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
IrpStack->Parameters.DeviceCapabilities.
|
|
Capabilities->SurpriseRemovalOK = TRUE;
|
|
|
|
Status = SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
//
|
|
// show one less reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (Status);
|
|
|
|
default:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamPnP: unknown function\n",
|
|
DeviceObject));
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) {
|
|
|
|
//
|
|
// unknown function, so call it down to the minidriver as such.
|
|
// this routine completes the IRP if we are able to issue the
|
|
// request.
|
|
//
|
|
|
|
Status = SCSendUnknownCommand(Irp,
|
|
DeviceExtension,
|
|
SCUnknownPNPCallback,
|
|
&RequestIssued);
|
|
|
|
if (!RequestIssued) {
|
|
//
|
|
// could not send the unknown command down. show one fewer
|
|
// I/O
|
|
// pending and fall thru to generic handler.
|
|
//
|
|
|
|
DEBUG_BREAKPOINT();
|
|
Status = SCCompleteIrp(Irp, STATUS_INSUFFICIENT_RESOURCES, DeviceExtension);
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// call next driver
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
} // if started
|
|
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (Status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassCleanup (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TODO: Remove this once KS can multiplex CLEANUP Irps.
|
|
|
|
Manual multiplex of cleanup Irps. Note that FsContext is NOT NECESSARILY
|
|
OURS. The cookie check is done to check for streams until KS handles
|
|
this correctly.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
The device object
|
|
|
|
Irp -
|
|
The CLEANUP irp
|
|
|
|
Return Value:
|
|
|
|
The Irp return code set appropriately.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation (Irp);
|
|
PCOOKIE_CHECK CookieCheck =
|
|
(PCOOKIE_CHECK) IoStack -> FileObject -> FsContext;
|
|
|
|
//
|
|
// Check for the cookie. If it's not there or the context is not there,
|
|
// bail.
|
|
//
|
|
if (CookieCheck &&
|
|
CookieCheck -> PossibleCookie == STREAM_OBJECT_COOKIE) {
|
|
|
|
return StreamDispatchCleanup (DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
Irp -> IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SciQuerySystemPowerHiberCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the completion of an unknown Power command for query system hiber
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
NTSTATUS Status, MiniStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// delete the SRB since we are done with it
|
|
//
|
|
|
|
MiniStatus = SCDequeueAndDeleteSrb(SRB);
|
|
|
|
if ( STATUS_NOT_IMPLEMENTED == MiniStatus ) {
|
|
MiniStatus = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( STATUS_NOT_SUPPORTED == MiniStatus ) {
|
|
|
|
//
|
|
// not surprising, old driver doesn't handle this.
|
|
//
|
|
|
|
if ( 0 != (DeviceExtension->RegistryFlags &
|
|
DRIVER_USES_SWENUM_TO_LOAD ) ||
|
|
0 != (DeviceExtension->RegistryFlags &
|
|
DEVICE_REG_FL_OK_TO_HIBERNATE ) ) {
|
|
|
|
//
|
|
// default for swenum driver is OK to hiber
|
|
// No hiber for other drivers unless explicitly
|
|
// say so in the registry
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"%ws Allow hibernation!\n",
|
|
DeviceExtension->DeviceObject->
|
|
DriverObject->DriverName.Buffer));
|
|
MiniStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// for others, disallow
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"%ws Disallow hibernation!\n",
|
|
DeviceExtension->DeviceObject->
|
|
DriverObject->DriverName.Buffer));
|
|
MiniStatus = STATUS_DEVICE_BUSY;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( MiniStatus )) {
|
|
|
|
//
|
|
// it is not explicitly failed by the mini driver pass down the Irp
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
if ( Status == STATUS_NOT_SUPPORTED ) {
|
|
|
|
//
|
|
// no one below knows/cares. Use our mini status
|
|
//
|
|
|
|
Status = MiniStatus;
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// mini driver explicitly failed this
|
|
//
|
|
|
|
Status = MiniStatus;
|
|
}
|
|
|
|
//
|
|
// complete the IRP with the final status
|
|
//
|
|
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCSysWakeCallNextDriver(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we receive a wake up system Irp which we can't not block.
|
|
If we block, the lower driver might queue this Irp ( such as acpi ) and the
|
|
po system could be dead locked. In theory, we should complete the requested
|
|
D Irp and use the status as the status for the SWake Irp. In practise, we can
|
|
just send down this Irp assuming all is well. In the unlikely condition, the SWake
|
|
Irp was unsuccessful, the D Irp will fail. But there is really nothing we can
|
|
improve or nothing will get worse.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - pointer to device extension
|
|
Irp - pointer to IRP
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// call down and be done with this SWake Irp; the D Irp completion routine
|
|
// should not complete this SWake Irp.
|
|
//
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
Status = PoCallDriver(DeviceExtension->AttachedPdo, Irp);
|
|
|
|
//
|
|
// If we get an error, we complete this S irp in the caller with the error.
|
|
//
|
|
|
|
return (Status);
|
|
}
|
|
|
|
VOID
|
|
SCDevIrpCompletionWorker(
|
|
PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This is the worker routine for Device Power Wakeup Irp which schedule
|
|
a workitem to continue the work at the Irp on its way up. We
|
|
need to schedule this work because the completion routine could be called at
|
|
DISPATCH_LEVEL. We schedule the workitem so we can safely take
|
|
control event and call to our mini driver.
|
|
IRQL < DISPATCH_LEVEL
|
|
|
|
|
|
Parameter:
|
|
|
|
pIrp: the original Irp which we have marked MORE_PROCEESING_REQUIRED.
|
|
We will complete it after we call our mini driver.
|
|
|
|
Return:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(pIrp);
|
|
PDEVICE_EXTENSION DeviceExtension = IrpStack->DeviceObject->DeviceExtension;
|
|
BOOLEAN RequestIssued;
|
|
NTSTATUS Status;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// take the event to avoid race
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
//
|
|
// send a set power SRB to the device.
|
|
// additional processing will be done by the callback
|
|
// procedure. This routine completes the IRP if it is able
|
|
// to issue the request.
|
|
//
|
|
|
|
Status = SCSubmitRequest(SRB_CHANGE_POWER_STATE,
|
|
(PVOID) PowerDeviceD0,
|
|
0,
|
|
SCPowerCallback,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
pIrp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->
|
|
MinidriverData->HwInitData.
|
|
HwReceivePacket );
|
|
|
|
|
|
if (!RequestIssued) {
|
|
|
|
//
|
|
// If we fail to issue SRB, the SCPowerCallback won't happen.
|
|
// We need to carry out the power IRP processing here;
|
|
//
|
|
// set the new power state in the device extension.
|
|
//
|
|
SCSetCurrentDPowerState (DeviceExtension, PowerDeviceD0);
|
|
|
|
PoStartNextPowerIrp( pIrp );
|
|
SCCompleteIrp(pIrp, STATUS_SUCCESS, DeviceExtension);
|
|
}
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// Redispatch any Irps pended because of lower power states.
|
|
//
|
|
SCRedispatchPendedIrps (DeviceExtension, FALSE);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCDevWakeCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID pContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is for Device wakeup Irp completion.
|
|
We sent it to NextDeviceObject first. Now this is back.
|
|
We process out work for the mini driver. We might be called
|
|
at Dispatch_LEVEL.
|
|
|
|
IRQL <= DISPATCH_LEVEL
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
Irp - Irp that just completed
|
|
pContext - the context
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Schedule a work item in case we are called at DISPATCH_LEVEL
|
|
// note that we can use a global Devcice Power item since we have
|
|
// not yet issued the PoNextPowerIrp call which is called at the callback
|
|
// of the power Srb
|
|
//
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->DevIrpCompletionWorkItem,
|
|
SCDevIrpCompletionWorker,
|
|
Irp);
|
|
|
|
ExQueueWorkItem(&DeviceExtension->DevIrpCompletionWorkItem,
|
|
DelayedWorkQueue);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCDevWakeCallNextDriver(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive device wake up Irp. Need to send down the Irp 1st.
|
|
Also this can't be synchronous. We could dead lock, if we do this
|
|
synchronously. Send it down without waiting. Process it when it compltes
|
|
back to us.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - pointer to device extension
|
|
Irp - pointer to IRP
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
SCDevWakeCompletionRoutine,
|
|
NULL,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// We are to schedule a workitem to complete the work
|
|
// in the completion routin. Mark the Irp pending
|
|
//
|
|
IoMarkIrpPending( Irp );
|
|
|
|
Status = PoCallDriver(DeviceExtension->AttachedPdo, Irp);
|
|
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
StreamClassPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the various Plug N Play messages
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PHW_INITIALIZATION_DATA HwInitData;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
BOOLEAN RequestIssued;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_CHILD) {
|
|
|
|
switch (IrpStack->MinorFunction) {
|
|
|
|
default:
|
|
PoStartNextPowerIrp( Irp ); // shut down would bugcheck w/o this
|
|
Status = Irp->IoStatus.Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return (Status);
|
|
|
|
}
|
|
} // if child
|
|
//
|
|
// if the device is stopped, just call the power message down to the next
|
|
// level.
|
|
//
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_DEVICE_INACCESSIBLE) {
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
PoStartNextPowerIrp( Irp );
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return (Status);
|
|
} // if inaccessible
|
|
HwInitData = &(DeviceExtension->MinidriverData->HwInitData);
|
|
|
|
//
|
|
// show one more reference to driver.
|
|
//
|
|
|
|
SCReferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// show one more I/O pending
|
|
//
|
|
|
|
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
|
|
|
|
switch (IrpStack->MinorFunction) {
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
|
|
//
|
|
// presuppose good status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
switch (IrpStack->Parameters.Power.Type) {
|
|
|
|
case SystemPowerState:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"Query_power S[%d]\n",
|
|
IrpStack->Parameters.Power.State.SystemState));
|
|
|
|
//
|
|
// some minidrivers want to not suspend if their pins are in
|
|
// the RUN state. check for this case.
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"POWER Query_Power DevObj %x RegFlags=%x SysState=%x\n",
|
|
DeviceObject,
|
|
DeviceExtension->RegistryFlags,
|
|
IrpStack->Parameters.Power.State.SystemState));
|
|
|
|
#ifdef WIN9X_STREAM
|
|
|
|
if ( PowerSystemHibernate ==
|
|
IrpStack->Parameters.Power.State.SystemState ) {
|
|
|
|
//
|
|
// Power query to hibernation state. Many existing drivers
|
|
// are hibernation unaware. We will reject this query. Or
|
|
// drivers' devices woken up from hiber will be in un-init
|
|
// state. Some drivers would fault. Lucky others do not but
|
|
// would not work. For less of the evil, we try to protect
|
|
// the system by rejecting the hibernation. Note though, this
|
|
// chance to reject is not available with forced ( low battery
|
|
// or user force ) hibernation.
|
|
//
|
|
//
|
|
// unknown function, so call it down to the minidriver as such.
|
|
// this routine completes the IRP if it is able to issue the request.
|
|
//
|
|
|
|
Status = SCSendUnknownCommand(Irp,
|
|
DeviceExtension,
|
|
SciQuerySystemPowerHiberCallback,
|
|
&RequestIssued);
|
|
|
|
if (!RequestIssued) {
|
|
|
|
//
|
|
// could not send the unknown command down. show one fewer I/O
|
|
// pending and fall thru to generic handler.
|
|
//
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
Status = SCCompleteIrp(Irp,
|
|
STATUS_INSUFFICIENT_RESOURCES,
|
|
DeviceExtension);
|
|
}
|
|
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return Status;
|
|
} else
|
|
|
|
#endif //WIN9X_STREAM
|
|
|
|
if (DeviceExtension->RegistryFlags &
|
|
DEVICE_REG_FL_NO_SUSPEND_IF_RUNNING) {
|
|
|
|
|
|
PFILTER_INSTANCE FilterInstance;
|
|
KIRQL Irql;
|
|
PLIST_ENTRY FilterEntry,
|
|
FilterListHead;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
|
|
|
|
FilterListHead = FilterEntry = &DeviceExtension->FilterInstanceList;
|
|
|
|
while (FilterEntry->Flink != FilterListHead) {
|
|
|
|
FilterEntry = FilterEntry->Flink;
|
|
|
|
//
|
|
// follow the link to the instance
|
|
//
|
|
|
|
FilterInstance = CONTAINING_RECORD(FilterEntry,
|
|
FILTER_INSTANCE,
|
|
NextFilterInstance);
|
|
|
|
|
|
if (SCCheckIfStreamsRunning(FilterInstance)) {
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"POWER Query_Power FilterInstance %x busy\n",
|
|
FilterInstance ));
|
|
|
|
Status = STATUS_DEVICE_BUSY;
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
|
|
goto QuerySystemSuspendDone;
|
|
} // if streams running
|
|
//
|
|
// get the list entry for the next instance
|
|
//
|
|
|
|
FilterEntry = &FilterInstance->NextFilterInstance;
|
|
|
|
} // while local filter instances
|
|
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
|
|
|
|
} // if no suspend if running
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
|
|
QuerySystemSuspendDone:
|
|
|
|
//
|
|
// indicate we're ready for next power irp
|
|
//
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
|
|
case DevicePowerState:
|
|
|
|
switch (IrpStack->Parameters.Power.State.DeviceState) {
|
|
|
|
default:
|
|
case PowerDeviceD2:
|
|
case PowerDeviceD3:
|
|
|
|
//
|
|
// check to see if the device is opened.
|
|
//
|
|
if (!DeviceExtension->NumberOfOpenInstances) {
|
|
|
|
//
|
|
// show pending status and call next driver without a
|
|
// completion
|
|
// handler
|
|
//
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
} else {
|
|
|
|
//
|
|
// the device is opened. Don't do the power down.
|
|
//
|
|
Status = STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
}
|
|
|
|
default:
|
|
|
|
//
|
|
// unknown power type: indicate we're ready for next power irp
|
|
//
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (SCCompleteIrp(Irp, STATUS_NOT_SUPPORTED, DeviceExtension));
|
|
|
|
|
|
|
|
} // switch minorfunc
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
|
|
//
|
|
// presuppose good status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
switch (IrpStack->Parameters.Power.Type) {
|
|
|
|
case SystemPowerState:
|
|
|
|
if (DeviceExtension->Flags & DEVICE_FLAGS_PNP_STARTED) {
|
|
|
|
//
|
|
// Only care if the device is started.
|
|
// We depend on DE->ControlEvent being inited at SCStartWorker.
|
|
//
|
|
|
|
POWER_STATE PowerState;
|
|
SYSTEM_POWER_STATE RequestedSysState =
|
|
IrpStack->Parameters.Power.State.SystemState;
|
|
//
|
|
// look up the correct device power state in the table
|
|
//
|
|
|
|
PowerState.DeviceState =
|
|
DeviceExtension->DeviceState[RequestedSysState];
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"SCPower: DevObj %x S[%d]->D[%d]\n",
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
RequestedSysState,
|
|
PowerState.DeviceState));
|
|
|
|
//
|
|
// if this is a wakeup, we must first pass the request down
|
|
// to the PDO for preprocessing.
|
|
//
|
|
|
|
if (RequestedSysState == PowerSystemWorking) {
|
|
|
|
//
|
|
// Send down this S power IRP to the next layer and be
|
|
// done with it, except requesting D Irp in the following
|
|
// condition that related to the S Irp but does not reference
|
|
// it any further.
|
|
//
|
|
|
|
Status = SCSysWakeCallNextDriver(DeviceExtension, Irp);
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
//
|
|
// Nullify Irp, so at the D Irp completion, we dont complete this Irp.
|
|
// Be careful not to touch the Irp afterwards.
|
|
//
|
|
|
|
InterlockedDecrement(&DeviceExtension->OneBasedIoCount);
|
|
Irp = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Mark the S State.
|
|
//
|
|
SCSetCurrentSPowerState (DeviceExtension, RequestedSysState);
|
|
|
|
//
|
|
// take the event to avoid race.
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
if ((RequestedSysState == PowerSystemWorking) &&
|
|
(!DeviceExtension->NumberOfOpenInstances) &&
|
|
(DeviceExtension->RegistryFlags & DEVICE_REG_FL_POWER_DOWN_CLOSED)) {
|
|
|
|
// We are awakening from a suspend.
|
|
// we don't want to wake up the device at this
|
|
// point. We'll just wait til the first open
|
|
// occurs to wake it up.
|
|
//
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// Since there are no open instances, there can only be
|
|
// pended creates. Since we key device powerup off the
|
|
// creates, redispatch them now if there are any.
|
|
//
|
|
SCRedispatchPendedIrps (DeviceExtension, FALSE);
|
|
|
|
return Status;
|
|
|
|
} else { // if state = working
|
|
|
|
//
|
|
// now send down a set power based on this info.
|
|
//
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// per Pierre and Lonny, we should use D3 instead of the
|
|
// mapped array value, as the array value is always D0!
|
|
// of course, they'll change this next week...
|
|
//
|
|
|
|
if (RequestedSysState != PowerSystemWorking) {
|
|
|
|
PowerState.DeviceState = PowerDeviceD3;
|
|
|
|
}
|
|
DebugPrint((DebugLevelInfo,
|
|
"SCPower: PoRequestPowerIrp %x to state=%d\n",
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
PowerState));
|
|
|
|
//
|
|
// when (RequestedSysState == PowerSystemWorking) but
|
|
// (DeviceExtension->NumberOfOpenInstances) ||
|
|
// !(DeviceExtension->RegistryFlags & DEVICE_REG_FL_POWER_DOWN_CLOSED)
|
|
// we come here with Irp==NULL. Don't touch NULL Irp.
|
|
//
|
|
|
|
if ( NULL != Irp ) {
|
|
IoMarkIrpPending (Irp);
|
|
}
|
|
|
|
Status = PoRequestPowerIrp(DeviceExtension->PhysicalDeviceObject,
|
|
IRP_MN_SET_POWER,
|
|
PowerState,
|
|
SCSynchPowerCompletionRoutine,
|
|
Irp, // when NULL, it tells callback don't bother.
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS (Status) && NULL != Irp ) {
|
|
PoStartNextPowerIrp (Irp);
|
|
SCCompleteIrp (Irp, Status, DeviceExtension);
|
|
}
|
|
|
|
//
|
|
// The Irp has been marked pending. We MUST return
|
|
// pending. Error case will complete the Irp with the
|
|
// appropriate status.
|
|
//
|
|
return STATUS_PENDING;
|
|
|
|
} // if system state working
|
|
|
|
//
|
|
// if this is a NOT wakeup, we must first pass the request
|
|
// down to the PDO for postprocessing.
|
|
//
|
|
|
|
if (RequestedSysState != PowerSystemWorking) {
|
|
|
|
//
|
|
// send down the power IRP to the next layer. this
|
|
// routine
|
|
// has a completion routine which does not complete the
|
|
// IRP.
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugPrint((DebugLevelError, "'SCPower: PDO failed power request!\n"));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We have not started the device, don't bother.
|
|
// Besides, we can't use the DE->ControlEvent which is not
|
|
// inited yet in this case.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// indicate that we're ready for the next power IRP.
|
|
//
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// now complete the original request
|
|
//
|
|
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
|
|
// end of set system power state
|
|
|
|
case DevicePowerState:
|
|
|
|
{
|
|
|
|
DEVICE_POWER_STATE DeviceState;
|
|
DeviceState = IrpStack->Parameters.Power.State.DeviceState;
|
|
|
|
//
|
|
// if this is a power up, send the IRP down first to allow
|
|
// the PDO to preprocess it.
|
|
//
|
|
|
|
if (DeviceState == PowerDeviceD0) {
|
|
|
|
//
|
|
// Call down async or the Wakeup might dead lock.
|
|
// The subsequent work continues in the completion routine.
|
|
//
|
|
|
|
return SCDevWakeCallNextDriver(DeviceExtension, Irp);
|
|
}
|
|
//
|
|
// take the event to avoid race
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
//
|
|
// send down a set power SRB to the device.
|
|
// additional processing will be done by the callback
|
|
// procedure. This routine completes the IRP if it is able
|
|
// to issue the request.
|
|
//
|
|
|
|
Status = SCSubmitRequest(SRB_CHANGE_POWER_STATE,
|
|
(PVOID) DeviceState,
|
|
0,
|
|
SCPowerCallback,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->
|
|
MinidriverData->HwInitData.
|
|
HwReceivePacket
|
|
);
|
|
|
|
|
|
if (!RequestIssued) {
|
|
|
|
//
|
|
// If we fail to issue SRB, the SCPowerCallback won't happen.
|
|
// We need to carry out the power IRP processing here;
|
|
//
|
|
// set the new power state in the device extension.
|
|
//
|
|
SCSetCurrentDPowerState (DeviceExtension, DeviceState);
|
|
|
|
//
|
|
// send the Irp down to the next layer, and return that status
|
|
// as the final one.
|
|
//
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
}
|
|
}
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (Status);
|
|
|
|
} // case devicepowerstate
|
|
|
|
default:
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"StreamPower: unknown function %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// unknown function, so call it down to the minidriver as such.
|
|
// this routine completes the IRP if it is able to issue the request.
|
|
//
|
|
|
|
Status = SCSendUnknownCommand(Irp,
|
|
DeviceExtension,
|
|
SCUnknownPowerCallback,
|
|
&RequestIssued);
|
|
|
|
if (!RequestIssued) {
|
|
//
|
|
// could not send the unknown command down. show one fewer I/O
|
|
// pending and fall thru to generic handler.
|
|
//
|
|
PoStartNextPowerIrp(Irp);
|
|
Status = SCCompleteIrp(Irp, STATUS_INSUFFICIENT_RESOURCES, DeviceExtension);
|
|
}
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
return (Status);
|
|
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
SCPNPQueryCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the completion of an PNP Query Stop/Remove command.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
NTSTATUS Status, MiniStatus;
|
|
|
|
//
|
|
// delete the SRB
|
|
//
|
|
|
|
MiniStatus = SCDequeueAndDeleteSrb(SRB);
|
|
|
|
//
|
|
// IRP_MJ_PnP, IRP_MJ_POWER and IRP_MJ_SYSTEM_CONTROL
|
|
// are supposed to traverse the whole device stack unless
|
|
// it is to be failed right here.
|
|
// It should have been STATUS_NOT_SUUPORTED ||
|
|
// NT_SUCCESS( Status ), add STATUS_NOT_IMPLEMENTED as
|
|
// there are some mini drivers return it which should
|
|
// have been STATUS_NOT_SUPPORTED
|
|
//
|
|
|
|
if ( STATUS_NOT_IMPLEMENTED == MiniStatus ) {
|
|
MiniStatus = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( STATUS_NOT_SUPPORTED == MiniStatus ||
|
|
NT_SUCCESS( MiniStatus ) ) {
|
|
|
|
//
|
|
// Mini driver did not explicitly failed this, passs down the Irp
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
if ( Status == STATUS_NOT_SUPPORTED ) {
|
|
//
|
|
// noone below knows/cares. Use our mini status
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
}
|
|
|
|
else {
|
|
//
|
|
// mini driver explcitly failed this Irp, use MiniStatus
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
//
|
|
// query is vetoed, reset the INACCESSIBLE flag
|
|
//
|
|
KIRQL Irql;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
|
|
}
|
|
|
|
//
|
|
// complete the IRP with the final status
|
|
//
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCUnknownPNPCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the completion of an unknown PNP command.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
NTSTATUS Status, MiniStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// delete the SRB
|
|
//
|
|
|
|
MiniStatus = SCDequeueAndDeleteSrb(SRB);
|
|
|
|
//
|
|
// IRP_MJ_PnP, IRP_MJ_POWER and IRP_MJ_SYSTEM_CONTROL
|
|
// are supposed to traverse the whole device stack unless
|
|
// it is to be failed right here.
|
|
// It should have been STATUS_NOT_SUUPORTED ||
|
|
// NT_SUCCESS( Status ), add STATUS_NOT_IMPLEMENTED as
|
|
// there are some mini drivers return it which should
|
|
// have been STATUS_NOT_SUPPORTED
|
|
//
|
|
|
|
if ( STATUS_NOT_IMPLEMENTED == MiniStatus ) {
|
|
MiniStatus = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( STATUS_NOT_SUPPORTED == MiniStatus ||
|
|
NT_SUCCESS( MiniStatus ) ) {
|
|
|
|
//
|
|
// Mini driver did not explicitly failed this, passs down the Irp
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
if ( Status == STATUS_NOT_SUPPORTED ) {
|
|
//
|
|
// noone below knows/cares. Use our mini status
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
}
|
|
|
|
else {
|
|
//
|
|
// mini driver explcitly failed this Irp, use MiniStatus
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
|
|
//
|
|
// complete the IRP with the final status
|
|
//
|
|
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCUnknownPowerCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the completion of an unknown PNP command.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
NTSTATUS Status, MiniStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// delete the SRB
|
|
//
|
|
|
|
MiniStatus = SCDequeueAndDeleteSrb(SRB);
|
|
|
|
if ( STATUS_NOT_IMPLEMENTED == MiniStatus ) {
|
|
MiniStatus = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( STATUS_NOT_SUPPORTED == MiniStatus ||
|
|
NT_SUCCESS( MiniStatus )) {
|
|
|
|
//
|
|
// it is not explicitly failed by the mini driver pass down the Irp
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
if ( Status == STATUS_NOT_SUPPORTED ) {
|
|
//
|
|
// noone below knows/cares. Use our mini status
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
}
|
|
|
|
else {
|
|
//
|
|
// mini driver explicitly failed this
|
|
//
|
|
Status = MiniStatus;
|
|
}
|
|
//
|
|
// complete the IRP with the final status
|
|
//
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
}
|
|
|
|
NTSTATUS
|
|
SCQueryWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IRP completion handler for querying removal of the hardware
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object
|
|
Irp - pointer to Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
KIRQL Irql;
|
|
|
|
//
|
|
// if the query did not succeed, reenable the device.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
//
|
|
// clear the inaccessible bit.
|
|
//
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
|
|
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_DEVICE_INACCESSIBLE;
|
|
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
|
|
|
|
}
|
|
return (SCCompleteIrp(Irp, Irp->IoStatus.Status, DeviceExtension));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCStartWorker(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Passive level routine to process starting the hardware.
|
|
|
|
Arguments:
|
|
|
|
Irp - pointer to Irp
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_OBJECT DeviceObject = IrpStack->DeviceObject;
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInfo;
|
|
PHW_INITIALIZATION_DATA HwInitData;
|
|
PCM_RESOURCE_LIST ResourceList;
|
|
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
|
|
KAFFINITY affinity;
|
|
PVOID Buffer;
|
|
PACCESS_RANGE pAccessRanges = NULL;
|
|
ULONG CurrentRange = 0;
|
|
BOOLEAN interruptSharable = TRUE;
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
ULONG numberOfMapRegisters;
|
|
ULONG DmaBufferSize;
|
|
ULONG i;
|
|
PHYSICAL_ADDRESS TranslatedAddress;
|
|
NTSTATUS Status = Irp->IoStatus.Status;
|
|
BOOLEAN RequestIssued;
|
|
INTERFACE_TYPE InterfaceBuffer;
|
|
ULONG InterfaceLength;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// continue processing if we got good status from our parent.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
HwInitData = &(DeviceExtension->MinidriverData->HwInitData);
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"SCPNPStartWorker %x\n",
|
|
DeviceObject));
|
|
|
|
//
|
|
// Initialize spin lock for critical sections.
|
|
//
|
|
|
|
KeInitializeSpinLock(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// initialize a worker DPC for this device
|
|
//
|
|
|
|
KeInitializeDpc(&DeviceExtension->WorkDpc,
|
|
StreamClassDpc,
|
|
DeviceObject);
|
|
//
|
|
// initialize the control and remove events for this device
|
|
//
|
|
// move this to AddDevice, we use the control event at Remove_device
|
|
// which can come in before the device starts.
|
|
// KeInitializeEvent(&DeviceExtension->ControlEvent,
|
|
// SynchronizationEvent,
|
|
// TRUE);
|
|
|
|
KeInitializeEvent(&DeviceExtension->RemoveEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Initialize minidriver timer and timer DPC for this stream
|
|
//
|
|
|
|
KeInitializeTimer(&DeviceExtension->ComObj.MiniDriverTimer);
|
|
KeInitializeDpc(&DeviceExtension->ComObj.MiniDriverTimerDpc,
|
|
SCMinidriverDeviceTimerDpc,
|
|
DeviceExtension);
|
|
|
|
//
|
|
// retrieve the resources for the device
|
|
//
|
|
|
|
ResourceList = IrpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
|
|
|
|
//
|
|
// allocate space for the config info structure.
|
|
//
|
|
|
|
ConfigInfo = ExAllocatePool(NonPagedPool,
|
|
sizeof(PORT_CONFIGURATION_INFORMATION)
|
|
);
|
|
|
|
|
|
if (ConfigInfo == NULL) {
|
|
|
|
DebugPrint((DebugLevelFatal, "StreamClassPNP: ConfigInfo alloc failed."));
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
DebugPrint((DebugLevelVerbose, "StreamClassPNP: ConfigInfo = %x\n", ConfigInfo));
|
|
|
|
RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION));
|
|
|
|
DeviceExtension->ConfigurationInformation = ConfigInfo;
|
|
|
|
//
|
|
// fill in the ConfigInfo fields we know about.
|
|
//
|
|
|
|
ConfigInfo->SizeOfThisPacket = sizeof(PORT_CONFIGURATION_INFORMATION);
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// make sure that the minidriver handles receiving a bigger structure
|
|
// so we can expand it later
|
|
//
|
|
|
|
ConfigInfo->SizeOfThisPacket *= ConfigInfo->SizeOfThisPacket;
|
|
#endif
|
|
|
|
//
|
|
// set the callable PDO in the configinfo structure
|
|
//
|
|
|
|
ConfigInfo->PhysicalDeviceObject = DeviceExtension->AttachedPdo;
|
|
ConfigInfo->RealPhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject;
|
|
|
|
ConfigInfo->BusInterruptVector = MP_UNINITIALIZED_VALUE;
|
|
ConfigInfo->InterruptMode = Latched;
|
|
ConfigInfo->DmaChannel = MP_UNINITIALIZED_VALUE;
|
|
ConfigInfo->Irp = Irp;
|
|
|
|
//
|
|
// Now we get to chew thru the resources the OS found for us, if any.
|
|
//
|
|
|
|
if (ResourceList) {
|
|
|
|
FullResourceDescriptor = &ResourceList->List[0];
|
|
|
|
PartialResourceList = &FullResourceDescriptor->PartialResourceList;
|
|
|
|
//
|
|
// fill in the bus # and interface type based on the device
|
|
// properties
|
|
// for the PDO. default to InterfaceTypeUndefined if
|
|
// failure to retrieve interface type (if the miniport tries to
|
|
// use
|
|
// this value when filling in DEVICE_DESCRIPTION.InterfaceType
|
|
// for
|
|
// calling IoGetDmaAdapter, the right thing will happen, since
|
|
// PnP
|
|
// will automatically pick the correct legacy bus in the system
|
|
// (ISA or MCA).
|
|
//
|
|
|
|
if (!NT_SUCCESS(
|
|
IoGetDeviceProperty(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
DevicePropertyBusNumber,
|
|
sizeof(ULONG),
|
|
(PVOID) & (ConfigInfo->SystemIoBusNumber),
|
|
&InterfaceLength))) {
|
|
//
|
|
// Couldn't retrieve bus number property--assume bus zero.
|
|
//
|
|
ConfigInfo->SystemIoBusNumber = 0;
|
|
}
|
|
if (NT_SUCCESS(
|
|
IoGetDeviceProperty(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
DevicePropertyLegacyBusType,
|
|
sizeof(INTERFACE_TYPE),
|
|
&InterfaceBuffer,
|
|
&InterfaceLength))) {
|
|
|
|
|
|
ASSERT(InterfaceLength == sizeof(INTERFACE_TYPE));
|
|
ConfigInfo->AdapterInterfaceType = InterfaceBuffer;
|
|
|
|
} else { // if success
|
|
//
|
|
// Couldn't retrieve bus interface type--initialize to
|
|
// InterfaceTypeUndefined.
|
|
//
|
|
ConfigInfo->AdapterInterfaceType = InterfaceTypeUndefined;
|
|
|
|
} // if success
|
|
|
|
|
|
//
|
|
// allocate space for access ranges. We use the Count field
|
|
// in the resource list for determining this size, as the count
|
|
// will be >= the max # of ranges we will need.
|
|
//
|
|
|
|
if (PartialResourceList->Count) {
|
|
|
|
pAccessRanges = ExAllocatePool(NonPagedPool,
|
|
sizeof(ACCESS_RANGE) *
|
|
PartialResourceList->Count
|
|
);
|
|
|
|
if (pAccessRanges == NULL) {
|
|
|
|
DebugPrint((DebugLevelFatal,
|
|
"StreamClassPNP: No pool for global info"));
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
SCFreeAllResources(DeviceExtension);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
} // if count
|
|
|
|
//
|
|
// Stash the AccessRanges structure at this time so that
|
|
// SCFreeAllResources will free it on resource failures below.
|
|
//
|
|
ConfigInfo->AccessRanges = pAccessRanges;
|
|
|
|
//
|
|
// Now update the port configuration info structure by looping
|
|
// thru the config
|
|
//
|
|
|
|
for (i = 0; i < PartialResourceList->Count; i++) {
|
|
|
|
switch (PartialResourceList->PartialDescriptors[i].Type) {
|
|
|
|
case CmResourceTypePort:
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassPnP: Port Resources Found at %x, Length %x\n",
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Start,
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Length));
|
|
|
|
//
|
|
// translate the bus address for the minidriver
|
|
//
|
|
|
|
TranslatedAddress = PartialResourceList->PartialDescriptors[i].u.Port.Start;
|
|
|
|
//
|
|
// set the access range in the structure.
|
|
//
|
|
|
|
pAccessRanges[CurrentRange].RangeStart = TranslatedAddress;
|
|
|
|
pAccessRanges[CurrentRange].RangeLength =
|
|
PartialResourceList->
|
|
PartialDescriptors[i].u.Port.Length;
|
|
|
|
pAccessRanges[CurrentRange++].RangeInMemory =
|
|
FALSE;
|
|
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassPnP: Interrupt Resources Found! Level = %x Vector = %x\n",
|
|
PartialResourceList->PartialDescriptors[i].u.Interrupt.Level,
|
|
PartialResourceList->PartialDescriptors[i].u.Interrupt.Vector));
|
|
|
|
//
|
|
// Set the interrupt vector in the config info
|
|
//
|
|
|
|
ConfigInfo->BusInterruptVector = PartialResourceList->PartialDescriptors[i].u.Interrupt.Vector;
|
|
|
|
;
|
|
affinity = PartialResourceList->PartialDescriptors[i].u.Interrupt.Affinity;
|
|
|
|
ConfigInfo->BusInterruptLevel = (ULONG) PartialResourceList->PartialDescriptors[i].u.Interrupt.Level;
|
|
|
|
ConfigInfo->InterruptMode = PartialResourceList->PartialDescriptors[i].Flags;
|
|
|
|
//
|
|
// Go to next resource for this Adapter
|
|
//
|
|
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
|
|
//
|
|
// translate the bus address for the minidriver
|
|
//
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassPnP: Memory Resources Found @ %x'%x, Length = %x\n",
|
|
PartialResourceList->PartialDescriptors[i].u.Memory.Start.HighPart,
|
|
PartialResourceList->PartialDescriptors[i].u.Memory.Start.LowPart,
|
|
PartialResourceList->PartialDescriptors[i].u.Memory.Length));
|
|
|
|
|
|
TranslatedAddress = PartialResourceList->PartialDescriptors[i].u.Memory.Start;
|
|
|
|
if (!SCMapMemoryAddress(&pAccessRanges[CurrentRange++],
|
|
TranslatedAddress,
|
|
ConfigInfo,
|
|
DeviceExtension,
|
|
ResourceList,
|
|
&PartialResourceList->
|
|
PartialDescriptors[i])) {
|
|
|
|
SCFreeAllResources(DeviceExtension);
|
|
Status = STATUS_CONFLICTING_ADDRESSES;
|
|
goto exit;
|
|
|
|
} // if !scmapmemoryaddress
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // if resources
|
|
//
|
|
// reference the access range structure to the
|
|
// config info structure & the ConfigInfo structure to the
|
|
// device extension & indicate # of ranges.
|
|
//
|
|
|
|
ConfigInfo->NumberOfAccessRanges = CurrentRange;
|
|
|
|
//
|
|
// Determine if a Dma Adapter must be allocated.
|
|
//
|
|
|
|
DmaBufferSize = HwInitData->DmaBufferSize;
|
|
|
|
if ((HwInitData->BusMasterDMA) || (DmaBufferSize)) {
|
|
|
|
//
|
|
// Get the adapter object for this card.
|
|
//
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassPnP: Allocating DMA adapter\n"));
|
|
|
|
RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));
|
|
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
|
|
deviceDescription.DmaChannel = ConfigInfo->DmaChannel;
|
|
deviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType;
|
|
deviceDescription.DmaWidth = Width32Bits;
|
|
deviceDescription.DmaSpeed = Compatible;
|
|
deviceDescription.ScatterGather = TRUE;
|
|
deviceDescription.Master = TRUE;
|
|
deviceDescription.Dma32BitAddresses = !(HwInitData->Dma24BitAddresses);
|
|
deviceDescription.AutoInitialize = FALSE;
|
|
deviceDescription.MaximumLength = (ULONG) - 1;
|
|
|
|
DeviceExtension->DmaAdapterObject = IoGetDmaAdapter(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
&deviceDescription,
|
|
&numberOfMapRegisters
|
|
);
|
|
ASSERT(DeviceExtension->DmaAdapterObject);
|
|
|
|
//
|
|
// Set maximum number of pages
|
|
//
|
|
|
|
DeviceExtension->NumberOfMapRegisters = numberOfMapRegisters;
|
|
|
|
//
|
|
// expose the object to the minidriver
|
|
//
|
|
|
|
ConfigInfo->DmaAdapterObject = DeviceExtension->DmaAdapterObject;
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// no DMA adapter object. show unlimited map registers so
|
|
// we won't have to do a real time check later for DMA.
|
|
//
|
|
|
|
DeviceExtension->NumberOfMapRegisters = -1;
|
|
|
|
}
|
|
|
|
if (DmaBufferSize) {
|
|
|
|
Buffer = HalAllocateCommonBuffer(DeviceExtension->DmaAdapterObject,
|
|
DmaBufferSize,
|
|
&DeviceExtension->DmaBufferPhysical,
|
|
FALSE);
|
|
|
|
if (Buffer == NULL) {
|
|
DEBUG_BREAKPOINT();
|
|
DebugPrint((DebugLevelFatal, "StreamClassPnPStart: Could not alloc buffer, size: %d\n", DmaBufferSize));
|
|
SCFreeAllResources(DeviceExtension);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
//
|
|
// zero init the common buffer.
|
|
//
|
|
|
|
RtlZeroMemory(Buffer, DmaBufferSize);
|
|
|
|
//
|
|
// save virtual address of buffer
|
|
//
|
|
|
|
DeviceExtension->DmaBuffer = Buffer;
|
|
DeviceExtension->DmaBufferLength = DmaBufferSize; // osr#99489
|
|
|
|
} // if DMA buffer
|
|
//
|
|
// Performance Improvement chance
|
|
// - on rebalance, the uninitialize handler clears the sync
|
|
// vector when the interrupt is disconnected, but since we
|
|
// initialized this vector ONLY at AddDevice time, it wasn't getting
|
|
// reset correctly since only a new start (and not an adddevice) is
|
|
// sent on a rebalance. the correct fix is to move all of the
|
|
// initial vector setting to here, but I'm worried that there could
|
|
// be a case where if they aren't set up on the adddevice we could
|
|
// reference a null. So, I've duplicated the following few lines to
|
|
// reset the vector here. For code savings, this should be done
|
|
// only in one place.
|
|
//
|
|
|
|
//
|
|
// presuppose full synch
|
|
//
|
|
|
|
#if DBG
|
|
DeviceExtension->SynchronizeExecution = SCDebugKeSynchronizeExecution;
|
|
#else
|
|
DeviceExtension->SynchronizeExecution = KeSynchronizeExecution;
|
|
#endif
|
|
|
|
if (DeviceExtension->NoSync) {
|
|
|
|
//
|
|
// we won't do synchronization, so use the dummy sync routine.
|
|
//
|
|
|
|
DeviceExtension->SynchronizeExecution = StreamClassSynchronizeExecution;
|
|
DeviceExtension->InterruptObject = (PVOID) DeviceExtension;
|
|
|
|
}
|
|
//
|
|
// see if the driver has an interrupt, and process if so.
|
|
//
|
|
|
|
if (HwInitData->HwInterrupt == NULL ||
|
|
(ConfigInfo->BusInterruptLevel == 0 &&
|
|
ConfigInfo->BusInterruptVector == 0)) {
|
|
|
|
//
|
|
// There is no interrupt so use the dummy sync routine.
|
|
//
|
|
|
|
DeviceExtension->SynchronizeExecution = StreamClassSynchronizeExecution;
|
|
DeviceExtension->InterruptObject = (PVOID) DeviceExtension;
|
|
|
|
DebugPrint((1, "'StreamClassInitialize: Adapter has no interrupt.\n"));
|
|
|
|
} else {
|
|
|
|
DebugPrint((1,
|
|
"'StreamClassInitialize: STREAM adapter IRQ is %d\n",
|
|
ConfigInfo->BusInterruptLevel));
|
|
|
|
//
|
|
// Set up for a real interrupt.
|
|
//
|
|
|
|
Status = IoConnectInterrupt(
|
|
&DeviceExtension->InterruptObject,
|
|
StreamClassInterrupt,
|
|
DeviceObject,
|
|
(PKSPIN_LOCK) NULL,
|
|
ConfigInfo->BusInterruptVector,
|
|
(UCHAR) ConfigInfo->BusInterruptLevel,
|
|
(UCHAR) ConfigInfo->BusInterruptLevel,
|
|
ConfigInfo->InterruptMode,
|
|
interruptSharable,
|
|
affinity,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugPrint((1, "'SCStartWorker: Can't connect interrupt %d\n",
|
|
ConfigInfo->BusInterruptLevel));
|
|
DeviceExtension->InterruptObject = NULL;
|
|
SCFreeAllResources(DeviceExtension);
|
|
goto exit;
|
|
}
|
|
//
|
|
// set the interrupt object for the minidriver
|
|
//
|
|
|
|
ConfigInfo->InterruptObject = DeviceExtension->InterruptObject;
|
|
|
|
}
|
|
|
|
//
|
|
// point the config info structure to the device extension &
|
|
// device object as
|
|
// we can only pass in one context value to KeSync....
|
|
//
|
|
|
|
ConfigInfo->HwDeviceExtension =
|
|
DeviceExtension->HwDeviceExtension;
|
|
|
|
ConfigInfo->ClassDeviceObject = DeviceObject;
|
|
|
|
//
|
|
// Start timer.
|
|
//
|
|
|
|
IoStartTimer(DeviceObject);
|
|
|
|
//
|
|
// the ConfigInfo structure is filled in and the IRQ hooked.
|
|
// call the minidriver to find the specified adapter.
|
|
//
|
|
|
|
//
|
|
// initialize the device extension queues
|
|
//
|
|
|
|
InitializeListHead(&DeviceExtension->PendingQueue);
|
|
InitializeListHead(&DeviceExtension->OutstandingQueue);
|
|
|
|
/// move to add device, we could have child PDO if we start and stop
|
|
///InitializeListHead(&DeviceExtension->Children);
|
|
InitializeListHead(&DeviceExtension->DeadEventList);
|
|
IFN_MF(InitializeListHead(&DeviceExtension->NotifyList);)
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->EventWorkItem,
|
|
SCFreeDeadEvents,
|
|
DeviceExtension);
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->RescanWorkItem,
|
|
SCRescanStreams,
|
|
DeviceExtension);
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->PowerCompletionWorkItem,
|
|
SCPowerCompletionWorker,
|
|
DeviceExtension);
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->DevIrpCompletionWorkItem,
|
|
SCDevIrpCompletionWorker,
|
|
DeviceExtension);
|
|
|
|
|
|
//
|
|
// show that the device is ready for its first request.
|
|
//
|
|
|
|
DeviceExtension->ReadyForNextReq = TRUE;
|
|
|
|
//
|
|
// submit the initialize command.
|
|
// additional processing will be done by the callback procedure.
|
|
//
|
|
|
|
Status = SCSubmitRequest(
|
|
SRB_INITIALIZE_DEVICE,
|
|
ConfigInfo,
|
|
sizeof(PORT_CONFIGURATION_INFORMATION),
|
|
SCInitializeCallback,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->MinidriverData->HwInitData.HwReceivePacket
|
|
);
|
|
|
|
//
|
|
// If the device failed to start then set the error and return.
|
|
//
|
|
|
|
if (!RequestIssued) {
|
|
|
|
DebugPrint((DebugLevelFatal, "StreamClassPnP: Adapter not found\n"));
|
|
|
|
SCFreeAllResources(DeviceExtension);
|
|
goto exit;
|
|
}
|
|
}
|
|
return (Status);
|
|
|
|
exit:
|
|
return (SCCompleteIrp(Irp, Status, DeviceExtension));
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCInitializeCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the minidriver's stream info structure.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHW_STREAM_DESCRIPTOR StreamBuffer;
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PDEVICE_OBJECT DeviceObject = DeviceExtension->DeviceObject;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInfo =
|
|
SRB->HwSRB.CommandData.ConfigInfo;
|
|
BOOLEAN RequestIssued;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (NT_SUCCESS(SRB->HwSRB.Status)) {
|
|
|
|
DebugPrint((DebugLevelVerbose, "'Stream: returned from HwInitialize\n"));
|
|
|
|
//
|
|
// send an SRB to retrieve the stream information
|
|
//
|
|
|
|
ASSERT(ConfigInfo->StreamDescriptorSize);
|
|
|
|
StreamBuffer =
|
|
ExAllocatePool(NonPagedPool,
|
|
ConfigInfo->StreamDescriptorSize
|
|
);
|
|
|
|
if (!StreamBuffer) {
|
|
|
|
SCUninitializeMinidriver(DeviceObject, Irp);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
//
|
|
// zero-init the buffer
|
|
//
|
|
|
|
RtlZeroMemory(StreamBuffer, ConfigInfo->StreamDescriptorSize);
|
|
|
|
|
|
//
|
|
// submit the command.
|
|
// additional processing will be done by the callback
|
|
// procedure.
|
|
//
|
|
|
|
Status = SCSubmitRequest(SRB_GET_STREAM_INFO,
|
|
StreamBuffer,
|
|
ConfigInfo->StreamDescriptorSize,
|
|
SCStreamInfoCallback,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->MinidriverData->HwInitData.HwReceivePacket
|
|
);
|
|
|
|
if (!RequestIssued) {
|
|
|
|
ExFreePool(StreamBuffer);
|
|
SCUninitializeMinidriver(DeviceObject, Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// If the device failed to start then set the error and
|
|
// return.
|
|
//
|
|
|
|
DebugPrint((DebugLevelFatal, "StreamClassPnP: Adapter not found\n"));
|
|
SCFreeAllResources(DeviceExtension);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
|
|
//
|
|
// dequeue and delete the SRB for initialize. Null out the IRP field
|
|
// so the dequeue routine won't try to access it, as it has been freed.
|
|
//
|
|
|
|
SRB->HwSRB.Irp = NULL;
|
|
SCDequeueAndDeleteSrb(SRB);
|
|
return (Status);
|
|
|
|
}
|
|
|
|
|
|
#if ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
PUNICODE_STRING
|
|
SciCreateSymbolicLinks(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN ULONG FilterTypeIndex,
|
|
IN PHW_STREAM_HEADER StreamHeader)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create symbolic links for all categories in the Topology of
|
|
one filter type so that clients can find them.
|
|
The Symbolic link array is kept in the FilterType so that
|
|
they can be released later.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtenion: The device instance.
|
|
FiltertypeIndex: The filter type to create symbolic links.
|
|
StreamHeader: Go thru the categories in the Topology.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
LPGUID GuidIndex = (LPGUID)StreamHeader->Topology->Categories;
|
|
ULONG ArrayCount = StreamHeader->Topology->CategoriesCount;
|
|
PUNICODE_STRING NamesArray;
|
|
ULONG i,j;
|
|
HANDLE ClassHandle, PdoHandle=NULL; // prefixbug 17135
|
|
UNICODE_STRING TempUnicodeString;
|
|
PVOID DataBuffer[MAX_STRING_LENGTH];
|
|
//ULONG NumberOfFilterTypes;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_DEVICE_EXTENSION( DeviceExtension );
|
|
|
|
//
|
|
// allocate space for the array of catagory names
|
|
//
|
|
NamesArray = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING) * ArrayCount);
|
|
if ( NULL == NamesArray ) {
|
|
DEBUG_BREAKPOINT();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// zero the array in case we're unable to fill it in below. the Destroy
|
|
// routine below will then correctly handle this case.
|
|
//
|
|
|
|
RtlZeroMemory(NamesArray, sizeof(UNICODE_STRING) * ArrayCount);
|
|
|
|
//
|
|
// open the PDO
|
|
//
|
|
|
|
Status = IoOpenDeviceRegistryKey(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
STANDARD_RIGHTS_ALL,
|
|
&PdoHandle);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't open Pdo\n"));
|
|
PdoHandle = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// loop through each of the catagory GUID's for each of the pins,
|
|
// creating a symbolic link for each one.
|
|
//
|
|
|
|
for (i = 0; i < ArrayCount; i++) {
|
|
//
|
|
// Create the symbolic link for each category
|
|
//
|
|
PKSOBJECT_CREATE_ITEM CreateItem;
|
|
|
|
CreateItem = &DeviceExtension->CreateItems[FilterTypeIndex];
|
|
|
|
DebugPrint((DebugLevelVerbose,
|
|
"RegisterDeviceInterface FType %d,"
|
|
"CreateItemName=%S\n",
|
|
FilterTypeIndex,
|
|
CreateItem->ObjectClass.Buffer));
|
|
|
|
Status = IoRegisterDeviceInterface(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
&GuidIndex[i],
|
|
(PUNICODE_STRING) &CreateItem->ObjectClass,
|
|
&NamesArray[i]);
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
//
|
|
// Can't register device interface
|
|
//
|
|
DebugPrint((DebugLevelError,
|
|
"StreamCreateSymLinks: couldn't register\n"));
|
|
DEBUG_BREAKPOINT();
|
|
goto Exit;
|
|
}
|
|
|
|
DebugPrint((DebugLevelVerbose,
|
|
"SymbolicLink:%S\n",
|
|
NamesArray[i].Buffer));
|
|
//
|
|
// Now set the symbolic link for the association
|
|
//
|
|
Status = IoSetDeviceInterfaceState(&NamesArray[i], TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// unsuccessful
|
|
//
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't set\n"));
|
|
DEBUG_BREAKPOINT();
|
|
goto Exit;
|
|
}
|
|
//
|
|
// add the strings from the PDO's key to the association key.
|
|
// Performance Improvement Chance
|
|
// - the INF should be able to directly propogate these;
|
|
// forrest & lonny are fixing.
|
|
//
|
|
|
|
Status = IoOpenDeviceInterfaceRegistryKey(&NamesArray[i],
|
|
STANDARD_RIGHTS_ALL,
|
|
&ClassHandle);
|
|
if ( !NT_SUCCESS( Status )) {
|
|
//
|
|
// unsuccessful open Class interface
|
|
//
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't set\n"));
|
|
DEBUG_BREAKPOINT();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// write the class ID for the proxy, if any.
|
|
//
|
|
Status = SCGetRegistryValue(PdoHandle,
|
|
(PWCHAR) ClsIdString,
|
|
sizeof(ClsIdString),
|
|
DataBuffer,
|
|
MAX_STRING_LENGTH);
|
|
|
|
if ( NT_SUCCESS(Status) ){
|
|
//
|
|
// write the class ID for the proxy
|
|
//
|
|
RtlInitUnicodeString(&TempUnicodeString, ClsIdString);
|
|
|
|
ZwSetValueKey(ClassHandle,
|
|
&TempUnicodeString,
|
|
0,
|
|
REG_SZ,
|
|
DataBuffer,
|
|
MAX_STRING_LENGTH);
|
|
} // if cls guid read
|
|
//
|
|
// first check if a friendly name has already been propogated
|
|
// to the class via the INF. If not, we'll just use the device
|
|
// description string for this.
|
|
//
|
|
Status = SCGetRegistryValue(ClassHandle,
|
|
(PWCHAR) FriendlyNameString,
|
|
sizeof(FriendlyNameString),
|
|
DataBuffer,
|
|
MAX_STRING_LENGTH);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// friendly name non-exists yet.
|
|
// write the friendly name for the device, if any.
|
|
//
|
|
|
|
Status = SCGetRegistryValue(PdoHandle,
|
|
(PWCHAR) DriverDescString,
|
|
sizeof(DriverDescString),
|
|
DataBuffer,
|
|
MAX_STRING_LENGTH);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
//
|
|
// driver descrption string available, use it.
|
|
//
|
|
RtlInitUnicodeString(&TempUnicodeString, FriendlyNameString);
|
|
|
|
ZwSetValueKey(ClassHandle,
|
|
&TempUnicodeString,
|
|
0,
|
|
REG_SZ,
|
|
DataBuffer,
|
|
MAX_STRING_LENGTH);
|
|
|
|
|
|
}
|
|
}
|
|
ZwClose(ClassHandle);
|
|
|
|
} // for # Categories
|
|
|
|
//
|
|
// If we reach here, consider as successful.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Exit: {
|
|
if ( NULL != PdoHandle ) {
|
|
ZwClose(PdoHandle);
|
|
}
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
if ( NULL != NamesArray ) {
|
|
ExFreePool( NamesArray );
|
|
NamesArray = NULL;
|
|
}
|
|
}
|
|
return NamesArray;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SciOnFilterStreamDescriptor(
|
|
PFILTER_INSTANCE FilterInstance,
|
|
PHW_STREAM_DESCRIPTOR StreamDescriptor)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the minidriver's stream descriptor structure.
|
|
This is used for one FilterType specific streams.
|
|
|
|
Arguments:
|
|
|
|
FilterInstance: The one that we are to process for.
|
|
StreamDescriptor: Point to the descriptor to process for the filter.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG NumberOfPins, i;
|
|
PKSPIN_DESCRIPTOR PinDescs = NULL;
|
|
PHW_STREAM_INFORMATION CurrentInfo;
|
|
ULONG PinSize;
|
|
PSTREAM_ADDITIONAL_INFO NewStreamArray;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
NumberOfPins = StreamDescriptor->StreamHeader.NumberOfStreams;
|
|
|
|
DebugPrint((DebugLevelVerbose,
|
|
"Parsing StreamInfo Pins=%x\n", NumberOfPins ));
|
|
|
|
if (StreamDescriptor->StreamHeader.SizeOfHwStreamInformation <
|
|
sizeof(HW_STREAM_INFORMATION)) {
|
|
|
|
DebugPrint((DebugLevelError, "minidriver stream info too small!"));
|
|
|
|
DEBUG_BREAKPOINT();
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (NumberOfPins) {
|
|
//
|
|
// parse the minidriver's info into CSA format to build the
|
|
// mother of all structures.
|
|
//
|
|
|
|
PinSize = (sizeof(KSPIN_DESCRIPTOR) + sizeof(STREAM_ADDITIONAL_INFO))*
|
|
NumberOfPins;
|
|
|
|
PinDescs = ExAllocatePool(NonPagedPool, PinSize);
|
|
if (PinDescs == NULL) {
|
|
DebugPrint((DebugLevelError, "Stream: No pool for stream info"));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
RtlZeroMemory(PinDescs, PinSize);
|
|
|
|
//
|
|
// we need a new array to hold the new copies of the
|
|
// stream properties and events which are allocated below.
|
|
//
|
|
|
|
NewStreamArray = (PSTREAM_ADDITIONAL_INFO)
|
|
((PBYTE) PinDescs + sizeof(KSPIN_DESCRIPTOR) * NumberOfPins);
|
|
|
|
FilterInstance->StreamPropEventArray = NewStreamArray;
|
|
|
|
CurrentInfo = &StreamDescriptor->StreamInfo;
|
|
|
|
for (i = 0; i < StreamDescriptor->StreamHeader.NumberOfStreams; i++) {
|
|
//
|
|
// process each pin info
|
|
//
|
|
|
|
PinDescs[i].InterfacesCount = SIZEOF_ARRAY(PinInterfaces);
|
|
PinDescs[i].Interfaces = PinInterfaces;
|
|
|
|
//
|
|
// use default medium if minidriver does not specify
|
|
//
|
|
if (CurrentInfo->MediumsCount) {
|
|
PinDescs[i].MediumsCount = CurrentInfo->MediumsCount;
|
|
PinDescs[i].Mediums = CurrentInfo->Mediums;
|
|
|
|
}
|
|
else {
|
|
PinDescs[i].MediumsCount = SIZEOF_ARRAY(PinMediums);
|
|
PinDescs[i].Mediums = PinMediums;
|
|
}
|
|
|
|
//
|
|
// set the # of data format blocks
|
|
//
|
|
|
|
PinDescs[i].DataRangesCount =
|
|
CurrentInfo->NumberOfFormatArrayEntries;
|
|
|
|
//
|
|
// point to the data format blocks for the pin
|
|
//
|
|
|
|
PinDescs[i].DataRanges = CurrentInfo->StreamFormatsArray;
|
|
|
|
//
|
|
// set the data flow direction
|
|
//
|
|
|
|
PinDescs[i].DataFlow = (KSPIN_DATAFLOW) CurrentInfo->DataFlow;
|
|
|
|
//
|
|
// set the communication field
|
|
//
|
|
|
|
if (CurrentInfo->BridgeStream) {
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_BRIDGE;
|
|
}
|
|
else {
|
|
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_BOTH;
|
|
#else
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_SINK;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// copy the pointers for the pin name and category
|
|
//
|
|
PinDescs[i].Category = CurrentInfo->Category;
|
|
PinDescs[i].Name = CurrentInfo->Name;
|
|
|
|
if ( CurrentInfo->NumStreamPropArrayEntries) {
|
|
|
|
ASSERT(CurrentInfo->StreamPropertiesArray);
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Imporovement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(NewStreamArray[i].StreamPropertiesArray =
|
|
SCCopyMinidriverProperties(
|
|
CurrentInfo->NumStreamPropArrayEntries,
|
|
CurrentInfo->StreamPropertiesArray))) {
|
|
//
|
|
// Fail to copy
|
|
//
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
if (CurrentInfo->NumStreamEventArrayEntries) {
|
|
|
|
ASSERT(CurrentInfo->StreamEventsArray);
|
|
//
|
|
// make a copy of the events since we modify the
|
|
// struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(NewStreamArray[i].StreamEventsArray =
|
|
SCCopyMinidriverEvents(
|
|
CurrentInfo->NumStreamEventArrayEntries,
|
|
CurrentInfo->StreamEventsArray))) {
|
|
//
|
|
// Fail to copy
|
|
//
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// update the minidriver's properties for this stream.
|
|
//
|
|
SCUpdateMinidriverProperties(
|
|
CurrentInfo->NumStreamPropArrayEntries,
|
|
NewStreamArray[i].StreamPropertiesArray,
|
|
TRUE);
|
|
|
|
//
|
|
// update the minidriver's events for this stream.
|
|
//
|
|
SCUpdateMinidriverEvents(
|
|
CurrentInfo->NumStreamEventArrayEntries,
|
|
NewStreamArray[i].StreamEventsArray,
|
|
TRUE);
|
|
|
|
|
|
//
|
|
// index to next streaminfo structure.
|
|
//
|
|
CurrentInfo++;
|
|
} // for # pins
|
|
} // if there are pins
|
|
|
|
if (StreamDescriptor->StreamHeader.NumDevPropArrayEntries) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DevicePropertiesArray);
|
|
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(FilterInstance->DevicePropertiesArray =
|
|
SCCopyMinidriverProperties(
|
|
StreamDescriptor->StreamHeader.NumDevPropArrayEntries,
|
|
StreamDescriptor->StreamHeader.DevicePropertiesArray))) {
|
|
//
|
|
// Fail to copy
|
|
//
|
|
ASSERT( 0 );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (StreamDescriptor->StreamHeader.NumDevEventArrayEntries) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DeviceEventsArray);
|
|
|
|
//
|
|
// make a copy of the events since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(FilterInstance->EventInfo =
|
|
SCCopyMinidriverEvents(
|
|
StreamDescriptor->StreamHeader.NumDevEventArrayEntries,
|
|
StreamDescriptor->StreamHeader.DeviceEventsArray))) {
|
|
//
|
|
// Fail to copy
|
|
//
|
|
ASSERT( 0 );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_KS_METHODS
|
|
//
|
|
// process the device methods
|
|
//
|
|
if (StreamDescriptor->StreamHeader.NumDevMethodArrayEntries) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DeviceMethodsArray);
|
|
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(FilterInstance->DeviceMethodsArray =
|
|
SCCopyMinidriverMethods(
|
|
StreamDescriptor->StreamHeader.NumDevMethodArrayEntries,
|
|
StreamDescriptor->StreamHeader.DeviceMethodsArray))) {
|
|
//
|
|
// Fail to copy
|
|
//
|
|
ASSERT( 0 );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// process the minidriver's device properties.
|
|
//
|
|
|
|
SCUpdateMinidriverProperties(
|
|
StreamDescriptor->StreamHeader.NumDevPropArrayEntries,
|
|
FilterInstance->DevicePropertiesArray,
|
|
FALSE);
|
|
|
|
|
|
//
|
|
// process the minidriver's device events.
|
|
//
|
|
|
|
SCUpdateMinidriverEvents(
|
|
StreamDescriptor->StreamHeader.NumDevEventArrayEntries,
|
|
FilterInstance->EventInfo,
|
|
FALSE);
|
|
|
|
#ifdef ENABLE_KS_METHODS
|
|
//
|
|
// process the minidriver's device methods.
|
|
//
|
|
|
|
SCUpdateMinidriverMethods(
|
|
StreamDescriptor->StreamHeader.NumDevMethodArrayEntries,
|
|
FilterInstance->DeviceMethodsArray,
|
|
FALSE);
|
|
#endif
|
|
|
|
//
|
|
// set the event info count in the device extension
|
|
//
|
|
|
|
FilterInstance->EventInfoCount =
|
|
StreamDescriptor->StreamHeader.NumDevEventArrayEntries;
|
|
|
|
FilterInstance->HwEventRoutine =
|
|
StreamDescriptor->StreamHeader.DeviceEventRoutine;
|
|
|
|
//
|
|
// call routine to save new stream info
|
|
//
|
|
|
|
SciInsertFilterStreamInfo(FilterInstance,
|
|
PinDescs,
|
|
NumberOfPins);
|
|
|
|
Exit:{
|
|
// ToDo: need to cleanup in error conditions.
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SciInsertFilterStreamInfo(
|
|
IN PFILTER_INSTANCE FilterInstance,
|
|
IN PKSPIN_DESCRIPTOR PinDescs,
|
|
IN ULONG NumberOfPins)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// save the pin info in the dev extension
|
|
//
|
|
|
|
if (FilterInstance->PinInformation) {
|
|
|
|
ExFreePool(FilterInstance->PinInformation);
|
|
}
|
|
FilterInstance->PinInformation = PinDescs;
|
|
FilterInstance->NumberOfPins = NumberOfPins;
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCStreamInfoCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the minidriver's stream info structure(s). This is used to
|
|
process an StreamDescriptor list as well as for one StreamInfo when
|
|
called by StreamClassReenumerateFilterStreams() to rescan.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHW_STREAM_DESCRIPTOR StreamDescriptor;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS Status;
|
|
|
|
DeviceExtension= (PDEVICE_EXTENSION)SRB->HwSRB.HwDeviceExtension -1;
|
|
|
|
ASSERT_DEVICE_EXTENSION( DeviceExtension );
|
|
|
|
if ( NULL == SRB->HwSRB.HwInstanceExtension ) {
|
|
//
|
|
// This is a complete list of StreamInfos for the mini driver
|
|
//
|
|
|
|
//
|
|
// some validations and Just hang it off the DeviceExtension
|
|
//
|
|
ULONG TotalLength;
|
|
ULONG ul;
|
|
PFILTER_TYPE_INFO FilterTypeInfo;
|
|
PHW_STREAM_DESCRIPTOR NextStreamDescriptor;
|
|
BOOLEAN RequestIssued;
|
|
|
|
|
|
FilterTypeInfo = DeviceExtension->FilterTypeInfos;
|
|
StreamDescriptor =
|
|
(PHW_STREAM_DESCRIPTOR) SRB->HwSRB.CommandData.StreamBuffer;
|
|
DeviceExtension->StreamDescriptor = StreamDescriptor;
|
|
NextStreamDescriptor = StreamDescriptor;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// take the event early here. an open could come in the middle of
|
|
// enabling device interface.
|
|
//
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
for ( ul=0, TotalLength=0;
|
|
ul < DeviceExtension->NumberOfFilterTypes;
|
|
ul++) {
|
|
//
|
|
// need a StreamDescriptor for each filter type
|
|
//
|
|
if ((TotalLength+sizeof(HW_STREAM_HEADER) >
|
|
SRB->HwSRB.ActualBytesTransferred ) ||
|
|
(sizeof(HW_STREAM_INFORMATION) !=
|
|
NextStreamDescriptor->StreamHeader.SizeOfHwStreamInformation)){
|
|
//
|
|
// Invalid data, bail out
|
|
//
|
|
DEBUG_BREAKPOINT();
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if ( !(DeviceExtension->RegistryFlags & DRIVER_USES_SWENUM_TO_LOAD )) {
|
|
//
|
|
// Don't create symbolic link if loaded by SWEnum which
|
|
// will create a duplicate one.
|
|
//
|
|
// create the symbolic link to the device.
|
|
//
|
|
|
|
FilterTypeInfo[ul].SymbolicLinks =
|
|
SciCreateSymbolicLinks(
|
|
DeviceExtension,
|
|
ul,
|
|
&NextStreamDescriptor->StreamHeader );
|
|
FilterTypeInfo[ul].LinkNameCount =
|
|
NextStreamDescriptor->StreamHeader.Topology->CategoriesCount;
|
|
}
|
|
|
|
else {
|
|
//
|
|
// no creation, 0 count and null pointer.
|
|
//
|
|
FilterTypeInfo[ul].LinkNameCount = 0;
|
|
FilterTypeInfo[ul].SymbolicLinks = NULL;
|
|
}
|
|
|
|
FilterTypeInfo[ul].StreamDescriptor = NextStreamDescriptor;
|
|
|
|
|
|
TotalLength = TotalLength +
|
|
sizeof(HW_STREAM_HEADER) +
|
|
(sizeof(HW_STREAM_INFORMATION) *
|
|
NextStreamDescriptor->StreamHeader.NumberOfStreams);
|
|
|
|
DebugPrint((DebugLevelVerbose, "TotalLength=%d\n", TotalLength ));
|
|
|
|
NextStreamDescriptor = (PHW_STREAM_DESCRIPTOR)
|
|
((PBYTE) StreamDescriptor + TotalLength);
|
|
|
|
}
|
|
|
|
if ( TotalLength != SRB->HwSRB.ActualBytesTransferred ) {
|
|
DebugPrint((DebugLevelWarning,
|
|
"TotalLength %x of StreamInfo not equal to "
|
|
"ActualBytesTransferred %x\n",
|
|
TotalLength,
|
|
SRB->HwSRB.ActualBytesTransferred ));
|
|
}
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_PNP_STARTED;
|
|
|
|
//
|
|
// call the minidriver to indicate that initialization is
|
|
// complete.
|
|
//
|
|
|
|
SCSubmitRequest(SRB_INITIALIZATION_COMPLETE,
|
|
NULL,
|
|
0,
|
|
SCDequeueAndDeleteSrb,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
SRB->HwSRB.Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->MinidriverData->HwInitData.HwReceivePacket
|
|
);
|
|
|
|
|
|
//
|
|
// tell the device to power down now, since it is not yet opened.
|
|
// acquire the control event since this routine needs it.
|
|
//
|
|
|
|
//KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
// Executive,
|
|
// KernelMode,
|
|
// FALSE, // not alertable
|
|
// NULL);
|
|
|
|
SCCheckPowerDown(DeviceExtension);
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
else {
|
|
//
|
|
// This is a rescan for the specific FilterInstance
|
|
//
|
|
|
|
PFILTER_INSTANCE FilterInstance;
|
|
|
|
FilterInstance = (PFILTER_INSTANCE) SRB->HwSRB.HwInstanceExtension-1;
|
|
StreamDescriptor = (PHW_STREAM_DESCRIPTOR)
|
|
SRB->HwSRB.CommandData.StreamBuffer;
|
|
|
|
Status = SciOnFilterStreamDescriptor(
|
|
FilterInstance,
|
|
StreamDescriptor);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
ASSERT( NULL != FilterInstance->StreamDescriptor );
|
|
ExFreePool( FilterInstance->StreamDescriptor );
|
|
///if ( InterlockedExchange( &FilterInstance->Reenumerated, 1)) {
|
|
/// ASSERT( FilterInstance->StreamDescriptor );
|
|
/// ExFreePool( FilterInstance->StreamDescriptor );
|
|
///}
|
|
FilterInstance->StreamDescriptor = StreamDescriptor;
|
|
}
|
|
}
|
|
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
|
|
|
|
|
|
#else // ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
NTSTATUS
|
|
SCStreamInfoCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the minidriver's stream info structure.
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PHW_STREAM_DESCRIPTOR StreamDescriptor = SRB->HwSRB.CommandData.StreamBuffer;
|
|
ULONG NumberOfPins,
|
|
i;
|
|
PKSPIN_DESCRIPTOR PinDescs = NULL;
|
|
PHW_STREAM_INFORMATION CurrentInfo;
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
ULONG PinSize;
|
|
BOOLEAN Rescan = FALSE;
|
|
BOOLEAN RequestIssued;
|
|
PSTREAM_ADDITIONAL_INFO NewStreamArray;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// if this is a stream rescan, set the boolean
|
|
//
|
|
|
|
if (DeviceExtension->StreamDescriptor) {
|
|
|
|
Rescan = TRUE;
|
|
|
|
}
|
|
if (NT_SUCCESS(SRB->HwSRB.Status)) {
|
|
NumberOfPins = StreamDescriptor->StreamHeader.NumberOfStreams;
|
|
|
|
if (StreamDescriptor->StreamHeader.SizeOfHwStreamInformation < sizeof(HW_STREAM_INFORMATION)) {
|
|
|
|
DebugPrint((DebugLevelError,
|
|
"DecoderClassInit: minidriver stream info too small!"));
|
|
|
|
DEBUG_BREAKPOINT();
|
|
SRB->HwSRB.Status = STATUS_REVISION_MISMATCH;
|
|
|
|
//
|
|
// if this is not a rescan, uninitialize
|
|
//
|
|
|
|
if (!Rescan) {
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
}
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
if (NumberOfPins) {
|
|
|
|
//
|
|
// parse the minidriver's info into CSA format to build the
|
|
// mother of all structures.
|
|
//
|
|
|
|
PinSize = (sizeof(KSPIN_DESCRIPTOR) + sizeof(STREAM_ADDITIONAL_INFO)) * NumberOfPins;
|
|
|
|
PinDescs = ExAllocatePool(NonPagedPool,
|
|
PinSize);
|
|
if (PinDescs == NULL) {
|
|
DebugPrint((DebugLevelError,
|
|
"DecoderClassInit: No pool for stream info"));
|
|
|
|
SRB->HwSRB.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
//
|
|
// if this is not a rescan, uninitialize
|
|
//
|
|
|
|
if (!Rescan) {
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
}
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
RtlZeroMemory(PinDescs, PinSize);
|
|
|
|
//
|
|
// we need a new array to hold the new copies of the
|
|
// stream properties and events which are allocated below.
|
|
//
|
|
|
|
NewStreamArray = (PSTREAM_ADDITIONAL_INFO) ((ULONG_PTR) PinDescs + sizeof(KSPIN_DESCRIPTOR) * NumberOfPins);
|
|
|
|
DeviceExtension->StreamPropEventArray = NewStreamArray;
|
|
|
|
CurrentInfo = &StreamDescriptor->StreamInfo;
|
|
|
|
for (i = 0; i < StreamDescriptor->StreamHeader.NumberOfStreams; i++) {
|
|
|
|
|
|
PinDescs[i].InterfacesCount = SIZEOF_ARRAY(PinInterfaces);
|
|
PinDescs[i].Interfaces = PinInterfaces;
|
|
|
|
//
|
|
// use default medium if minidriver does not specify
|
|
//
|
|
|
|
if (CurrentInfo->MediumsCount) {
|
|
|
|
PinDescs[i].MediumsCount = CurrentInfo->MediumsCount;
|
|
PinDescs[i].Mediums = CurrentInfo->Mediums;
|
|
|
|
} else {
|
|
|
|
PinDescs[i].MediumsCount = SIZEOF_ARRAY(PinMediums);
|
|
PinDescs[i].Mediums = PinMediums;
|
|
|
|
} // if minidriver mediums
|
|
|
|
//
|
|
// set the # of data format blocks
|
|
//
|
|
|
|
PinDescs[i].DataRangesCount =
|
|
CurrentInfo->NumberOfFormatArrayEntries;
|
|
|
|
//
|
|
// point to the data format blocks for the pin
|
|
//
|
|
|
|
PinDescs[i].DataRanges = CurrentInfo->StreamFormatsArray;
|
|
|
|
//
|
|
// set the data flow direction
|
|
//
|
|
|
|
PinDescs[i].DataFlow = (KSPIN_DATAFLOW) CurrentInfo->DataFlow;
|
|
|
|
//
|
|
// set the communication field
|
|
//
|
|
|
|
if (CurrentInfo->BridgeStream) {
|
|
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_BRIDGE;
|
|
|
|
} else {
|
|
|
|
#ifdef ENABLE_STREAM_CLASS_AS_ALLOCATOR
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_BOTH;
|
|
#else
|
|
PinDescs[i].Communication = KSPIN_COMMUNICATION_SINK;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// copy the pointers for the pin name and category
|
|
//
|
|
|
|
PinDescs[i].Category = CurrentInfo->Category;
|
|
PinDescs[i].Name = CurrentInfo->Name;
|
|
|
|
|
|
if ((!Rescan) && (CurrentInfo->NumStreamPropArrayEntries)) {
|
|
|
|
ASSERT(CurrentInfo->StreamPropertiesArray);
|
|
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(NewStreamArray[i].StreamPropertiesArray = SCCopyMinidriverProperties(CurrentInfo->NumStreamPropArrayEntries,
|
|
CurrentInfo->StreamPropertiesArray))) {
|
|
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
}
|
|
if ((!Rescan) && (CurrentInfo->NumStreamEventArrayEntries)) {
|
|
|
|
ASSERT(CurrentInfo->StreamEventsArray);
|
|
|
|
//
|
|
// make a copy of the events since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance:
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(NewStreamArray[i].StreamEventsArray = SCCopyMinidriverEvents(CurrentInfo->NumStreamEventArrayEntries,
|
|
CurrentInfo->StreamEventsArray))) {
|
|
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
}
|
|
//
|
|
// update the minidriver's properties for this stream.
|
|
//
|
|
|
|
SCUpdateMinidriverProperties(
|
|
CurrentInfo->NumStreamPropArrayEntries,
|
|
NewStreamArray[i].StreamPropertiesArray,
|
|
TRUE);
|
|
|
|
//
|
|
// update the minidriver's events for this stream.
|
|
//
|
|
|
|
SCUpdateMinidriverEvents(
|
|
CurrentInfo->NumStreamEventArrayEntries,
|
|
NewStreamArray[i].StreamEventsArray,
|
|
TRUE);
|
|
|
|
|
|
//
|
|
// index to next streaminfo structure.
|
|
//
|
|
|
|
CurrentInfo++;
|
|
|
|
|
|
} // for # pins
|
|
|
|
} // if pins
|
|
if ((!Rescan) && (StreamDescriptor->StreamHeader.NumDevPropArrayEntries)) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DevicePropertiesArray);
|
|
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance:
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(DeviceExtension->DevicePropertiesArray =
|
|
SCCopyMinidriverProperties(StreamDescriptor->StreamHeader.NumDevPropArrayEntries,
|
|
StreamDescriptor->StreamHeader.DevicePropertiesArray))) {
|
|
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
}
|
|
if ((!Rescan) && (StreamDescriptor->StreamHeader.NumDevEventArrayEntries)) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DeviceEventsArray);
|
|
|
|
//
|
|
// make a copy of the events since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(DeviceExtension->EventInfo =
|
|
SCCopyMinidriverEvents(StreamDescriptor->StreamHeader.NumDevEventArrayEntries,
|
|
StreamDescriptor->StreamHeader.DeviceEventsArray))) {
|
|
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_KS_METHODS
|
|
//
|
|
// process the device methods
|
|
//
|
|
if ((!Rescan) && (StreamDescriptor->StreamHeader.NumDevMethodArrayEntries)) {
|
|
|
|
ASSERT(StreamDescriptor->StreamHeader.DeviceMethodsArray);
|
|
|
|
//
|
|
// make a copy of the properties since we modify the struct
|
|
// though parts of it may be marked as a const.
|
|
// Performance Improvement Chance
|
|
// - check for const in future if possible
|
|
//
|
|
|
|
if (!(DeviceExtension->DeviceMethodsArray =
|
|
SCCopyMinidriverMethods(StreamDescriptor->StreamHeader.NumDevMethodArrayEntries,
|
|
StreamDescriptor->StreamHeader.DeviceMethodsArray))) {
|
|
|
|
|
|
SCUninitializeMinidriver(DeviceExtension->DeviceObject,
|
|
SRB->HwSRB.Irp);
|
|
return (SCProcessCompletedRequest(SRB));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// process the minidriver's device properties.
|
|
//
|
|
|
|
SCUpdateMinidriverProperties(
|
|
StreamDescriptor->StreamHeader.NumDevPropArrayEntries,
|
|
DeviceExtension->DevicePropertiesArray,
|
|
FALSE);
|
|
|
|
|
|
//
|
|
// process the minidriver's device events.
|
|
//
|
|
|
|
SCUpdateMinidriverEvents(
|
|
StreamDescriptor->StreamHeader.NumDevEventArrayEntries,
|
|
DeviceExtension->EventInfo,
|
|
FALSE);
|
|
|
|
#ifdef ENABLE_KS_METHODS
|
|
//
|
|
// process the minidriver's device methods.
|
|
//
|
|
|
|
SCUpdateMinidriverMethods(
|
|
StreamDescriptor->StreamHeader.NumDevMethodArrayEntries,
|
|
DeviceExtension->DeviceMethodsArray,
|
|
FALSE);
|
|
#endif
|
|
|
|
//
|
|
// set the event info count in the device extension
|
|
//
|
|
|
|
DeviceExtension->EventInfoCount = StreamDescriptor->StreamHeader.NumDevEventArrayEntries;
|
|
|
|
DeviceExtension->HwEventRoutine = StreamDescriptor->StreamHeader.DeviceEventRoutine;
|
|
|
|
//
|
|
// call routine to save new stream info
|
|
//
|
|
|
|
SCInsertStreamInfo(DeviceExtension,
|
|
PinDescs,
|
|
StreamDescriptor,
|
|
NumberOfPins);
|
|
|
|
|
|
if (!Rescan) {
|
|
|
|
//
|
|
// show device is started from PNP's perspective
|
|
//
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_PNP_STARTED;
|
|
|
|
//
|
|
// create the symbolic link to the device.
|
|
//
|
|
|
|
if ( !(DeviceExtension->RegistryFlags & DRIVER_USES_SWENUM_TO_LOAD )) {
|
|
//
|
|
// Don't create symbolic link if loaded by SWEnum which
|
|
// will create a duplicate one.
|
|
//
|
|
// create the symbolic link to the device.
|
|
//
|
|
|
|
SCCreateSymbolicLinks(DeviceExtension);
|
|
}
|
|
|
|
//
|
|
// call the minidriver to indicate that initialization is
|
|
// complete.
|
|
//
|
|
|
|
|
|
SCSubmitRequest(SRB_INITIALIZATION_COMPLETE,
|
|
NULL,
|
|
0,
|
|
SCDequeueAndDeleteSrb,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
SRB->HwSRB.Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->
|
|
MinidriverData->HwInitData.
|
|
HwReceivePacket
|
|
);
|
|
|
|
|
|
//
|
|
// tell the device to power down now, since it is not yet opened.
|
|
// acquire the control event since this routine needs it.
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE, // not alertable
|
|
NULL);
|
|
|
|
SCCheckPowerDown(DeviceExtension);
|
|
|
|
} // if !rescan
|
|
//
|
|
// release the event. if we are doing a rescan, this is taken
|
|
// by the caller. If not, we took it a few lines above.
|
|
//
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
} // if good status
|
|
//
|
|
// complete this SRB and the original IRP with the final
|
|
// status.
|
|
//
|
|
|
|
return (SCProcessCompletedRequest(SRB));
|
|
|
|
}
|
|
|
|
VOID
|
|
SCInsertStreamInfo(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PKSPIN_DESCRIPTOR PinDescs,
|
|
IN PHW_STREAM_DESCRIPTOR StreamDescriptor,
|
|
IN ULONG NumberOfPins
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// save the pin info in the dev extension
|
|
//
|
|
|
|
if (DeviceExtension->PinInformation) {
|
|
|
|
ExFreePool(DeviceExtension->PinInformation);
|
|
}
|
|
DeviceExtension->PinInformation = PinDescs;
|
|
DeviceExtension->NumberOfPins = NumberOfPins;
|
|
|
|
//
|
|
// save the minidriver's descriptor also.
|
|
//
|
|
|
|
if (DeviceExtension->StreamDescriptor) {
|
|
|
|
ExFreePool(DeviceExtension->StreamDescriptor);
|
|
}
|
|
DeviceExtension->StreamDescriptor = StreamDescriptor;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif //ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
NTSTATUS
|
|
SCPowerCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SRB callback procedure for powering down the hardware
|
|
|
|
Arguments:
|
|
|
|
SRB - address of the completed SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
// These are the return codes that the minidriver is permitted to return,
|
|
// but there is no reason to make this a retail check since we are mandated
|
|
// to never fail a power IRP.
|
|
ASSERT(
|
|
SRB->HwSRB.Status == STATUS_SUCCESS ||
|
|
SRB->HwSRB.Status == STATUS_NOT_IMPLEMENTED ||
|
|
SRB->HwSRB.Status == STATUS_NOT_SUPPORTED ||
|
|
SRB->HwSRB.Status == STATUS_IO_DEVICE_ERROR
|
|
);
|
|
|
|
//
|
|
// set the new power state in the device extension.
|
|
//
|
|
SCSetCurrentDPowerState (DeviceExtension, SRB->HwSRB.CommandData.DeviceState);
|
|
|
|
//
|
|
// free our SRB structure
|
|
//
|
|
|
|
SCDequeueAndDeleteSrb(SRB);
|
|
|
|
//
|
|
// if the state is NOT a power up, we must now send it to the PDO
|
|
// for postprocessing.
|
|
//
|
|
|
|
if (DeviceExtension->CurrentPowerState != PowerDeviceD0) {
|
|
|
|
//
|
|
// send the Irp down to the next layer, and return that status
|
|
// as the final one.
|
|
//
|
|
|
|
Status = SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugPrint((DebugLevelError, "'SCPowerCB: PDO failed power request!\n"));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
PoStartNextPowerIrp(Irp);
|
|
SCCompleteIrp(Irp, Status, DeviceExtension);
|
|
|
|
return (Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCUninitializeMinidriver(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls the minidriver's HWUninitialize routine. If
|
|
successful, all adapter resources are freed, and the adapter is marked
|
|
as stopped.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object for adapter
|
|
Irp - pointer to the PNP Irp.
|
|
|
|
Return Value:
|
|
|
|
NT status code is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHW_INITIALIZATION_DATA HwInitData;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS Status;
|
|
BOOLEAN RequestIssued;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// call minidriver to indicate we are uninitializing.
|
|
//
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// remove the symbolic links for the device
|
|
//
|
|
|
|
SCDestroySymbolicLinks(DeviceExtension);
|
|
|
|
//
|
|
// show one less I/O on this call since our wait logic won't
|
|
// finish until the I/O count goes to zero.
|
|
//
|
|
|
|
InterlockedDecrement(&DeviceExtension->OneBasedIoCount);
|
|
|
|
//
|
|
// wait for any outstanding I/O to complete
|
|
//
|
|
|
|
SCWaitForOutstandingIo(DeviceExtension);
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,// not alertable
|
|
NULL);
|
|
// release event at the callback. or next if !RequestIssued.
|
|
|
|
//
|
|
// restore I/O count to one as we have the PNP I/O outstanding.
|
|
//
|
|
|
|
InterlockedIncrement(&DeviceExtension->OneBasedIoCount);
|
|
|
|
HwInitData = &DeviceExtension->MinidriverData->HwInitData;
|
|
|
|
Status = SCSubmitRequest(SRB_UNINITIALIZE_DEVICE,
|
|
NULL,
|
|
0,
|
|
SCUninitializeCallback,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->
|
|
MinidriverData->HwInitData.
|
|
HwReceivePacket);
|
|
|
|
if (!RequestIssued) {
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
return (Status);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCUninitializeCallback(
|
|
IN PSTREAM_REQUEST_BLOCK SRB
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SRB callback procedure for uninitialize
|
|
|
|
Arguments:
|
|
|
|
SRB - pointer to the uninitialize SRB
|
|
|
|
Return Value:
|
|
|
|
NT status code is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) SRB->HwSRB.HwDeviceExtension - 1;
|
|
PDEVICE_OBJECT DeviceObject = DeviceExtension->DeviceObject;
|
|
PIRP Irp = SRB->HwSRB.Irp;
|
|
NTSTATUS Status = SRB->HwSRB.Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// free all adapter resources we allocated on the START
|
|
// function if the minidriver did not fail
|
|
//
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
|
|
if (Status != STATUS_ADAPTER_HARDWARE_ERROR) {
|
|
|
|
//
|
|
// show not started
|
|
//
|
|
|
|
DeviceExtension->Flags &= ~DEVICE_FLAGS_PNP_STARTED;
|
|
|
|
//
|
|
// free all resources on our device.
|
|
//
|
|
|
|
SCFreeAllResources(DeviceExtension);
|
|
|
|
} // if hwuninitialize
|
|
//
|
|
// free the SRB but don't call back the IRP.
|
|
//
|
|
|
|
SCDequeueAndDeleteSrb(SRB);
|
|
|
|
return (Status);
|
|
}
|
|
|
|
PVOID
|
|
StreamClassGetDmaBuffer(
|
|
IN PVOID HwDeviceExtension)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the DMA buffer previously allocated.
|
|
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Supplies a pointer to the minidriver's device extension.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the uncached device extension or NULL if the extension could
|
|
not be allocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension =
|
|
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
|
|
return (DeviceExtension->DmaBuffer);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Entry point for explicitely loaded stream class.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object created by the system.
|
|
RegistryPath - unused.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(DriverObject);
|
|
PAGED_CODE();
|
|
DEBUG_BREAKPOINT();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
#define DEFAULT_STREAMDEBUG 1
|
|
#define DEFAULT_MAX_LOG_ENTRIES 1024
|
|
#define SCLOG_LEVEL 0
|
|
#define SCLOG_MASK 0
|
|
#define SCLOG_FL_PNP 0x0001
|
|
|
|
#define STR_REG_DBG_STREAM L"\\Registry\\Machine\\system\\currentcontrolset\\services\\stream"
|
|
|
|
typedef struct _SCLOG_ENTRY {
|
|
ULONG ulTag;
|
|
ULONG ulArg1;
|
|
ULONG ulArg2;
|
|
ULONG ulArg3;
|
|
} SCLOG_ENTRY, *PSCLOG_ENTRY;
|
|
|
|
PSCLOG_ENTRY psclogBuffer;
|
|
ULONG scLogNextEntry;
|
|
ULONG scMaxLogEntries;
|
|
ULONG sclogMask;
|
|
ULONG ulTimeIncrement;
|
|
|
|
NTSTATUS
|
|
SCLog(
|
|
ULONG ulTag,
|
|
ULONG ulArg1,
|
|
ULONG ulArg2,
|
|
ULONG ulArg3 )
|
|
/*++
|
|
Description:
|
|
Log the information to the psclogBuffer in a circular mannar. Start from entry 0.
|
|
Wrap around when we hit the end.
|
|
|
|
Parameters:
|
|
ulTag: Tag for the log entry
|
|
ulArg1: argument 1
|
|
ulArg2: argument 2
|
|
ulArg3: argument 3
|
|
|
|
Return:
|
|
SUCCESS: if logged
|
|
UNSUCCESSFUL: otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status=STATUS_UNSUCCESSFUL;
|
|
ULONG ulMyLogEntry;
|
|
|
|
if ( NULL == psclogBuffer ) return Status;
|
|
|
|
//
|
|
// grab the line ticket
|
|
//
|
|
ulMyLogEntry = (ULONG)InterlockedIncrement( &scLogNextEntry );
|
|
//
|
|
// land in the range
|
|
//
|
|
ulMyLogEntry = ulMyLogEntry % scMaxLogEntries;
|
|
|
|
//
|
|
// fill the entry
|
|
//
|
|
psclogBuffer[ulMyLogEntry].ulTag = ulTag;
|
|
psclogBuffer[ulMyLogEntry].ulArg1 = ulArg1;
|
|
psclogBuffer[ulMyLogEntry].ulArg2 = ulArg2;
|
|
psclogBuffer[ulMyLogEntry].ulArg3 = ulArg3;
|
|
|
|
if ( sclogMask & SCLOG_FLAGS_PRINT) {
|
|
char *pCh=(char*) &ulTag;
|
|
DbgPrint( "++scLOG %c%c%c%c %08x %08x %08x\n",
|
|
pCh[0], pCh[1], pCh[2], pCh[3],
|
|
ulArg1,
|
|
ulArg2,
|
|
ulArg3);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS SCLogWithTime(
|
|
ULONG ulTag,
|
|
ULONG ulArg1,
|
|
ULONG ulArg2 )
|
|
/*++
|
|
Description:
|
|
A wrapper to SCLog to also log time in ms in the record. We can have one less
|
|
Argument because time use 1.
|
|
|
|
Parameters:
|
|
ulTag: Tag for the log entry
|
|
ulArg1: argument 1
|
|
ulArg2: argument 2
|
|
|
|
Return:
|
|
SUCCESS: if logged
|
|
UNSUCCESSFUL: otherwise
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER liTime;
|
|
ULONG ulTime;
|
|
|
|
|
|
KeQueryTickCount(&liTime);
|
|
ulTime = (ULONG)(liTime.QuadPart*ulTimeIncrement/10000); // convert to ms
|
|
|
|
if ( NULL == psclogBuffer ) return STATUS_UNSUCCESSFUL;
|
|
return SCLog( ulTag, ulArg1, ulArg2, ulTime );
|
|
}
|
|
|
|
NTSTATUS
|
|
DbgDllUnload()
|
|
/*++
|
|
called by DllUnload to undo the work at DllInitialize
|
|
|
|
--*/
|
|
{
|
|
if ( NULL != psclogBuffer ) {
|
|
ExFreePool( psclogBuffer );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SCGetRegValue(
|
|
IN HANDLE KeyHandle,
|
|
IN PWSTR ValueName,
|
|
OUT PKEY_VALUE_FULL_INFORMATION *Information
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copied from IopGetRegistryValue().
|
|
This routine is invoked to retrieve the data for a registry key's value.
|
|
This is done by querying the value of the key with a zero-length buffer
|
|
to determine the size of the value, and then allocating a buffer and
|
|
actually querying the value into the buffer.
|
|
|
|
It is the responsibility of the caller to free the buffer.
|
|
|
|
Arguments:
|
|
|
|
KeyHandle - Supplies the key handle whose value is to be queried
|
|
|
|
ValueName - Supplies the null-terminated Unicode name of the value.
|
|
|
|
Information - Returns a pointer to the allocated data buffer.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the query operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING unicodeString;
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION infoBuffer;
|
|
ULONG keyValueLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString( &unicodeString, ValueName );
|
|
|
|
//
|
|
// Figure out how big the data value is so that a buffer of the
|
|
// appropriate size can be allocated.
|
|
//
|
|
|
|
status = ZwQueryValueKey( KeyHandle,
|
|
&unicodeString,
|
|
KeyValueFullInformation,
|
|
(PVOID) NULL,
|
|
0,
|
|
&keyValueLength );
|
|
if (status != STATUS_BUFFER_OVERFLOW &&
|
|
status != STATUS_BUFFER_TOO_SMALL) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer large enough to contain the entire key data value.
|
|
//
|
|
|
|
infoBuffer = ExAllocatePool( NonPagedPool, keyValueLength );
|
|
if (!infoBuffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Query the data for the key value.
|
|
//
|
|
|
|
status = ZwQueryValueKey( KeyHandle,
|
|
&unicodeString,
|
|
KeyValueFullInformation,
|
|
infoBuffer,
|
|
keyValueLength,
|
|
&keyValueLength );
|
|
if (!NT_SUCCESS( status )) {
|
|
ExFreePool( infoBuffer );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Everything worked, so simply return the address of the allocated
|
|
// buffer to the caller, who is now responsible for freeing it.
|
|
//
|
|
|
|
*Information = infoBuffer;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCGetRegDword(
|
|
HANDLE h,
|
|
PWCHAR ValueName,
|
|
PULONG pDword)
|
|
{
|
|
NTSTATUS Status;
|
|
PKEY_VALUE_FULL_INFORMATION pFullInfo;
|
|
|
|
Status = SCGetRegValue( h, ValueName, &pFullInfo );
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
*pDword = *(PULONG)((PUCHAR)pFullInfo+pFullInfo->DataOffset);
|
|
ExFreePool( pFullInfo );
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCSetRegDword(
|
|
IN HANDLE KeyHandle,
|
|
IN PWCHAR ValueName,
|
|
IN ULONG ValueData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets a value key in the registry to a specific value of string (REG_SZ)
|
|
type.
|
|
|
|
Parameters:
|
|
|
|
KeyHandle - A handle to the key under which the value is stored.
|
|
|
|
ValueName - Supplies a pointer to the name of the value key
|
|
|
|
ValueData - Supplies a pointer to the value to be stored in the key.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING unicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ValueName);
|
|
|
|
RtlInitUnicodeString( &unicodeString, ValueName );
|
|
|
|
//
|
|
// Set the registry value
|
|
//
|
|
Status = ZwSetValueKey(KeyHandle,
|
|
&unicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&ValueData,
|
|
sizeof(ValueData));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCCreateDbgReg(void)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hStreamDebug;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING PathName;
|
|
UNICODE_STRING uiStreamDebug;
|
|
ULONG ulDisposition;
|
|
ULONG dword;
|
|
static WCHAR strStreamDebug[]=L"StreamDebug";
|
|
static WCHAR strMaxLogEntries[]=L"MaxLogEntries";
|
|
static WCHAR strLogMask[]=L"MaxMask";
|
|
|
|
RtlInitUnicodeString( &PathName, STR_REG_DBG_STREAM );
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&PathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwCreateKey( &hStreamDebug,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0, // title index
|
|
NULL, // class
|
|
0,// create options
|
|
&ulDisposition);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
//
|
|
// getset StreamDebug
|
|
//
|
|
Status = SCGetRegDword( hStreamDebug, strStreamDebug, &dword);
|
|
if ( NT_SUCCESS( Status )) {
|
|
extern ULONG StreamDebug;
|
|
StreamDebug = dword;
|
|
}
|
|
else if ( STATUS_OBJECT_NAME_NOT_FOUND == Status ) {
|
|
//
|
|
// create one with the default value
|
|
//
|
|
Status = SCSetRegDword(hStreamDebug, strStreamDebug, DEFAULT_STREAMDEBUG);
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
}
|
|
|
|
//
|
|
// getset LogMask
|
|
//
|
|
Status = SCGetRegDword( hStreamDebug, strLogMask, &dword);
|
|
if ( NT_SUCCESS( Status )) {
|
|
sclogMask=dword;
|
|
}
|
|
else if ( STATUS_OBJECT_NAME_NOT_FOUND == Status ) {
|
|
//
|
|
// create one with the default to all ( 0x7fffffff )
|
|
//
|
|
Status = SCSetRegDword(hStreamDebug, strLogMask, 0x7fffffff);
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
}
|
|
|
|
//
|
|
// getset MaxLogEntries
|
|
//
|
|
Status = SCGetRegDword( hStreamDebug, strMaxLogEntries, &dword);
|
|
if ( NT_SUCCESS( Status )) {
|
|
scMaxLogEntries=dword;
|
|
}
|
|
|
|
else if ( STATUS_OBJECT_NAME_NOT_FOUND == Status ) {
|
|
//
|
|
// create one with the default value
|
|
//
|
|
Status = SCSetRegDword(hStreamDebug, strMaxLogEntries, DEFAULT_MAX_LOG_ENTRIES);
|
|
ASSERT( NT_SUCCESS( Status ));
|
|
}
|
|
|
|
ZwClose( hStreamDebug );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCInitDbg(
|
|
void )
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
Status = SCCreateDbgReg(); // read or create
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
if ( scMaxLogEntries ) {
|
|
psclogBuffer = ExAllocatePool( NonPagedPool, scMaxLogEntries*sizeof(SCLOG_ENTRY));
|
|
if ( NULL == psclogBuffer ) {
|
|
DbgPrint( "SC: Cant allocate log buffer for %d entries\n", scMaxLogEntries );
|
|
sclogMask = 0; // disable logging
|
|
}
|
|
else {
|
|
DbgPrint( "SC: Allocate log buffer for %d entries\n", scMaxLogEntries );
|
|
ulTimeIncrement = KeQueryTimeIncrement();
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
#endif // DBG
|
|
|
|
NTSTATUS
|
|
DllInitialize(
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
Description:
|
|
System invokes this entry point when it load the image in memory.
|
|
|
|
Arguments:
|
|
RegistryPath - unreferenced parameter.
|
|
|
|
Return:
|
|
STATUS_SUCCESS or appropriate error code.
|
|
--*/
|
|
{
|
|
//UNICODE_STRING DriverName;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
Status = SCInitDbg();
|
|
#endif
|
|
//RtlInitUnicodeString(&DriverName, STREAM_DRIVER_NAME);
|
|
//Status = IoCreateDriver(&DriverName, StreamDriverEntry);
|
|
|
|
if(!NT_SUCCESS(Status)){
|
|
DbgPrint("Stream DLL Initialization failed = %x\n",Status);
|
|
ASSERT(FALSE);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
SCFreeAllResources(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions deletes all of the storage associated with a device
|
|
extension, disconnects from the timers and interrupts.
|
|
This function can be called any time during the initialization.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension to be processed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
PMAPPED_ADDRESS tempPointer;
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInfo;
|
|
PADAPTER_OBJECT DmaAdapterObject;
|
|
ULONG DmaBufferSize;
|
|
ULONG i;
|
|
PSTREAM_ADDITIONAL_INFO NewStreamArray;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((DebugLevelTrace, "'SCFreeAllResources: enter\n"));
|
|
|
|
//
|
|
// take the event to avoid race with the CLOSE handler, which is
|
|
// the only code that will be executed at this point since the
|
|
// INACCESSIBLE bit has been set.
|
|
//
|
|
|
|
KeWaitForSingleObject(&DeviceExtension->ControlEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,// not alertable
|
|
NULL);
|
|
|
|
//
|
|
// if an interrupt is in use, disconnect from it.
|
|
//
|
|
|
|
if ((DeviceExtension->InterruptObject != (PKINTERRUPT) DeviceExtension) &&
|
|
(DeviceExtension->InterruptObject != NULL)) {
|
|
|
|
DebugPrint((DebugLevelVerbose, "'SCFreeAllResources: Interrupt Disconnect\n"));
|
|
IoDisconnectInterrupt(DeviceExtension->InterruptObject);
|
|
|
|
//
|
|
// change the synchronization mechanism to internal, since
|
|
// the IRQ is gone away, hence IRQL level sync.
|
|
//
|
|
|
|
DeviceExtension->SynchronizeExecution = StreamClassSynchronizeExecution;
|
|
DeviceExtension->InterruptObject = (PVOID) DeviceExtension;
|
|
|
|
}
|
|
//
|
|
// Free the configuration information structure if it exists.
|
|
//
|
|
|
|
ConfigInfo = DeviceExtension->ConfigurationInformation;
|
|
if (ConfigInfo) {
|
|
|
|
//
|
|
// free the access range structure if it exists
|
|
//
|
|
|
|
if (ConfigInfo->AccessRanges) {
|
|
ExFreePool(ConfigInfo->AccessRanges);
|
|
}
|
|
DebugPrint((DebugLevelVerbose, "'SCFreeAllResources: freeing ConfigurationInfo\n"));
|
|
ExFreePool(ConfigInfo);
|
|
DeviceExtension->ConfigurationInformation = NULL;
|
|
}
|
|
//
|
|
// free the DMA adapter object and DMA buffer if present
|
|
//
|
|
|
|
DmaAdapterObject = DeviceExtension->DmaAdapterObject;
|
|
|
|
|
|
if (DmaAdapterObject) {
|
|
|
|
DmaBufferSize = DeviceExtension->DriverInfo->HwInitData.DmaBufferSize;
|
|
|
|
if (DeviceExtension->DmaBufferPhysical.QuadPart) {
|
|
|
|
//
|
|
// free the DMA buffer
|
|
//
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClass SCFreeAllResources- Freeing DMA stuff\n"));
|
|
HalFreeCommonBuffer(DmaAdapterObject,
|
|
DmaBufferSize,
|
|
DeviceExtension->DmaBufferPhysical,
|
|
DeviceExtension->DmaBuffer,
|
|
FALSE);
|
|
}
|
|
DeviceExtension->DmaAdapterObject = NULL;
|
|
}
|
|
//
|
|
// Unmap any mapped areas.
|
|
//
|
|
|
|
while (DeviceExtension->MappedAddressList != NULL) {
|
|
DebugPrint((DebugLevelVerbose, "'SCFreeAllResources: unmapping addresses\n"));
|
|
MmUnmapIoSpace(
|
|
DeviceExtension->MappedAddressList->MappedAddress,
|
|
DeviceExtension->MappedAddressList->NumberOfBytes
|
|
);
|
|
|
|
tempPointer = DeviceExtension->MappedAddressList;
|
|
DeviceExtension->MappedAddressList =
|
|
DeviceExtension->MappedAddressList->NextMappedAddress;
|
|
|
|
ExFreePool(tempPointer);
|
|
}
|
|
|
|
DeviceExtension->MappedAddressList = NULL;
|
|
|
|
//
|
|
// We can't free FilterInstances or PinInstances. They
|
|
// must be freed at close calls. However, release StreamDescriptor
|
|
// which is allocated at Start device
|
|
//
|
|
if ( DeviceExtension->StreamDescriptor ) {
|
|
ExFreePool( DeviceExtension->StreamDescriptor );
|
|
DeviceExtension->StreamDescriptor = NULL;
|
|
}
|
|
|
|
//
|
|
// Stop our timers and release event.
|
|
//
|
|
|
|
IoStopTimer(DeviceExtension->DeviceObject);
|
|
KeCancelTimer(&DeviceExtension->ComObj.MiniDriverTimer);
|
|
|
|
KeSetEvent(&DeviceExtension->ControlEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
|
|
#if ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
NTSTATUS
|
|
SciFreeFilterInstance(
|
|
PFILTER_INSTANCE pFilterInstance
|
|
)
|
|
/*++
|
|
|
|
Free all resources associated with a FilterInstance and the filter
|
|
instance itself. This function assume Device control event is taken
|
|
by the caller.
|
|
|
|
Argument:
|
|
|
|
pFilterInstance : pointer to the filter instance to free
|
|
|
|
Return:
|
|
|
|
NTSTATUS: STATUS_SUCCESS of successful, error otherwise
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION pDeviceExtension;
|
|
PSTREAM_ADDITIONAL_INFO NewStreamArray;
|
|
ULONG i;
|
|
|
|
ASSERT_FILTER_INSTANCE( pFilterInstance );
|
|
|
|
pDeviceExtension = pFilterInstance->DeviceExtension;
|
|
|
|
ASSERT_DEVICE_EXTENSION( pDeviceExtension );
|
|
|
|
|
|
NewStreamArray = pFilterInstance->StreamPropEventArray;
|
|
pFilterInstance->StreamPropEventArray = NULL;
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"Freeing filterinstance %x\n", pFilterInstance));
|
|
|
|
while (!IsListEmpty( &pFilterInstance->FirstStream )) {
|
|
|
|
//
|
|
// free all stream instances
|
|
//
|
|
PLIST_ENTRY Node;
|
|
PSTREAM_OBJECT StreamObject;
|
|
|
|
DebugPrint((DebugLevelWarning,
|
|
"Freeing filterinstance %x still open streams\n", pFilterInstance));
|
|
|
|
Node = RemoveHeadList( &pFilterInstance->FirstStream );
|
|
|
|
StreamObject = CONTAINING_RECORD(Node,
|
|
STREAM_OBJECT,
|
|
NextStream);
|
|
|
|
if ( NULL != StreamObject->ComObj.DeviceHeader ) {
|
|
KsFreeObjectHeader( StreamObject->ComObj.DeviceHeader );
|
|
}
|
|
|
|
//
|
|
// null out FsContext for "surprise" stop cases
|
|
//
|
|
ASSERT( StreamObject->FileObject );
|
|
ASSERT( StreamObject->FileObject->FsContext );
|
|
StreamObject->FileObject->FsContext = NULL;
|
|
ExFreePool( StreamObject );
|
|
}
|
|
|
|
if (pFilterInstance->StreamDescriptor) {
|
|
|
|
//
|
|
// free each of the property buffers for the pins
|
|
//
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"FI StreamDescriptor %x has %x pins\n",
|
|
pFilterInstance->StreamDescriptor,
|
|
pFilterInstance->StreamDescriptor->StreamHeader.NumberOfStreams));
|
|
|
|
for (i = 0;
|
|
i < pFilterInstance->StreamDescriptor->StreamHeader.NumberOfStreams;
|
|
i++) {
|
|
|
|
if (NewStreamArray[i].StreamPropertiesArray) {
|
|
|
|
DebugPrint((DebugLevelInfo,"\tFree pin %x Prop %x\n",
|
|
i, NewStreamArray[i].StreamPropertiesArray));
|
|
ExFreePool(NewStreamArray[i].StreamPropertiesArray);
|
|
}
|
|
|
|
if (NewStreamArray[i].StreamEventsArray) {
|
|
DebugPrint((DebugLevelInfo,"\tFree pin %x event %x\n",
|
|
i, NewStreamArray[i].StreamEventsArray));
|
|
ExFreePool(NewStreamArray[i].StreamEventsArray);
|
|
}
|
|
}
|
|
|
|
if (pFilterInstance->DevicePropertiesArray) {
|
|
|
|
DebugPrint((DebugLevelInfo,"Free dev prop %x\n",
|
|
pFilterInstance->DevicePropertiesArray));
|
|
ExFreePool(pFilterInstance->DevicePropertiesArray);
|
|
pFilterInstance->DevicePropertiesArray = NULL;
|
|
|
|
}
|
|
|
|
if (pFilterInstance->EventInfo) {
|
|
|
|
DebugPrint((DebugLevelInfo,"Free dev Event %x\n",
|
|
pFilterInstance->EventInfo));
|
|
ExFreePool(pFilterInstance->EventInfo);
|
|
pFilterInstance->EventInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// always allocate, always free
|
|
//
|
|
DebugPrint((DebugLevelInfo,"Free StreamDescriptor %x\n",
|
|
pFilterInstance->StreamDescriptor));
|
|
|
|
ExFreePool(pFilterInstance->StreamDescriptor);
|
|
pFilterInstance->StreamDescriptor = NULL;
|
|
}
|
|
|
|
if (pFilterInstance->PinInformation) {
|
|
|
|
DebugPrint((DebugLevelInfo,"Free pininformationn %x\n",
|
|
pFilterInstance->PinInformation));
|
|
ExFreePool(pFilterInstance->PinInformation);
|
|
pFilterInstance->PinInformation = NULL;
|
|
}
|
|
|
|
if ( NULL != pFilterInstance->DeviceHeader ) {
|
|
KsFreeObjectHeader( pFilterInstance->DeviceHeader );
|
|
pFilterInstance->DeviceHeader = NULL;
|
|
}
|
|
|
|
if ( pFilterInstance->WorkerRead ) {
|
|
KsUnregisterWorker( pFilterInstance->WorkerRead );
|
|
pFilterInstance->WorkerRead = NULL;
|
|
}
|
|
|
|
if ( pFilterInstance->WorkerWrite ) {
|
|
KsUnregisterWorker( pFilterInstance->WorkerWrite );
|
|
pFilterInstance->WorkerWrite = NULL;
|
|
}
|
|
|
|
//
|
|
// finally the pFilterInstance itself.
|
|
//
|
|
ExFreePool( pFilterInstance );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SCDestroySymbolicLinks(
|
|
IN PDEVICE_EXTENSION DeviceExtension)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For all device interfaces of all filter types of a device, disable
|
|
it and free the name list ofeach filter type
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - pointer to the device extension to be processed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PFILTER_TYPE_INFO FilterTypeInfo;
|
|
ULONG i, j;
|
|
UNICODE_STRING *LinkNames;
|
|
ULONG LinkNameCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
for ( i =0; i < DeviceExtension->NumberOfFilterTypes; i++ ) {
|
|
|
|
LinkNames = DeviceExtension->FilterTypeInfos[i].SymbolicLinks;
|
|
LinkNameCount = DeviceExtension->FilterTypeInfos[i].LinkNameCount;
|
|
//
|
|
// if no names array, we're done.
|
|
//
|
|
|
|
if ( NULL == LinkNames ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// loop through each of the catagory GUID's for each of the pins,
|
|
// deleting the symbolic link for each one.
|
|
//
|
|
|
|
for (j = 0; j < LinkNameCount; j++) {
|
|
|
|
if (LinkNames[j].Buffer) {
|
|
|
|
//
|
|
// Delete the symbolic link, ignoring the status.
|
|
//
|
|
DebugPrint((DebugLevelVerbose,
|
|
" Deleteing symbolic link %S\n",
|
|
LinkNames[j].Buffer));
|
|
|
|
IoSetDeviceInterfaceState(&LinkNames[j], FALSE);
|
|
|
|
//
|
|
// free the buffer allocated by
|
|
// IoRegisterDeviceClassAssociation.
|
|
//
|
|
ExFreePool(LinkNames[j].Buffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// free the links structure and null the pointer
|
|
//
|
|
|
|
ExFreePool(LinkNames);
|
|
DeviceExtension->FilterTypeInfos[i].SymbolicLinks = NULL;
|
|
|
|
} // for # of FilterTypes
|
|
|
|
return;
|
|
}
|
|
|
|
#else // ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
VOID
|
|
SCCreateSymbolicLinks(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension to be processed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHW_STREAM_DESCRIPTOR StreamDescriptor = DeviceExtension->StreamDescriptor;
|
|
LPGUID GuidIndex = (LPGUID) StreamDescriptor->StreamHeader.Topology->Categories;
|
|
ULONG ArrayCount = StreamDescriptor->StreamHeader.Topology->CategoriesCount;
|
|
UNICODE_STRING *NamesArray;
|
|
ULONG i;
|
|
HANDLE ClassHandle,
|
|
PdoHandle;
|
|
UNICODE_STRING TempUnicodeString;
|
|
PVOID DataBuffer[MAX_STRING_LENGTH];
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// allocate space for the array of catagory names
|
|
//
|
|
|
|
if (!(NamesArray = ExAllocatePool(PagedPool,
|
|
sizeof(UNICODE_STRING) * ArrayCount))) {
|
|
return;
|
|
}
|
|
//
|
|
// zero the array in case we're unable to fill it in below. the Destroy
|
|
// routine below will then correctly handle this case.
|
|
//
|
|
|
|
RtlZeroMemory(NamesArray,
|
|
sizeof(UNICODE_STRING) * ArrayCount);
|
|
|
|
DeviceExtension->SymbolicLinks = NamesArray;
|
|
|
|
|
|
//
|
|
// open the PDO
|
|
//
|
|
|
|
|
|
if (!NT_SUCCESS(IoOpenDeviceRegistryKey(DeviceExtension->PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
STANDARD_RIGHTS_ALL,
|
|
&PdoHandle))) {
|
|
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't open\n"));
|
|
return;
|
|
|
|
}
|
|
//
|
|
// loop through each of the catagory GUID's for each of the pins,
|
|
// creating a symbolic link for each one.
|
|
//
|
|
|
|
for (i = 0; i < ArrayCount; i++) {
|
|
|
|
//
|
|
// Create the symbolic link
|
|
//
|
|
|
|
if (!NT_SUCCESS(IoRegisterDeviceInterface(
|
|
DeviceExtension->PhysicalDeviceObject,
|
|
&GuidIndex[i],
|
|
(PUNICODE_STRING) & CreateItems[0].ObjectClass,
|
|
&NamesArray[i]))) {
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't register\n"));
|
|
DEBUG_BREAKPOINT();
|
|
return;
|
|
|
|
}
|
|
//
|
|
// Now set the symbolic link for the association
|
|
//
|
|
|
|
if (!NT_SUCCESS(IoSetDeviceInterfaceState(&NamesArray[i], TRUE))) {
|
|
|
|
DebugPrint((DebugLevelError, "StreamCreateSymLinks: couldn't set\n"));
|
|
DEBUG_BREAKPOINT();
|
|
return;
|
|
|
|
}
|
|
//
|
|
// add the strings from the PDO's key to the association key.
|
|
// Performance Improvement Chance
|
|
// - the INF should be able to directly propogate these;
|
|
// forrest & lonny are fixing.
|
|
//
|
|
|
|
if (NT_SUCCESS(IoOpenDeviceInterfaceRegistryKey(&NamesArray[i],
|
|
STANDARD_RIGHTS_ALL,
|
|
&ClassHandle))) {
|
|
|
|
|
|
//
|
|
// write the class ID for the proxy, if any.
|
|
//
|
|
|
|
if (NT_SUCCESS(SCGetRegistryValue(PdoHandle,
|
|
(PWCHAR) ClsIdString,
|
|
sizeof(ClsIdString),
|
|
&DataBuffer,
|
|
MAX_STRING_LENGTH))) {
|
|
|
|
|
|
RtlInitUnicodeString(&TempUnicodeString, ClsIdString);
|
|
|
|
ZwSetValueKey(ClassHandle,
|
|
&TempUnicodeString,
|
|
0,
|
|
REG_SZ,
|
|
&DataBuffer,
|
|
MAX_STRING_LENGTH
|
|
);
|
|
|
|
} // if cls guid read
|
|
//
|
|
// first check if a friendly name has already been propogated
|
|
// to the class via the INF. If not, we'll just use the device
|
|
// description string for this.
|
|
//
|
|
|
|
if (!NT_SUCCESS(SCGetRegistryValue(ClassHandle,
|
|
(PWCHAR) FriendlyNameString,
|
|
sizeof(FriendlyNameString),
|
|
&DataBuffer,
|
|
MAX_STRING_LENGTH))) {
|
|
|
|
|
|
//
|
|
// write the friendly name for the device, if any.
|
|
//
|
|
|
|
if (NT_SUCCESS(SCGetRegistryValue(PdoHandle,
|
|
(PWCHAR) DriverDescString,
|
|
sizeof(DriverDescString),
|
|
&DataBuffer,
|
|
MAX_STRING_LENGTH))) {
|
|
|
|
|
|
RtlInitUnicodeString(&TempUnicodeString, FriendlyNameString);
|
|
|
|
ZwSetValueKey(ClassHandle,
|
|
&TempUnicodeString,
|
|
0,
|
|
REG_SZ,
|
|
&DataBuffer,
|
|
MAX_STRING_LENGTH
|
|
);
|
|
|
|
|
|
} // if cls guid read
|
|
} // if !friendly name already
|
|
ZwClose(ClassHandle);
|
|
|
|
} // if class key opened
|
|
} // for # Categories
|
|
|
|
ZwClose(PdoHandle);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
SCDestroySymbolicLinks(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension to be processed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHW_STREAM_DESCRIPTOR StreamDescriptor = DeviceExtension->StreamDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (StreamDescriptor) {
|
|
|
|
ULONG ArrayCount = StreamDescriptor->StreamHeader.Topology->CategoriesCount;
|
|
UNICODE_STRING *NamesArray;
|
|
ULONG i;
|
|
|
|
//
|
|
// if no names array, we're done.
|
|
//
|
|
|
|
if (NULL == DeviceExtension->SymbolicLinks) {
|
|
|
|
return;
|
|
}
|
|
|
|
NamesArray = DeviceExtension->SymbolicLinks;
|
|
|
|
//
|
|
// loop through each of the catagory GUID's for each of the pins,
|
|
// deleting the symbolic link for each one.
|
|
//
|
|
|
|
for (i = 0; i < ArrayCount; i++) {
|
|
|
|
|
|
if (NamesArray[i].Buffer) {
|
|
|
|
//
|
|
// Delete the symbolic link, ignoring the status.
|
|
//
|
|
|
|
IoSetDeviceInterfaceState(&NamesArray[i], FALSE);
|
|
|
|
//
|
|
// free the buffer allocated by
|
|
// IoRegisterDeviceClassAssociation.
|
|
//
|
|
|
|
ExFreePool(NamesArray[i].Buffer);
|
|
|
|
} // if buffer
|
|
} // for # Categories
|
|
|
|
//
|
|
// free the links structure and null the pointer
|
|
//
|
|
|
|
ExFreePool(NamesArray);
|
|
DeviceExtension->SymbolicLinks = NULL;
|
|
|
|
} // if StreamDescriptor
|
|
}
|
|
|
|
#endif // ENABLE_MULTIPLE_FILTER_TYPES
|
|
|
|
NTSTATUS
|
|
SCSynchCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PKEVENT Event
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is for use with synchronous IRP processing.
|
|
All it does is signal an event, so the driver knows it
|
|
can continue.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
Irp - Irp that just completed
|
|
|
|
Event - Event we'll signal to say Irp is done
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KeSetEvent((PKEVENT) Event, IO_NO_INCREMENT, FALSE);
|
|
return (STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SCSynchPowerCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE DeviceState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is for use with synchronous IRP power processing.
|
|
All it does is signal an event, so the driver knows it
|
|
can continue.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
SetState - TRUE for set, FALSE for query.
|
|
|
|
DevicePowerState - power state
|
|
|
|
Context - Driver defined context, in our case, an IRP.
|
|
|
|
IoStatus - The status of the IRP.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP SystemIrp = Context;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
|
|
if ( NULL == SystemIrp ) {
|
|
|
|
//
|
|
// SystemIrp has been completed if it is a Wake Irp
|
|
//
|
|
|
|
return ( IoStatus->Status );
|
|
}
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(SystemIrp);
|
|
DeviceExtension = (PDEVICE_EXTENSION)
|
|
(IrpStack->DeviceObject)->DeviceExtension;
|
|
|
|
//
|
|
// cache the status of the device power irp we sent in the system IRP
|
|
//
|
|
|
|
SystemIrp->IoStatus.Status = IoStatus->Status;
|
|
|
|
//
|
|
// schedule a worker item to complete processing. note that we can use
|
|
// a global item since we have not yet issued the PoNextPowerIrp call.
|
|
//
|
|
|
|
ExInitializeWorkItem(&DeviceExtension->PowerCompletionWorkItem,
|
|
SCPowerCompletionWorker,
|
|
SystemIrp);
|
|
|
|
ExQueueWorkItem(&DeviceExtension->PowerCompletionWorkItem,
|
|
DelayedWorkQueue);
|
|
|
|
return (IoStatus->Status);
|
|
}
|
|
|
|
VOID
|
|
SCPowerCompletionWorker(
|
|
IN PIRP SystemIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(SystemIrp);
|
|
PDEVICE_EXTENSION DeviceExtension = IrpStack->DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// preset the status to the status of the Device request, which we cached
|
|
// in the system IRP's status field. We'll override it with the status
|
|
// of the system request if we haven't sent it yet.
|
|
//
|
|
|
|
NTSTATUS Status = SystemIrp->IoStatus.Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// if this is a NOT wakeup, we must first pass the request down
|
|
// to the PDO for postprocessing.
|
|
//
|
|
|
|
if (IrpStack->Parameters.Power.State.SystemState != PowerSystemWorking) {
|
|
|
|
|
|
//
|
|
// send down the system power IRP to the next layer. this routine
|
|
// has a completion routine which does not complete the IRP.
|
|
// preset the status to SUCCESS in this case.
|
|
//
|
|
|
|
SystemIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
Status = SCCallNextDriver(DeviceExtension, SystemIrp);
|
|
|
|
}
|
|
//
|
|
// indicate that we're ready for the next power IRP.
|
|
//
|
|
|
|
PoStartNextPowerIrp(SystemIrp);
|
|
|
|
//
|
|
// show one fewer reference to driver.
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// now complete the system power IRP.
|
|
//
|
|
|
|
SCCompleteIrp(SystemIrp, Status, DeviceExtension);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCBustedSynchPowerCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE DeviceState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(I don't see this can go away) this routine needs to go away
|
|
This routine is for use with synchronous IRP power processing.
|
|
All it does is signal an event, so the driver knows it
|
|
can continue.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
SetState - TRUE for set, FALSE for query.
|
|
|
|
DevicePowerState - power state
|
|
|
|
Context - Driver defined context, in our case, an event.
|
|
|
|
IoStatus - The status of the IRP.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPOWER_CONTEXT PowerContext = Context;
|
|
|
|
PAGED_CODE();
|
|
|
|
PowerContext->Status = IoStatus->Status;
|
|
KeSetEvent(&PowerContext->Event, IO_NO_INCREMENT, FALSE);
|
|
return (PowerContext->Status);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SCCreateChildPdo(
|
|
IN PVOID PnpId,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG InstanceNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to create a PDO for a child device.
|
|
|
|
Arguments:
|
|
|
|
PnpId - ID of device to create
|
|
|
|
ChildNode - node for the device
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT ChildPdo;
|
|
NTSTATUS Status;
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
PCHILD_DEVICE_EXTENSION ChildDeviceExtension;
|
|
PWCHAR NameBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// create a PDO for the child device.
|
|
//
|
|
|
|
Status = IoCreateDevice(DeviceObject->DriverObject,
|
|
sizeof(CHILD_DEVICE_EXTENSION),
|
|
NULL,
|
|
FILE_DEVICE_UNKNOWN,
|
|
FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&ChildPdo);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DEBUG_BREAKPOINT();
|
|
return Status;
|
|
}
|
|
//
|
|
// set the stack size to be the # of stacks used by the FDO.
|
|
//
|
|
|
|
ChildPdo->StackSize = DeviceObject->StackSize+1;
|
|
|
|
//
|
|
// Initialize fields in the ChildDeviceExtension.
|
|
//
|
|
|
|
ChildDeviceExtension = ChildPdo->DeviceExtension;
|
|
ChildDeviceExtension->ChildDeviceObject = ChildPdo;
|
|
ChildDeviceExtension->Flags |= DEVICE_FLAGS_CHILD;
|
|
ChildDeviceExtension->DeviceIndex = InstanceNumber;
|
|
ChildDeviceExtension->ParentDeviceObject = DeviceObject;
|
|
|
|
|
|
//
|
|
// create a new string for the device name and save it away in the device
|
|
// extension. I spent about 4 hours trying to find a way to
|
|
// get unicode strings to work with this. If you ask me why I didn't
|
|
// use a unicode string, I will taunt you and #%*&# in your general
|
|
// direction.
|
|
//
|
|
|
|
|
|
if (NameBuffer = ExAllocatePool(PagedPool,
|
|
wcslen(PnpId) * 2 + 2)) {
|
|
|
|
|
|
wcscpy(NameBuffer,
|
|
PnpId);
|
|
|
|
//
|
|
// save the device name pointer. this is freed when the device is
|
|
// removed.
|
|
//
|
|
|
|
ChildDeviceExtension->DeviceName = NameBuffer;
|
|
|
|
} // if namebuffer
|
|
//
|
|
// initialize the link and insert this node
|
|
//
|
|
|
|
InitializeListHead(&ChildDeviceExtension->ChildExtensionList);
|
|
|
|
InsertTailList(
|
|
&DeviceExtension->Children,
|
|
&ChildDeviceExtension->ChildExtensionList);
|
|
|
|
ChildPdo->Flags |= DO_POWER_PAGABLE;
|
|
ChildPdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCEnumerateChildren(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called in the context of an IRP_MN_QUERY_DEVICE_RELATIONS
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
PVOID PnpId;
|
|
PCHILD_DEVICE_EXTENSION ChildDeviceExtension = NULL,
|
|
CurrentChildExtension;
|
|
PDEVICE_RELATIONS DeviceRelations = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE ParentKey,
|
|
RootKey,
|
|
ChildKey;
|
|
|
|
UNICODE_STRING UnicodeEnumName;
|
|
ULONG NumberOfChildren,
|
|
RelationsSize;
|
|
PDEVICE_OBJECT *ChildPdo;
|
|
PLIST_ENTRY ListEntry,
|
|
ChildEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"EnumChilds for %x %s\n",
|
|
DeviceObject,
|
|
(DeviceExtension->Flags & DEVICE_FLAGS_CHILDREN_ENUMED) == 0 ?
|
|
"1st Time": "has enumed" ));
|
|
|
|
if ( 0 == (DeviceExtension->Flags & DEVICE_FLAGS_CHILDREN_ENUMED) ) {
|
|
//
|
|
// we haven't enumerated children from the registry
|
|
// do it now.
|
|
//
|
|
|
|
Status = IoOpenDeviceRegistryKey(DeviceExtension->PhysicalDeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
STANDARD_RIGHTS_ALL,
|
|
&ParentKey);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
DebugPrint((DebugLevelError, "SCEnumerateChildren: couldn't open\n"));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
//
|
|
// create the subkey for the enum section, in the form "\enum"
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeEnumName, EnumString);
|
|
|
|
//
|
|
// read the registry to determine if children are present.
|
|
//
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeEnumName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ParentKey,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes))) {
|
|
|
|
ZwClose(ParentKey);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// allocate a buffer to contain the ID string. Performance Improvement Chance
|
|
// - this should
|
|
// really get the size and alloc only that size, but I have an existing
|
|
// routine that reads the registry, & this is a temp allocation only.
|
|
//
|
|
|
|
if (!(PnpId = ExAllocatePool(PagedPool, MAX_STRING_LENGTH))) {
|
|
|
|
ZwClose(RootKey);
|
|
ZwClose(ParentKey);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Loop through all the values until either no more entries exist, or an
|
|
// error occurs.
|
|
//
|
|
|
|
for (NumberOfChildren = 0;; NumberOfChildren++) {
|
|
|
|
ULONG BytesReturned;
|
|
PKEY_BASIC_INFORMATION BasicInfoBuffer;
|
|
KEY_BASIC_INFORMATION BasicInfoHeader;
|
|
|
|
//
|
|
// Retrieve the value size.
|
|
//
|
|
|
|
Status = ZwEnumerateKey(
|
|
RootKey,
|
|
NumberOfChildren,
|
|
KeyBasicInformation,
|
|
&BasicInfoHeader,
|
|
sizeof(BasicInfoHeader),
|
|
&BytesReturned);
|
|
|
|
if ((Status != STATUS_BUFFER_OVERFLOW) && !NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// exit the loop, as we either had an error or reached the end
|
|
// of the list of keys.
|
|
//
|
|
|
|
break;
|
|
} // if error
|
|
//
|
|
// Allocate a buffer for the actual size of data needed.
|
|
//
|
|
|
|
BasicInfoBuffer = (PKEY_BASIC_INFORMATION)
|
|
ExAllocatePool(PagedPool,
|
|
BytesReturned);
|
|
|
|
if (!BasicInfoBuffer) {
|
|
|
|
break;
|
|
}
|
|
//
|
|
// Retrieve the name of the nth child device
|
|
//
|
|
|
|
Status = ZwEnumerateKey(
|
|
RootKey,
|
|
NumberOfChildren,
|
|
KeyBasicInformation,
|
|
BasicInfoBuffer,
|
|
BytesReturned,
|
|
&BytesReturned);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ExFreePool(BasicInfoBuffer);
|
|
break;
|
|
|
|
}
|
|
//
|
|
// build object attributes for the key, & try to open it.
|
|
//
|
|
|
|
UnicodeEnumName.Length = (USHORT) BasicInfoBuffer->NameLength;
|
|
UnicodeEnumName.MaximumLength = (USHORT) BasicInfoBuffer->NameLength;
|
|
UnicodeEnumName.Buffer = (PWCHAR) BasicInfoBuffer->Name;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeEnumName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL);
|
|
|
|
|
|
if (!NT_SUCCESS(Status = ZwOpenKey(&ChildKey, KEY_READ, &ObjectAttributes))) {
|
|
|
|
ExFreePool(BasicInfoBuffer);
|
|
break;
|
|
}
|
|
//
|
|
// we've now opened the key for the child. We next read in the PNPID
|
|
// value, and if present, create a PDO of that name.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status = SCGetRegistryValue(ChildKey,
|
|
(PWCHAR) PnpIdString,
|
|
sizeof(PnpIdString),
|
|
PnpId,
|
|
MAX_STRING_LENGTH))) {
|
|
|
|
ExFreePool(BasicInfoBuffer);
|
|
ZwClose(ChildKey);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// create a PDO representing the child.
|
|
//
|
|
|
|
Status = SCCreateChildPdo(PnpId,
|
|
DeviceObject,
|
|
NumberOfChildren);
|
|
|
|
//
|
|
// free the Basic info buffer and close the child key
|
|
//
|
|
|
|
ExFreePool(BasicInfoBuffer);
|
|
ZwClose(ChildKey);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// break out of the loop if we could not create the
|
|
// PDO
|
|
//
|
|
|
|
DEBUG_BREAKPOINT();
|
|
break;
|
|
} // if !success
|
|
} // for NumberOfChildren
|
|
|
|
//
|
|
// close the root and parent keys and free the ID buffer
|
|
//
|
|
|
|
ZwClose(RootKey);
|
|
ZwClose(ParentKey);
|
|
ExFreePool(PnpId);
|
|
|
|
//
|
|
// has enumed, remember this
|
|
//
|
|
|
|
DeviceExtension->Flags |= DEVICE_FLAGS_CHILDREN_ENUMED;
|
|
|
|
//
|
|
// we now have processed all children, and have a linked list of
|
|
// them.
|
|
//
|
|
|
|
if (!NumberOfChildren) {
|
|
|
|
//
|
|
// if no children, just return not supported. this means that the
|
|
// device did not have children.
|
|
//
|
|
|
|
return (STATUS_NOT_IMPLEMENTED);
|
|
|
|
} // if !NumberOfChildren
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// count children which are not marked delete pending
|
|
//
|
|
ListEntry = ChildEntry = &DeviceExtension->Children;
|
|
NumberOfChildren = 0;
|
|
|
|
while (ChildEntry->Flink != ListEntry) {
|
|
|
|
ChildEntry = ChildEntry->Flink;
|
|
|
|
CurrentChildExtension = CONTAINING_RECORD(ChildEntry,
|
|
CHILD_DEVICE_EXTENSION,
|
|
ChildExtensionList );
|
|
if (!(CurrentChildExtension->Flags & DEVICE_FLAGS_CHILD_MARK_DELETE)){
|
|
NumberOfChildren++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate the device relations buffer. This will be freed by the
|
|
// caller.
|
|
//
|
|
|
|
RelationsSize = sizeof(DEVICE_RELATIONS) +
|
|
(NumberOfChildren * sizeof(PDEVICE_OBJECT));
|
|
|
|
DeviceRelations = ExAllocatePool(PagedPool, RelationsSize);
|
|
|
|
if (DeviceRelations == NULL) {
|
|
|
|
//
|
|
// return, but keep the list of children allocated.
|
|
//
|
|
|
|
DEBUG_BREAKPOINT();
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} // if no heap
|
|
RtlZeroMemory(DeviceRelations, RelationsSize);
|
|
|
|
//
|
|
// Walk our chain of children, and initialize the relations
|
|
//
|
|
|
|
ChildPdo = &(DeviceRelations->Objects[0]);
|
|
|
|
//
|
|
// get the 1st child from the parent device extension anchor
|
|
//
|
|
|
|
ListEntry = ChildEntry = &DeviceExtension->Children;
|
|
|
|
while (ChildEntry->Flink != ListEntry) {
|
|
|
|
ChildEntry = ChildEntry->Flink;
|
|
|
|
CurrentChildExtension = CONTAINING_RECORD(ChildEntry,
|
|
CHILD_DEVICE_EXTENSION,
|
|
ChildExtensionList);
|
|
|
|
DebugPrint((DebugLevelInfo,
|
|
"Enumed Child DevObj %x%s marked delete\n",
|
|
CurrentChildExtension->ChildDeviceObject,
|
|
(CurrentChildExtension->Flags & DEVICE_FLAGS_CHILD_MARK_DELETE)==0 ?
|
|
" not" : ""));
|
|
|
|
if ( CurrentChildExtension->Flags & DEVICE_FLAGS_CHILD_MARK_DELETE ) {
|
|
continue;
|
|
}
|
|
|
|
*ChildPdo = CurrentChildExtension->ChildDeviceObject;
|
|
|
|
//
|
|
// per DDK doc we need to inc ref count
|
|
//
|
|
ObReferenceObject( *ChildPdo );
|
|
|
|
ChildPdo++;
|
|
|
|
} // while Children
|
|
|
|
|
|
DeviceRelations->Count = NumberOfChildren;
|
|
|
|
//
|
|
// Stuff that pDeviceRelations into the IRP and return SUCCESS.
|
|
//
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) DeviceRelations;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCEnumGetCaps(
|
|
IN PCHILD_DEVICE_EXTENSION DeviceExtension,
|
|
OUT PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to get the capabilities of a child
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - child device extension
|
|
Capibilities - capabilities structure
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// fill in the structure with non-controversial values
|
|
//
|
|
|
|
Capabilities->SystemWake = PowerSystemUnspecified;
|
|
Capabilities->DeviceWake = PowerDeviceUnspecified;
|
|
Capabilities->D1Latency = 10;
|
|
Capabilities->D2Latency = 10;
|
|
Capabilities->D3Latency = 10;
|
|
Capabilities->LockSupported = FALSE;
|
|
Capabilities->EjectSupported = FALSE;
|
|
Capabilities->Removable = FALSE;
|
|
Capabilities->DockDevice = FALSE;
|
|
Capabilities->UniqueID = FALSE; // set to false so PNP will make us
|
|
|
|
for (i = 0; i < PowerDeviceMaximum; i++) {
|
|
Capabilities->DeviceState[i] = PowerDeviceD0;
|
|
|
|
} // for i
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SCBuildChildIds (
|
|
IN PDEVICE_OBJECT ParentPDO,
|
|
IN BOOLEAN HardwareIDs,
|
|
IN PWCHAR ChildDeviceName,
|
|
OUT PWCHAR *IDBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to build the hardware ID or compatible ID list for a child device.
|
|
This list is built based on the old Stream\<vendor PnPId> naming
|
|
style as a compatible ID and everything else as a munged version of
|
|
the corresponding parent ID (with child information prepended)
|
|
|
|
Arguments:
|
|
|
|
ParentPDO -
|
|
The PDO of the parent
|
|
|
|
HardwareIDs -
|
|
TRUE -
|
|
Build hardware ID list
|
|
|
|
FALSE -
|
|
Build compatible ID list
|
|
|
|
ChildDeviceName -
|
|
The vendor supplied PnPId which used to be the device ID.
|
|
|
|
Return Value:
|
|
|
|
Success / Failure
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR ParentId = NULL;
|
|
PWCHAR ChildId = NULL;
|
|
PWCHAR NameBuffer;
|
|
ULONG ParentIdLength;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// The format for the HWId & CompatId now is going to be:
|
|
//
|
|
// Stream\<vendor supplied PnPId>#<parent ID (w \ -> # replacement)
|
|
//
|
|
// Since we have no understanding of the format of the ID string
|
|
// (as we can be on any bus) and we cannot simply report the most
|
|
// specific ID, all HWIDs from the parent will need to be munged
|
|
// and reported as child HWIDs.
|
|
//
|
|
Status = IoGetDeviceProperty (
|
|
ParentPDO,
|
|
HardwareIDs ?
|
|
DevicePropertyHardwareID : DevicePropertyCompatibleIDs,
|
|
0,
|
|
NULL,
|
|
&ParentIdLength
|
|
);
|
|
|
|
//
|
|
// If the device has no compat IDs and doesn't return a NULL MULTI-SZ,
|
|
// special case the addition of the old HWID.
|
|
//
|
|
if (Status != STATUS_BUFFER_TOO_SMALL && !HardwareIDs) {
|
|
|
|
//
|
|
// Length for: Stream\<vendor ID>00
|
|
//
|
|
ULONG CompatLength = sizeof (WCHAR) * (
|
|
STREAM_BUSENUM_STRING_LENGTH +
|
|
wcslen (ChildDeviceName)
|
|
) + 2 * sizeof (UNICODE_NULL);
|
|
|
|
NameBuffer = (PWCHAR)ExAllocatePool (PagedPool, CompatLength);
|
|
|
|
if (!NameBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
RtlZeroMemory (NameBuffer, CompatLength);
|
|
swprintf (
|
|
NameBuffer,
|
|
STREAM_BUSENUM_STRING L"%s",
|
|
ChildDeviceName
|
|
);
|
|
|
|
//
|
|
// Double NULL terminated by virtue of the RtlZeroMemory.
|
|
//
|
|
*IDBuffer = NameBuffer;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
while (Status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
if (ParentId) {
|
|
ExFreePool (ParentId);
|
|
}
|
|
|
|
ParentId =
|
|
(PWCHAR) ExAllocatePool (PagedPool, ParentIdLength);
|
|
|
|
if (!ParentId) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
Status = IoGetDeviceProperty (
|
|
ParentPDO,
|
|
HardwareIDs ?
|
|
DevicePropertyHardwareID : DevicePropertyCompatibleIDs,
|
|
ParentIdLength,
|
|
ParentId,
|
|
&ParentIdLength
|
|
);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
//
|
|
// Count the total string length of all parent ID's, add necessary
|
|
// munging, and allocate a buffer large enough to report back
|
|
// the MULTI_SZ child HWIDs.
|
|
//
|
|
PWCHAR CurCh = ParentId;
|
|
ULONG ParentLength = 0;
|
|
ULONG ParentCount = 0;
|
|
ULONG ChildLength;
|
|
|
|
while (*CurCh) {
|
|
for (; *CurCh; CurCh++) {
|
|
//
|
|
// Munge \ into #
|
|
//
|
|
if (*CurCh == L'\\') *CurCh = L'#';
|
|
ParentLength++;
|
|
}
|
|
CurCh++;
|
|
ParentCount++;
|
|
}
|
|
|
|
//
|
|
// Account for parent strings total then for adding Stream\ # NULL
|
|
// for each string then for the <vendor PnPId> for each
|
|
// string. The additional wchar is for the extra NULL terminator
|
|
// for the MULTI_SZ.
|
|
//
|
|
ChildLength = sizeof (WCHAR) * (
|
|
ParentLength +
|
|
ParentCount * (
|
|
STREAM_BUSENUM_STRING_LENGTH +
|
|
STREAM_BUSENUM_SEPARATOR_LENGTH +
|
|
1 + /* each NULL terminator for each string of the MULTI_SZ */
|
|
wcslen (ChildDeviceName)
|
|
)
|
|
) + sizeof (UNICODE_NULL);
|
|
|
|
//
|
|
// If the string is an empty MULTI_SZ, we need a NULL string at the
|
|
// beginning.
|
|
//
|
|
if (!ParentCount) {
|
|
ChildLength += sizeof (UNICODE_NULL);
|
|
}
|
|
|
|
//
|
|
// We need the old hardware ID tacked as least ranked compat ID if that
|
|
// is what we are querying.
|
|
//
|
|
if (!HardwareIDs) {
|
|
ChildLength += sizeof (WCHAR) * (
|
|
STREAM_BUSENUM_STRING_LENGTH +
|
|
wcslen (ChildDeviceName)
|
|
) + sizeof (UNICODE_NULL);
|
|
}
|
|
|
|
ChildId = NameBuffer = (PWCHAR)ExAllocatePool (PagedPool, ChildLength);
|
|
|
|
if (!NameBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
RtlZeroMemory (NameBuffer, ChildLength);
|
|
CurCh = ParentId;
|
|
|
|
//
|
|
// Munge each parent ID into a child ID by prefixing the
|
|
// Stream\<vendor PnPId># string.
|
|
//
|
|
while (ParentCount--) {
|
|
|
|
ASSERT (*CurCh != 0);
|
|
|
|
_snwprintf (
|
|
NameBuffer,
|
|
MAX_DEVICE_ID_LEN - 1,
|
|
STREAM_BUSENUM_STRING L"%s#%s",
|
|
ChildDeviceName,
|
|
CurCh
|
|
);
|
|
|
|
NameBuffer += wcslen (NameBuffer) + 1;
|
|
CurCh += wcslen (CurCh) + 1;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tack on the old hardware ID if we're reporting the new compat IDs.
|
|
//
|
|
if (!HardwareIDs) {
|
|
swprintf (
|
|
NameBuffer,
|
|
STREAM_BUSENUM_STRING L"%s",
|
|
ChildDeviceName
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
*IDBuffer = ChildId;
|
|
}
|
|
else {
|
|
if (ChildId) ExFreePool (ChildId);
|
|
}
|
|
|
|
if (ParentId) {
|
|
ExFreePool (ParentId);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SCQueryEnumId(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BUS_QUERY_ID_TYPE BusQueryIdType,
|
|
IN OUT PWSTR * BusQueryId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to get the ID of a child device
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - device object from child
|
|
QueryIdType - ID type from PNP
|
|
BusQueryId - buffer containing the info requested if successful
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
PWCHAR NameBuffer = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PCHILD_DEVICE_EXTENSION DeviceExtension =
|
|
(PCHILD_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
PDEVICE_EXTENSION ParentExtension =
|
|
(PDEVICE_EXTENSION) DeviceExtension->ParentDeviceObject->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// process the query
|
|
//
|
|
|
|
switch (BusQueryIdType) {
|
|
|
|
case BusQueryDeviceID:
|
|
//
|
|
// In order not to orphan any devices installed prior to changing the
|
|
// format of the hardware ID's, the device ID will continue to be
|
|
// reported as Stream\<vendor supplied PnPId> (also a CID) while
|
|
// the hardware ID will contain the more specific munged names.
|
|
//
|
|
NameBuffer = (PWCHAR)ExAllocatePool (
|
|
PagedPool,
|
|
sizeof (WCHAR) * (
|
|
STREAM_BUSENUM_STRING_LENGTH +
|
|
wcslen (DeviceExtension -> DeviceName)
|
|
) + sizeof (UNICODE_NULL)
|
|
);
|
|
|
|
swprintf (
|
|
NameBuffer,
|
|
STREAM_BUSENUM_STRING L"%s",
|
|
DeviceExtension -> DeviceName
|
|
);
|
|
|
|
break;
|
|
|
|
case BusQueryCompatibleIDs:
|
|
|
|
//
|
|
// Compatible IDs are reported as:
|
|
//
|
|
// Stream\<Vendor PnPId>
|
|
// - This was the old hardware / device ID
|
|
//
|
|
// Stream\<Vendor PnPId>#<Munged Parent Compat ID>
|
|
// - These are new compat IDs based off EVERY parent compat ID
|
|
//
|
|
Status = SCBuildChildIds (
|
|
ParentExtension -> PhysicalDeviceObject,
|
|
FALSE,
|
|
DeviceExtension -> DeviceName,
|
|
&NameBuffer
|
|
);
|
|
|
|
break;
|
|
|
|
case BusQueryHardwareIDs:
|
|
|
|
//
|
|
// Hardware IDs are reported as:
|
|
//
|
|
// Stream\<Vendor PnPId>#<Munged Parent Hardware ID>
|
|
// - These are new hardware IDs pased off EVERY parent HWId
|
|
//
|
|
Status = SCBuildChildIds (
|
|
ParentExtension -> PhysicalDeviceObject,
|
|
TRUE,
|
|
DeviceExtension -> DeviceName,
|
|
&NameBuffer
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
case BusQueryInstanceID:
|
|
|
|
{
|
|
|
|
UNICODE_STRING DeviceName;
|
|
WCHAR Buffer[8];
|
|
|
|
//
|
|
// convert the instance # from the device extension to unicode,
|
|
// then copy it over to the output buffer.
|
|
//
|
|
|
|
DeviceName.Buffer = Buffer;
|
|
DeviceName.Length = 0;
|
|
DeviceName.MaximumLength = 8;
|
|
|
|
RtlIntegerToUnicodeString(DeviceExtension->DeviceIndex,
|
|
10,
|
|
&DeviceName);
|
|
|
|
NameBuffer = (PWCHAR)ExAllocatePool (
|
|
PagedPool,
|
|
8 * sizeof (WCHAR)
|
|
);
|
|
|
|
if (!NameBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
RtlZeroMemory (NameBuffer, 8 * sizeof (WCHAR));
|
|
wcscpy(NameBuffer, DeviceName.Buffer);
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return (STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
//
|
|
// return the string and good status.
|
|
//
|
|
|
|
*BusQueryId = NameBuffer;
|
|
|
|
return (Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
StreamClassForwardUnsupported(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine forwards unsupported major function calls to the PDO.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to class device object.
|
|
|
|
Irp - Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
|
|
DebugPrint((DebugLevelVerbose, "'StreamClassForwardUnsupported: enter\n"));
|
|
|
|
if ( !(DeviceExtension->Flags & DEVICE_FLAGS_CHILD)) {
|
|
|
|
//
|
|
// show one more reference to driver.
|
|
//
|
|
SCReferenceDriver(DeviceExtension);
|
|
|
|
//
|
|
// show one more I/O pending & verify that we can actually do I/O.
|
|
//
|
|
Status = SCShowIoPending(DeviceExtension, Irp);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
//
|
|
// the device is currently not accessible, so just return with error
|
|
//
|
|
Irp->IoStatus.Status= Status;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// synchronouosly call the next driver in the stack.
|
|
//
|
|
SCCallNextDriver(DeviceExtension, Irp);
|
|
|
|
//
|
|
// dereference the driver
|
|
//
|
|
|
|
SCDereferenceDriver(DeviceExtension);
|
|
//
|
|
// complete the IRP and return status
|
|
//
|
|
return (SCCompleteIrp(Irp, Irp->IoStatus.Status, DeviceExtension));
|
|
} else {
|
|
//
|
|
// We are the PDO, return error and complete the Irp
|
|
//
|
|
Irp->IoStatus.Status = Status = STATUS_NOT_SUPPORTED;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SCSendSurpriseNotification(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
BOOLEAN RequestIssued;
|
|
|
|
PAGED_CODE();
|
|
SCSubmitRequest(SRB_SURPRISE_REMOVAL,
|
|
NULL,
|
|
0,
|
|
SCDequeueAndDeleteSrb,
|
|
DeviceExtension,
|
|
NULL,
|
|
NULL,
|
|
Irp,
|
|
&RequestIssued,
|
|
&DeviceExtension->PendingQueue,
|
|
(PVOID) DeviceExtension->
|
|
MinidriverData->HwInitData.
|
|
HwReceivePacket
|
|
);
|
|
|
|
|
|
}
|