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