Windows2003-3790/drivers/storage/redbook/pnp.c
2020-09-30 16:53:55 +02:00

1298 lines
39 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}