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.
1297 lines
39 KiB
1297 lines
39 KiB
/*++
|
|
Copyright (C) Microsoft Corporation, 1998 - 1999
|
|
|
|
Module Name:
|
|
|
|
pnp.c
|
|
|
|
Abstract:
|
|
|
|
This file handles the plug and play portions of redbook.sys
|
|
This also handles the AddDevice, DriverEntry, and Unload routines,
|
|
as they are part of initialization.
|
|
|
|
Author:
|
|
|
|
Henry Gabryjelski (henrygab)
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "redbook.h"
|
|
#include "ntddredb.h"
|
|
#include "proto.h"
|
|
|
|
#ifdef _USE_ETW
|
|
#include "pnp.tmh"
|
|
#endif // _USE_ETW
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, DriverEntry )
|
|
#pragma alloc_text(PAGE, RedBookAddDevice )
|
|
#pragma alloc_text(PAGE, RedBookPnp )
|
|
#pragma alloc_text(PAGE, RedBookPnpRemoveDevice )
|
|
#pragma alloc_text(PAGE, RedBookPnpStartDevice )
|
|
#pragma alloc_text(PAGE, RedBookPnpStopDevice )
|
|
#pragma alloc_text(PAGE, RedBookUnload )
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize RedBook driver.
|
|
This is the system initialization entry point
|
|
when the driver is linked into the kernel.
|
|
|
|
Arguments:
|
|
|
|
DriverObject
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS status;
|
|
PREDBOOK_DRIVER_EXTENSION driverExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
#ifdef _USE_ETW
|
|
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
|
#endif // _USE_ETW
|
|
|
|
//
|
|
// WMI requires registry path
|
|
//
|
|
|
|
status = IoAllocateDriverObjectExtension(DriverObject,
|
|
REDBOOK_DRIVER_EXTENSION_ID,
|
|
sizeof(REDBOOK_DRIVER_EXTENSION),
|
|
&driverExtension);
|
|
|
|
if (status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
//
|
|
// The extension already exists - get a pointer to it
|
|
//
|
|
|
|
driverExtension = IoGetDriverObjectExtension(DriverObject,
|
|
REDBOOK_DRIVER_EXTENSION_ID);
|
|
|
|
if (driverExtension != NULL)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"DriverEntry !! no drvObjExt %lx\n", status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Copy the RegistryPath to our newly acquired driverExtension
|
|
//
|
|
|
|
driverExtension->RegistryPath.Buffer =
|
|
ExAllocatePoolWithTag(NonPagedPool,
|
|
RegistryPath->Length + 2,
|
|
TAG_REGPATH);
|
|
|
|
if (driverExtension->RegistryPath.Buffer == NULL) {
|
|
|
|
status = STATUS_NO_MEMORY;
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"DriverEntry !! unable to alloc regPath %lx\n", status));
|
|
return status;
|
|
|
|
} else {
|
|
|
|
driverExtension->RegistryPath.Length = RegistryPath->Length;
|
|
driverExtension->RegistryPath.MaximumLength = RegistryPath->Length + 2;
|
|
RtlCopyUnicodeString(&driverExtension->RegistryPath, RegistryPath);
|
|
|
|
}
|
|
|
|
//
|
|
// Send everything down unless specifically handled.
|
|
//
|
|
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
|
|
DriverObject->MajorFunction[i] = RedBookSendToNextDriver;
|
|
|
|
}
|
|
|
|
//
|
|
// These are the only IRP_MJ types that are handled
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = RedBookWmiSystemControl;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RedBookDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = RedBookReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = RedBookReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = RedBookPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = RedBookPower;
|
|
DriverObject->DriverExtension->AddDevice = RedBookAddDevice;
|
|
DriverObject->DriverUnload = RedBookUnload;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RedBookAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates and initializes a new FDO for the
|
|
corresponding PDO. It may perform property queries on
|
|
the FDO but cannot do any media access operations.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - CDROM class driver object or lower level filter
|
|
|
|
Pdo - the physical device object we are being added to
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PREDBOOK_DEVICE_EXTENSION extension = NULL;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
TRY {
|
|
|
|
//
|
|
// Create the devObj so system doesn't unload us
|
|
//
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(REDBOOK_DEVICE_EXTENSION),
|
|
NULL,
|
|
FILE_DEVICE_CD_ROM,
|
|
0,
|
|
FALSE,
|
|
&deviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"AddDevice !! Couldn't create device %lx\n",
|
|
status));
|
|
LEAVE;
|
|
|
|
}
|
|
|
|
extension = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(extension, sizeof(REDBOOK_DEVICE_EXTENSION));
|
|
|
|
//
|
|
// Attach to the stack
|
|
//
|
|
|
|
extension->TargetDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
|
|
if (extension->TargetDeviceObject == NULL) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"AddDevice != Couldn't attach to stack %lx\n",
|
|
status));
|
|
LEAVE;
|
|
|
|
}
|
|
|
|
extension->DriverObject = DriverObject;
|
|
extension->TargetPdo = PhysicalDeviceObject;
|
|
extension->SelfDeviceObject = deviceObject;
|
|
|
|
//
|
|
// prepare the paging path additions
|
|
//
|
|
|
|
extension->PagingPathCount = 0;
|
|
KeInitializeEvent(&extension->PagingPathEvent,
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
|
|
//
|
|
// Create and acquire a remove lock for this device
|
|
//
|
|
|
|
IoInitializeRemoveLock(&extension->RemoveLock,
|
|
TAG_REMLOCK,
|
|
REMOVE_LOCK_MAX_MINUTES,
|
|
REMOVE_LOCK_HIGH_MARK);
|
|
|
|
//
|
|
// Initialize the Pnp states
|
|
//
|
|
|
|
extension->Pnp.CurrentState = 0xff;
|
|
extension->Pnp.PreviousState = 0xff;
|
|
extension->Pnp.RemovePending = FALSE;
|
|
|
|
//
|
|
// Create thread -- PUT INTO SEPERATE ROUTINE
|
|
//
|
|
|
|
{
|
|
HANDLE handle;
|
|
PKTHREAD thread;
|
|
|
|
//
|
|
// have to setup a minimum amount of stuff for the thread
|
|
// here....
|
|
//
|
|
|
|
extension->CDRom.StateNow = CD_STOPPED;
|
|
|
|
//
|
|
// Allocate memory for the numerous events all at once
|
|
//
|
|
|
|
extension->Thread.Events[0] =
|
|
ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(KEVENT) * EVENT_MAXIMUM,
|
|
TAG_EVENTS);
|
|
|
|
if (extension->Thread.Events[0] == NULL) {
|
|
status = STATUS_NO_MEMORY;
|
|
LEAVE;
|
|
}
|
|
|
|
//
|
|
// Set the pointers appropriately
|
|
// ps - i love pointer math
|
|
//
|
|
|
|
for (i = 1; i < EVENT_MAXIMUM; i++) {
|
|
extension->Thread.Events[i] = extension->Thread.Events[0] + i;
|
|
}
|
|
|
|
InitializeListHead( &extension->Thread.IoctlList);
|
|
KeInitializeSpinLock(&extension->Thread.IoctlLock);
|
|
InitializeListHead( &extension->Thread.WmiList);
|
|
KeInitializeSpinLock(&extension->Thread.WmiLock);
|
|
InitializeListHead( &extension->Thread.DigitalList);
|
|
KeInitializeSpinLock(&extension->Thread.DigitalLock);
|
|
|
|
|
|
extension->Thread.IoctlCurrent = NULL;
|
|
|
|
for ( i = 0; i < EVENT_MAXIMUM; i++) {
|
|
KeInitializeEvent(extension->Thread.Events[i],
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
}
|
|
|
|
ASSERT(extension->Thread.SelfPointer == NULL);
|
|
ASSERT(extension->Thread.SelfHandle == 0);
|
|
|
|
//
|
|
// create the thread that will do most of the work
|
|
//
|
|
|
|
status = PsCreateSystemThread(&handle,
|
|
(ACCESS_MASK) 0L,
|
|
NULL, NULL, NULL,
|
|
RedBookSystemThread,
|
|
extension);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"StartDevice !! Unable to create thread %lx\n",
|
|
status));
|
|
RedBookLogError(extension,
|
|
REDBOOK_ERR_CANNOT_CREATE_THREAD,
|
|
status);
|
|
LEAVE;
|
|
|
|
}
|
|
ASSERT(extension->Thread.SelfHandle == 0); // shouldn't be set yet
|
|
extension->Thread.SelfHandle = handle;
|
|
|
|
//
|
|
// Reference the thread so we can properly wait on it in
|
|
// the remove device routine.
|
|
//
|
|
status = ObReferenceObjectByHandle(handle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
KernelMode,
|
|
&thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// NOTE: we would leak a thread here, but don't
|
|
// know a way to handle this error case?
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"StartDevice !! Unable to reference thread %lx\n",
|
|
status));
|
|
RedBookLogError(extension,
|
|
REDBOOK_ERR_CANNOT_CREATE_THREAD,
|
|
status);
|
|
LEAVE;
|
|
}
|
|
extension->Thread.ThreadReference = thread;
|
|
}
|
|
|
|
} FINALLY {
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"AddDevice !! Failed with status %lx\n",
|
|
status));
|
|
|
|
if (!deviceObject) {
|
|
|
|
//
|
|
// same as no device extension
|
|
//
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
if (extension &&
|
|
extension->Thread.Events[0]) {
|
|
ExFreePool(extension->Thread.Events[0]);
|
|
}
|
|
|
|
if (extension &&
|
|
extension->TargetDeviceObject) {
|
|
IoDetachDevice(extension->TargetDeviceObject);
|
|
}
|
|
|
|
IoDeleteDevice( deviceObject );
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"AddDevice => DevExt at %p\n", extension));
|
|
|
|
//
|
|
// propogate only some flags from the lower devobj.
|
|
//
|
|
|
|
{
|
|
ULONG flagsToPropogate;
|
|
|
|
flagsToPropogate = DO_BUFFERED_IO | DO_DIRECT_IO;
|
|
flagsToPropogate &= extension->TargetDeviceObject->Flags;
|
|
|
|
SET_FLAG(deviceObject->Flags, flagsToPropogate);
|
|
|
|
}
|
|
|
|
SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
|
|
|
|
//
|
|
// No longer initializing
|
|
//
|
|
|
|
CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RedBookPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for PNP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
PREDBOOK_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PDEVICE_OBJECT targetDO = deviceExtension->TargetDeviceObject;
|
|
ULONG cdromState;
|
|
BOOLEAN completeRequest;
|
|
BOOLEAN lockReleased;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp !! Remove lock failed PNP Irp type [%#02x]\n",
|
|
irpSp->MinorFunction));
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Entering previous %x current %x\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction,
|
|
deviceExtension->Pnp.PreviousState,
|
|
deviceExtension->Pnp.CurrentState));
|
|
|
|
lockReleased = FALSE;
|
|
completeRequest = TRUE;
|
|
|
|
switch (irpSp->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
//
|
|
// first forward this down
|
|
//
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
//
|
|
// check status from new sent Start Irp
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => failed start status = %x\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction, status));
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// cannot pass this one down either, since it's already
|
|
// done that in the startdevice routine.
|
|
//
|
|
|
|
status = RedBookPnpStartDevice(DeviceObject);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Pnp.PreviousState =
|
|
deviceExtension->Pnp.CurrentState;
|
|
deviceExtension->Pnp.CurrentState =
|
|
irpSp->MinorFunction;
|
|
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
{
|
|
|
|
//
|
|
// if this device is in use for some reason (paging, etc...)
|
|
// then we need to fail the request.
|
|
//
|
|
|
|
if (deviceExtension->PagingPathCount != 0) {
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Device %p is in the paging path and cannot "
|
|
"be removed\n",
|
|
DeviceObject));
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// see if the query operation can succeed
|
|
//
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
|
|
status = RedBookPnpStopDevice(DeviceObject, Irp);
|
|
} else {
|
|
status = RedBookPnpRemoveDevice(DeviceObject, Irp);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ASSERT(deviceExtension->Pnp.CurrentState != irpSp->MinorFunction);
|
|
|
|
deviceExtension->Pnp.PreviousState =
|
|
deviceExtension->Pnp.CurrentState;
|
|
deviceExtension->Pnp.CurrentState =
|
|
irpSp->MinorFunction;
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE: {
|
|
|
|
//
|
|
// check if the cancel can succeed
|
|
//
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
|
|
|
|
status = RedBookPnpStopDevice(DeviceObject, Irp);
|
|
ASSERTMSG("Pnp !! CANCEL_STOP_DEVICE should never be "
|
|
" failed!\n", NT_SUCCESS(status));
|
|
|
|
} else {
|
|
|
|
status = RedBookPnpRemoveDevice(DeviceObject, Irp);
|
|
ASSERTMSG("Pnp !! CANCEL_REMOVE_DEVICE should never be "
|
|
"failed!\n", NT_SUCCESS(status));
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
//
|
|
// we got a CANCEL -- roll back to the previous state only if
|
|
// the current state is the respective QUERY state.
|
|
//
|
|
|
|
if ((irpSp->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE &&
|
|
deviceExtension->Pnp.CurrentState == IRP_MN_QUERY_STOP_DEVICE)
|
|
||
|
|
(irpSp->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE &&
|
|
deviceExtension->Pnp.CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
|
|
) {
|
|
|
|
deviceExtension->Pnp.CurrentState =
|
|
deviceExtension->Pnp.PreviousState;
|
|
deviceExtension->Pnp.PreviousState = 0xff;
|
|
|
|
}
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_STOP_DEVICE: {
|
|
|
|
ASSERT(deviceExtension->PagingPathCount == 0);
|
|
|
|
//
|
|
// call into the stop device routine.
|
|
//
|
|
|
|
status = RedBookPnpStopDevice(DeviceObject, Irp);
|
|
|
|
ASSERTMSG("[redbook] Pnp !! STOP_DEVICE should never be failed\n",
|
|
NT_SUCCESS(status));
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Pnp.CurrentState = irpSp->MinorFunction;
|
|
deviceExtension->Pnp.PreviousState = 0xff;
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
case IRP_MN_SURPRISE_REMOVAL: {
|
|
|
|
//
|
|
// forward the irp (to close pending io)
|
|
//
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// move this here so i know that i am removing....
|
|
//
|
|
|
|
deviceExtension->Pnp.PreviousState =
|
|
deviceExtension->Pnp.CurrentState;
|
|
deviceExtension->Pnp.CurrentState =
|
|
irpSp->MinorFunction;
|
|
|
|
//
|
|
// the remove lock is released by the remove device routine
|
|
//
|
|
|
|
lockReleased = TRUE;
|
|
status = RedBookPnpRemoveDevice(DeviceObject, Irp);
|
|
|
|
ASSERTMSG("Pnp !! REMOVE_DEVICE should never fail!\n",
|
|
NT_SUCCESS(status));
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
|
|
KEVENT event;
|
|
BOOLEAN setPagable;
|
|
|
|
if (irpSp->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
break; // out of case statement
|
|
}
|
|
|
|
KeWaitForSingleObject(&deviceExtension->PagingPathEvent,
|
|
Executive, KernelMode,
|
|
FALSE, NULL);
|
|
|
|
//
|
|
// if removing last paging device, need to set DO_POWER_PAGABLE
|
|
// bit here, and possible re-set it below on failure.
|
|
//
|
|
|
|
setPagable = FALSE;
|
|
if (!irpSp->Parameters.UsageNotification.InPath &&
|
|
deviceExtension->PagingPathCount == 1) {
|
|
|
|
//
|
|
// removing last paging file. must have
|
|
// DO_POWER_PAGABLE bits set prior to forwarding
|
|
//
|
|
|
|
if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Last paging file"
|
|
" removed, but DO_POWER_INRUSH set, so "
|
|
"not setting DO_POWER_PAGABLE\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction));
|
|
} else {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Setting PAGABLE "
|
|
"bit\n", DeviceObject, Irp,
|
|
irpSp->MinorFunction));
|
|
SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
setPagable = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// send the irp synchronously
|
|
//
|
|
|
|
status = RedBookForwardIrpSynchronous(deviceExtension, Irp);
|
|
|
|
//
|
|
// now deal with the failure and success cases.
|
|
// note that we are not allowed to fail the irp
|
|
// once it is sent to the lower drivers.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IoAdjustPagingPathCount(
|
|
&deviceExtension->PagingPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath);
|
|
|
|
if (irpSp->Parameters.UsageNotification.InPath) {
|
|
if (deviceExtension->PagingPathCount == 1) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Clearing PAGABLE "
|
|
"bit\n", DeviceObject, Irp,
|
|
irpSp->MinorFunction));
|
|
CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// cleanup the changes done above
|
|
//
|
|
|
|
if (setPagable == TRUE) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Clearing PAGABLE bit "
|
|
"due to irp failiing (%x)\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction,
|
|
status));
|
|
CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
setPagable = FALSE;
|
|
}
|
|
|
|
}
|
|
KeSetEvent(&deviceExtension->PagingPathEvent,
|
|
IO_NO_INCREMENT, FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Leaving previous %x "
|
|
"current %x (unhandled)\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction,
|
|
deviceExtension->Pnp.PreviousState,
|
|
deviceExtension->Pnp.CurrentState));
|
|
status = RedBookSendToNextDriver(DeviceObject, Irp);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
completeRequest = FALSE;
|
|
lockReleased = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (completeRequest) {
|
|
|
|
if (!lockReleased) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Leaving previous %x "
|
|
"current %x status %x\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction,
|
|
deviceExtension->Pnp.PreviousState,
|
|
deviceExtension->Pnp.CurrentState,
|
|
status));
|
|
} else {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Pnp (%p,%p,%x) => Leaving with released lock (unsafe "
|
|
"to use device extension for states) status %x\n",
|
|
DeviceObject, Irp, irpSp->MinorFunction, status));
|
|
}
|
|
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
|
|
if (!lockReleased) {
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
RedBookPnpRemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for PNP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PREDBOOK_DEVICE_EXTENSION deviceExtension;
|
|
UCHAR type;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
type = IoGetCurrentIrpStackLocation(Irp)->MinorFunction;
|
|
|
|
if (type == IRP_MN_QUERY_REMOVE_DEVICE ||
|
|
type == IRP_MN_CANCEL_REMOVE_DEVICE) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Type is now either SURPRISE_REMOVAL or REMOVE_DEVICE
|
|
//
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"PnpRemove => starting %s\n",
|
|
(type == IRP_MN_REMOVE_DEVICE ?
|
|
"remove device" : "surprise removal")));
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
deviceExtension->Pnp.RemovePending = TRUE;
|
|
|
|
if (type == IRP_MN_REMOVE_DEVICE) {
|
|
|
|
//
|
|
// prevent any new io
|
|
//
|
|
|
|
IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp);
|
|
|
|
//
|
|
// cleanup the thread, if one exists
|
|
// NOTE: a new one won't start due to the remove lock
|
|
//
|
|
|
|
if (deviceExtension->Thread.SelfHandle != NULL) {
|
|
|
|
ASSERT(deviceExtension->Thread.ThreadReference);
|
|
|
|
//
|
|
// there is no API to wait on a handle, so we must wait on
|
|
// the object.
|
|
//
|
|
|
|
|
|
KeSetEvent(deviceExtension->Thread.Events[EVENT_KILL_THREAD],
|
|
IO_CD_ROM_INCREMENT, FALSE);
|
|
KeWaitForSingleObject(deviceExtension->Thread.ThreadReference,
|
|
Executive, KernelMode,
|
|
FALSE, NULL);
|
|
ObDereferenceObject(deviceExtension->Thread.ThreadReference);
|
|
deviceExtension->Thread.ThreadReference = NULL;
|
|
|
|
ZwClose(deviceExtension->Thread.SelfHandle);
|
|
deviceExtension->Thread.SelfHandle = 0;
|
|
|
|
deviceExtension->Thread.SelfPointer = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// un-register pnp notification
|
|
//
|
|
|
|
if (deviceExtension->Stream.SysAudioReg != NULL) {
|
|
IoUnregisterPlugPlayNotification(deviceExtension->Stream.SysAudioReg);
|
|
deviceExtension->Stream.SysAudioReg = NULL;
|
|
}
|
|
|
|
//
|
|
// free any cached toc
|
|
//
|
|
|
|
if (deviceExtension->CDRom.Toc != NULL) {
|
|
ExFreePool(deviceExtension->CDRom.Toc);
|
|
deviceExtension->CDRom.Toc = NULL;
|
|
}
|
|
|
|
//
|
|
// de-register from wmi
|
|
//
|
|
|
|
if (deviceExtension->WmiLibInitialized) {
|
|
status = RedBookWmiUninit(deviceExtension);
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"PnpRemove => WMI Uninit returned %x\n", status));
|
|
deviceExtension->WmiLibInitialized = FALSE;
|
|
}
|
|
|
|
//
|
|
// Detach from the device stack
|
|
//
|
|
|
|
IoDetachDevice(deviceExtension->TargetDeviceObject);
|
|
deviceExtension->TargetDeviceObject = NULL;
|
|
|
|
//
|
|
// free the events
|
|
//
|
|
|
|
if (deviceExtension->Thread.Events[0]) {
|
|
ExFreePool(deviceExtension->Thread.Events[0]);
|
|
}
|
|
|
|
for (i=0;i<EVENT_MAXIMUM;i++) {
|
|
deviceExtension->Thread.Events[i] = NULL;
|
|
}
|
|
|
|
//
|
|
// make sure we aren't leaking anywhere...
|
|
//
|
|
|
|
ASSERT(deviceExtension->Buffer.Contexts == NULL);
|
|
ASSERT(deviceExtension->Buffer.ReadOk_X == NULL);
|
|
ASSERT(deviceExtension->Buffer.StreamOk_X == NULL);
|
|
|
|
//
|
|
// Now can safely (without leaks) delete our device object
|
|
//
|
|
|
|
IoDeleteDevice(deviceExtension->SelfDeviceObject);
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"PnpRemove => REMOVE_DEVICE finished.\n"));
|
|
|
|
} else {
|
|
|
|
//
|
|
// do nothing for a SURPRISE_REMOVAL, since a REMOVE_DEVICE
|
|
// will soon follow.
|
|
//
|
|
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"PnpRemove => SURPRISE_REMOVAL finished.\n"));
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RedBookPnpStopDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
RedBookPnpStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for START DEVICE.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PREDBOOK_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Never start my driver portion twice
|
|
// system guarantees one Pnp Irp at a time,
|
|
// so state will not change within this routine
|
|
//
|
|
|
|
switch ( deviceExtension->Pnp.CurrentState ) {
|
|
|
|
case 0xff:
|
|
case IRP_MN_STOP_DEVICE: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice => starting driver for devobj %p\n",
|
|
DeviceObject));
|
|
break;
|
|
}
|
|
case IRP_MN_START_DEVICE: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice => already started for devobj %p\n",
|
|
DeviceObject));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice !! remove pending for devobj %p\n",
|
|
DeviceObject));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice !! stop pending for devobj %p\n",
|
|
DeviceObject));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
default: {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"StartDevice !! unknown DeviceState for devobj %p\n",
|
|
DeviceObject));
|
|
ASSERT(!"[RedBook] Pnp !! Unkown Device State");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (deviceExtension->Pnp.Initialized) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// the following code will only successfully run once for each AddDevice()
|
|
// must still ensure that we check if something is already allocated
|
|
// if we allocate it here. also note that everything allocated here must
|
|
// explicitly be checked for in the RemoveDevice() routine, even if we
|
|
// never finished a start successfully.
|
|
//
|
|
|
|
deviceExtension->WmiData.MaximumSectorsPerRead = -1;
|
|
deviceExtension->WmiData.PlayEnabled = 1;
|
|
ASSERT(deviceExtension->CDRom.Toc == NULL);
|
|
if (deviceExtension->CDRom.Toc != NULL) {
|
|
ExFreePool(deviceExtension->CDRom.Toc);
|
|
}
|
|
ASSERT(deviceExtension->Buffer.ReadOk_X == NULL);
|
|
ASSERT(deviceExtension->Buffer.StreamOk_X == NULL);
|
|
ASSERT(deviceExtension->Buffer.Contexts == NULL);
|
|
|
|
RtlZeroMemory(&deviceExtension->Stream, sizeof(REDBOOK_STREAM_DATA));
|
|
deviceExtension->Stream.MixerPinId = -1;
|
|
deviceExtension->Stream.VolumeNodeId = -1;
|
|
deviceExtension->Stream.Connect.Interface.Set = KSINTERFACESETID_Standard;
|
|
deviceExtension->Stream.Connect.Interface.Id = KSINTERFACE_STANDARD_STREAMING;
|
|
deviceExtension->Stream.Connect.Interface.Flags = 0;
|
|
deviceExtension->Stream.Connect.Medium.Set = KSMEDIUMSETID_Standard;
|
|
deviceExtension->Stream.Connect.Medium.Id = KSMEDIUM_STANDARD_DEVIO;
|
|
deviceExtension->Stream.Connect.Medium.Flags = 0;
|
|
deviceExtension->Stream.Connect.Priority.PriorityClass = KSPRIORITY_NORMAL;
|
|
deviceExtension->Stream.Connect.Priority.PrioritySubClass = 1;
|
|
deviceExtension->Stream.Format.DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
|
deviceExtension->Stream.Format.DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
deviceExtension->Stream.Format.DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
deviceExtension->Stream.Format.DataFormat.FormatSize = sizeof( KSDATAFORMAT_WAVEFORMATEX );
|
|
deviceExtension->Stream.Format.DataFormat.Reserved = 0;
|
|
deviceExtension->Stream.Format.DataFormat.Flags = 0;
|
|
deviceExtension->Stream.Format.DataFormat.SampleSize = 0;
|
|
deviceExtension->Stream.Format.WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
|
|
deviceExtension->Stream.Format.WaveFormatEx.nChannels = 2;
|
|
deviceExtension->Stream.Format.WaveFormatEx.nSamplesPerSec = 44100;
|
|
deviceExtension->Stream.Format.WaveFormatEx.wBitsPerSample = 16;
|
|
deviceExtension->Stream.Format.WaveFormatEx.nAvgBytesPerSec = 44100*4;
|
|
deviceExtension->Stream.Format.WaveFormatEx.nBlockAlign = 4;
|
|
deviceExtension->Stream.Format.WaveFormatEx.cbSize = 0;
|
|
|
|
//
|
|
// set the volume, verify we're stopped
|
|
//
|
|
ASSERT(deviceExtension->CDRom.StateNow == CD_STOPPED);
|
|
deviceExtension->CDRom.Volume.PortVolume[0] = 0xff;
|
|
deviceExtension->CDRom.Volume.PortVolume[1] = 0xff;
|
|
deviceExtension->CDRom.Volume.PortVolume[2] = 0xff;
|
|
deviceExtension->CDRom.Volume.PortVolume[3] = 0xff;
|
|
|
|
//
|
|
// Register for Pnp Notifications for SysAudio
|
|
//
|
|
|
|
ASSERT(deviceExtension->Stream.SysAudioReg == NULL);
|
|
|
|
//
|
|
// read the defaults from the registry
|
|
//
|
|
|
|
RedBookRegistryRead(deviceExtension);
|
|
|
|
//
|
|
// get max transfer of adapter
|
|
//
|
|
status = RedBookSetTransferLength(deviceExtension);
|
|
if (!NT_SUCCESS(status)){
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] RedBookSetTransferLength failed with %x\n", status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// and write the new values (just in case)
|
|
//
|
|
|
|
RedBookRegistryWrite(deviceExtension);
|
|
|
|
//
|
|
// also init the WmiPerf structure
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->WmiPerfLock);
|
|
RtlZeroMemory(&deviceExtension->WmiPerf, sizeof(REDBOOK_WMI_PERF_DATA));
|
|
|
|
|
|
//
|
|
// Note dependency in OpenSysAudio() in sysaudio.c
|
|
//
|
|
|
|
if (deviceExtension->Stream.SysAudioReg == NULL) {
|
|
status = IoRegisterPlugPlayNotification(
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(GUID*)&KSCATEGORY_PREFERRED_WAVEOUT_DEVICE,
|
|
deviceExtension->DriverObject,
|
|
SysAudioPnpNotification,
|
|
deviceExtension,
|
|
&deviceExtension->Stream.SysAudioReg
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"StartDevice !! Unable to register for sysaudio pnp "
|
|
"notifications %x\n", status));
|
|
deviceExtension->Stream.SysAudioReg = NULL;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// initialize WMI now that wmi settings are initialized
|
|
//
|
|
status = RedBookWmiInit(deviceExtension);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RedBookLogError(deviceExtension,
|
|
REDBOOK_ERR_WMI_INIT_FAILED,
|
|
status);
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugError, "[redbook] "
|
|
"AddDevice !! WMI Init failed %lx\n",
|
|
status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// log an error if drive doesn't support accurate reads
|
|
//
|
|
|
|
if (!deviceExtension->WmiData.CDDAAccurate) {
|
|
RedBookLogError(deviceExtension,
|
|
REDBOOK_ERR_UNSUPPORTED_DRIVE,
|
|
STATUS_SUCCESS);
|
|
}
|
|
|
|
#if DBG
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice => DO %p SavedIoIndex @ %p Starts @ %p "
|
|
"Each is %x bytes in size\n",
|
|
DeviceObject,
|
|
&deviceExtension->SavedIoCurrentIndex,
|
|
&(deviceExtension->SavedIo[0]),
|
|
sizeof(SAVED_IO)));
|
|
#endif
|
|
|
|
deviceExtension->Pnp.Initialized = TRUE;
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"StartDevice => Finished Initialization\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
RedBookUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the control panel "Unloads"
|
|
the CDROM device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
PREDBOOK_DRIVER_EXTENSION driverExtension;
|
|
|
|
PAGED_CODE();
|
|
ASSERT( DriverObject->DeviceObject == NULL );
|
|
|
|
driverExtension = IoGetDriverObjectExtension(DriverObject,
|
|
REDBOOK_DRIVER_EXTENSION_ID);
|
|
|
|
KdPrintEx((DPFLTR_REDBOOK_ID, RedbookDebugPnp, "[redbook] "
|
|
"Unload => Unloading for DriverObject %p, ext %p\n",
|
|
DriverObject, driverExtension));
|
|
|
|
if (driverExtension != NULL &&
|
|
driverExtension->RegistryPath.Buffer != NULL ) {
|
|
ExFreePool( driverExtension->RegistryPath.Buffer );
|
|
}
|
|
|
|
#ifdef _USE_ETW
|
|
WPP_CLEANUP(DriverObject);
|
|
#endif // _USE_ETW
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|