summaryrefslogtreecommitdiff
path: root/storage/class/cdrom/src/cdrom.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-04-29 09:48:49 -0700
committerDave Wilson <[email protected]>2015-04-29 10:47:29 -0700
commitd40232638faaab19de557e70a3ee938da7a35295 (patch)
tree14e61c973745949af186b7940e3a53d25322f5de /storage/class/cdrom/src/cdrom.c
parent02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff)
samples update for //build
Diffstat (limited to 'storage/class/cdrom/src/cdrom.c')
-rw-r--r--storage/class/cdrom/src/cdrom.c4225
1 files changed, 4225 insertions, 0 deletions
diff --git a/storage/class/cdrom/src/cdrom.c b/storage/class/cdrom/src/cdrom.c
new file mode 100644
index 00000000..a93646ba
--- /dev/null
+++ b/storage/class/cdrom/src/cdrom.c
@@ -0,0 +1,4225 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ cdrom.c
+
+Abstract:
+
+ The CDROM class driver tranlates IRPs to SRBs with embedded CDBs
+ and sends them to its devices through the port driver.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+// this definition is used to link _StorDebugPrint() function.
+#define DEBUG_MAIN_SOURCE 1
+
+
+#include "ntddk.h"
+#include "ntstrsafe.h"
+
+#include "ntddstor.h"
+#include "ntddtape.h"
+#include "wdfcore.h"
+#include "devpkey.h"
+
+#include "cdrom.h"
+#include "ioctl.h"
+#include "mmc.h"
+#include "scratch.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "cdrom.tmh"
+#endif
+
+BOOLEAN
+BootEnvironmentIsWinPE(
+ VOID
+ );
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(INIT, BootEnvironmentIsWinPE)
+
+#pragma alloc_text(PAGE, DriverEvtCleanup)
+#pragma alloc_text(PAGE, DriverEvtDeviceAdd)
+#pragma alloc_text(PAGE, DeviceEvtCleanup)
+#pragma alloc_text(PAGE, DeviceEvtSelfManagedIoCleanup)
+#pragma alloc_text(PAGE, DeviceEvtD0Exit)
+#pragma alloc_text(PAGE, CreateQueueEvtIoDefault)
+#pragma alloc_text(PAGE, DeviceEvtFileClose)
+#pragma alloc_text(PAGE, DeviceCleanupProtectedLocks)
+#pragma alloc_text(PAGE, DeviceCleanupDisableMcn)
+#pragma alloc_text(PAGE, RequestProcessSerializedIoctl)
+#pragma alloc_text(PAGE, ReadWriteWorkItemRoutine)
+#pragma alloc_text(PAGE, IoctlWorkItemRoutine)
+#pragma alloc_text(PAGE, DeviceEvtSurpriseRemoval)
+
+#endif
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ Installable driver initialization entry point.
+ This entry point is called directly by the I/O system.
+
+Arguments:
+
+ DriverObject - pointer to the driver object
+
+ RegistryPath - pointer to a unicode string representing the path,
+ to driver-specific key in the registry.
+
+Return Value:
+
+ STATUS_SUCCESS if successful,
+ STATUS_UNSUCCESSFUL otherwise.
+
+--*/
+{
+ NTSTATUS status;
+ WDF_DRIVER_CONFIG config;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDFDRIVER driverObject = NULL;
+
+ PAGED_CODE();
+
+ // Initialize WPP Tracing
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "CDROM.SYS DriverObject %p loading\n",
+ DriverObject));
+
+ // Register DeviceAdd and DriverEvtCleanup callback.
+ // WPP_CLEANUP will be called in DriverEvtCleanup
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, CDROM_DRIVER_EXTENSION);
+ attributes.EvtCleanupCallback = DriverEvtCleanup;
+
+ WDF_DRIVER_CONFIG_INIT(&config, DriverEvtDeviceAdd);
+
+ status = WdfDriverCreate(DriverObject,
+ RegistryPath,
+ &attributes,
+ &config,
+ &driverObject);
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
+ "WdfDriverCreate failed. %x\n",
+ status));
+
+ // Cleanup tracing here because DriverUnload will not be called
+ // as we have failed to create WDFDRIVER object itself.
+ WPP_CLEANUP(DriverObject);
+
+ }
+ else
+ {
+ PCDROM_DRIVER_EXTENSION driverExtension = DriverGetExtension(driverObject);
+
+ // Copy the registry path into the driver extension so we can use it later
+ driverExtension->Version = 0x01;
+ driverExtension->DriverObject = DriverObject;
+
+ if (BootEnvironmentIsWinPE()) {
+
+ SET_FLAG(driverExtension->Flags, CDROM_FLAG_WINPE_MODE);
+ }
+
+ }
+
+ return status;
+}
+
+
+BOOLEAN
+BootEnvironmentIsWinPE(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine determines if the boot enviroment is WinPE
+
+Arguments:
+
+ None
+
+Return Value:
+
+ BOOLEAN - TRUE if the environment is WinPE; FALSE otherwise
+
+--*/
+{
+ NTSTATUS status;
+ WDFKEY registryKey = NULL;
+
+ DECLARE_CONST_UNICODE_STRING(registryKeyName, WINPE_REG_KEY_NAME);
+
+ PAGED_CODE();
+
+ status = WdfRegistryOpenKey(NULL,
+ &registryKeyName,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &registryKey);
+
+ if (!NT_SUCCESS(status))
+ {
+ return FALSE;
+ }
+
+ WdfRegistryClose(registryKey);
+ return TRUE;
+} // end BootEnvironmentIsWinPE()
+
+
+VOID
+DriverEvtCleanup(
+ _In_ WDFOBJECT Driver
+ )
+/*++
+Routine Description:
+
+ Free all the resources allocated in DriverEntry.
+
+Arguments:
+
+ Driver - handle to a WDF Driver object.
+
+Return Value:
+
+ VOID.
+
+--*/
+{
+ WDFDRIVER driver = (WDFDRIVER)Driver;
+
+ PAGED_CODE ();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "CDROM.SYS DriverObject %p cleanup. Will be unloaded soon\n",
+ driver));
+
+ // Stop WPP Tracing
+ WPP_CLEANUP( WdfDriverWdmGetDriverObject(driver) );
+
+
+ return;
+}
+
+
+NTSTATUS
+DriverEvtDeviceAdd(
+ _In_ WDFDRIVER Driver,
+ _Inout_ PWDFDEVICE_INIT DeviceInit
+ )
+/*++
+
+Routine Description:
+
+ EvtDeviceAdd is called by the framework in response to AddDevice
+ call from the PnP manager.
+
+
+Arguments:
+
+ Driver - Handle to a framework driver object created in DriverEntry
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDROM_DRIVER_EXTENSION driverExtension = NULL;
+ WDFDEVICE device = NULL;
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+ BOOLEAN deviceClaimed = FALSE;
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_FILEOBJECT_CONFIG fileObjectConfig;
+ WDF_IO_TARGET_OPEN_PARAMS ioTargetOpenParams;
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+ WDF_REMOVE_LOCK_OPTIONS removeLockOptions;
+ PWCHAR wideDeviceName = NULL;
+ UNICODE_STRING unicodeDeviceName;
+ PDEVICE_OBJECT lowerPdo = NULL;
+ ULONG deviceNumber = 0;
+ ULONG devicePropertySessionId = INVALID_SESSION;
+ ULONG devicePropertySize = 0;
+ DEVPROPTYPE devicePropertyType = DEVPROP_TYPE_EMPTY;
+
+ PAGED_CODE();
+
+ driverExtension = DriverGetExtension(Driver);
+
+ // 0. Initialize the objects that we're going to use
+ RtlInitUnicodeString(&unicodeDeviceName, NULL);
+
+ // 1. Register PnP&Power callbacks for any we are interested in.
+ // If a callback isn't set, Framework will take the default action by itself.
+ {
+ // Zero out the PnpPowerCallbacks structure.
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+
+ // Use this callback to init resources that are used by the device and only needs to be called once.
+ pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = DeviceEvtSelfManagedIoInit;
+
+ // Use this callback to prepare device for coming back from a lower power mode to D0.
+ pnpPowerCallbacks.EvtDeviceD0Entry = DeviceEvtD0Entry;
+
+ // Use this callback to prepare device for entering into a lower power mode.
+ pnpPowerCallbacks.EvtDeviceD0Exit = DeviceEvtD0Exit;
+
+ // Use this callback to free any resources used by device and will be called when the device is
+ // powered down.
+ pnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = DeviceEvtSelfManagedIoCleanup;
+
+ pnpPowerCallbacks.EvtDeviceSurpriseRemoval = DeviceEvtSurpriseRemoval;
+
+ // Register the PnP and power callbacks.
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+ }
+
+ // 2. Register the EvtIoInCallerContext to deal with IOCTLs that need to stay in original context.
+ WdfDeviceInitSetIoInCallerContextCallback(DeviceInit,
+ DeviceEvtIoInCallerContext);
+
+ // 3. Register PreprocessCallback for IRP_MJ_POWER, IRP_MJ_FLUSH_BUFFERS and IRP_MJ_SHUTDOWN
+ {
+ UCHAR minorFunctions[1];
+
+ minorFunctions[0] = IRP_MN_SET_POWER;
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(DeviceInit,
+ RequestProcessSetPower,
+ IRP_MJ_POWER,
+ minorFunctions,
+ RTL_NUMBER_OF(minorFunctions));
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Assign IrpPreprocessCallback for IRP_MJ_POWER failed, "
+ "status: 0x%X\n", status));
+
+ goto Exit;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(DeviceInit,
+ RequestProcessShutdownFlush,
+ IRP_MJ_FLUSH_BUFFERS,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Assign IrpPreprocessCallback for IRP_MJ_FLUSH_BUFFERS failed, "
+ "status: 0x%X\n", status));
+
+ goto Exit;
+ }
+
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(DeviceInit,
+ RequestProcessShutdownFlush,
+ IRP_MJ_SHUTDOWN,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Assign IrpPreprocessCallback for IRP_MJ_SHUTDOWN failed, "
+ "status: 0x%X\n", status));
+
+ goto Exit;
+ }
+ }
+
+ // 4. Set attributes to create Request Context area.
+ {
+ //Reuse this structure.
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+ CDROM_REQUEST_CONTEXT);
+ attributes.EvtCleanupCallback = RequestEvtCleanup;
+
+ WdfDeviceInitSetRequestAttributes(DeviceInit,
+ &attributes);
+ }
+
+ // 5. Register FileObject related callbacks
+ {
+ // Add FILE_OBJECT_EXTENSION as the context to the file object.
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FILE_OBJECT_CONTEXT);
+
+ // Set Entry points for Create and Close..
+
+ // The framework doesn't sync the file create requests with pnp/power
+ // state. Re-direct all the file create requests to a dedicated
+ // queue, which will be purged manually during removal.
+ WDF_FILEOBJECT_CONFIG_INIT(&fileObjectConfig,
+ NULL, //CreateQueueEvtIoDefault,
+ DeviceEvtFileClose,
+ WDF_NO_EVENT_CALLBACK); // No callback for Cleanup
+
+ fileObjectConfig.FileObjectClass = WdfFileObjectWdfCannotUseFsContexts;
+
+ // Since we are registering file events and fowarding create request
+ // ourself, we must also set AutoForwardCleanupClose so that cleanup
+ // and close can also get forwarded.
+ fileObjectConfig.AutoForwardCleanupClose = WdfTrue;
+ attributes.SynchronizationScope = WdfSynchronizationScopeNone;
+ attributes.ExecutionLevel = WdfExecutionLevelPassive;
+
+ // Indicate that file object is optional.
+ fileObjectConfig.FileObjectClass |= WdfFileObjectCanBeOptional;
+
+ WdfDeviceInitSetFileObjectConfig(DeviceInit,
+ &fileObjectConfig,
+ &attributes);
+ }
+
+ // 6. Initialize device-wide attributes
+ {
+ // Declare ourselves as NOT owning power policy.
+ // The power policy owner in storage stack is port driver.
+ WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
+
+ // Set other DeviceInit attributes.
+ WdfDeviceInitSetExclusive(DeviceInit, FALSE);
+ WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_CD_ROM);
+ WdfDeviceInitSetCharacteristics(DeviceInit, FILE_REMOVABLE_MEDIA, FALSE);
+ WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
+ WdfDeviceInitSetPowerPageable(DeviceInit);
+
+ // We require the framework to acquire a remove lock before delivering all IRP types
+ WDF_REMOVE_LOCK_OPTIONS_INIT(&removeLockOptions,
+ WDF_REMOVE_LOCK_OPTION_ACQUIRE_FOR_IO);
+
+ WdfDeviceInitSetRemoveLockOptions(DeviceInit, &removeLockOptions);
+
+ // save the PDO for later reference
+ lowerPdo = WdfFdoInitWdmGetPhysicalDevice(DeviceInit);
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+ CDROM_DEVICE_EXTENSION);
+
+ // We have a parallel queue, so WdfSynchronizationScopeNone is our only choice.
+ attributes.SynchronizationScope = WdfSynchronizationScopeNone;
+ attributes.ExecutionLevel = WdfExecutionLevelDispatch;
+
+ // Provide a cleanup callback which will release memory allocated with ExAllocatePool*
+ attributes.EvtCleanupCallback = DeviceEvtCleanup;
+ }
+
+ // 7. Now, the device can be created.
+ {
+ wideDeviceName = ExAllocatePoolWithTag(NonPagedPoolNx,
+ 64 * sizeof(WCHAR),
+ CDROM_TAG_STRINGS);
+
+ if (wideDeviceName == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ // Find the lowest device number currently available.
+ do {
+ status = RtlStringCchPrintfW((NTSTRSAFE_PWSTR)wideDeviceName,
+ 64,
+ L"\\Device\\CdRom%d",
+ deviceNumber);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Format device name failed with error: 0x%X\n", status));
+
+ goto Exit;
+ }
+
+ RtlInitUnicodeString(&unicodeDeviceName, wideDeviceName);
+
+ status = WdfDeviceInitAssignName(DeviceInit, &unicodeDeviceName);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: WdfDeviceInitAssignName() failed with error: 0x%X\n", status));
+
+ goto Exit;
+ }
+
+ status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
+
+ deviceNumber++;
+
+ } while (status == STATUS_OBJECT_NAME_COLLISION);
+
+ // When this loop exits the count is inflated by one - fix that.
+ deviceNumber--;
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Can not create a new device, status: 0x%X\n",
+ status));
+
+ goto Exit;
+ }
+ }
+
+ // 8. Fill up basic Device Extension settings and create a remote I/O target for the next-lower driver.
+ // The reason why we do not use the local I/O target is because we want to be able to close the
+ // I/O target on surprise removal.
+ {
+ deviceExtension = DeviceGetExtension(device);
+
+ deviceExtension->Version = 0x01;
+ deviceExtension->Size = sizeof(CDROM_DEVICE_EXTENSION);
+
+ deviceExtension->DeviceObject = WdfDeviceWdmGetDeviceObject(device);
+ deviceExtension->Device = device;
+ deviceExtension->DriverExtension = driverExtension;
+
+ deviceExtension->LowerPdo = lowerPdo;
+
+ deviceExtension->DeviceType = FILE_DEVICE_CD_ROM; //Always a FILE_DEVICE_CD_ROM for all device it manages.
+ deviceExtension->DeviceName = unicodeDeviceName;
+
+ deviceExtension->DeviceNumber = deviceNumber;
+ }
+ {
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = deviceExtension->Device;
+
+ status = WdfIoTargetCreate(deviceExtension->Device,
+ &attributes,
+ &deviceExtension->IoTarget);
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Can not create a remote I/O target object, status: 0x%X\n",
+ status));
+ goto Exit;
+ }
+
+ WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(&ioTargetOpenParams,
+ WdfDeviceWdmGetAttachedDevice(deviceExtension->Device));
+
+ status = WdfIoTargetOpen(deviceExtension->IoTarget,
+ &ioTargetOpenParams);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Can not open a remote I/O target for the next-lower device, status: 0x%X\n",
+ status));
+
+ WdfObjectDelete(deviceExtension->IoTarget);
+ deviceExtension->IoTarget = NULL;
+
+ goto Exit;
+ }
+ }
+
+ // 9. Claim the device, so that port driver will only accept the commands from CDROM.SYS for this device.
+ // NOTE: The I/O should be issued after the device is started. But we would like to claim
+ // the device as soon as possible, so this legacy behavior is kept.
+ status = DeviceClaimRelease(deviceExtension, FALSE);
+
+ if (!NT_SUCCESS(status))
+ {
+ // Someone already had this device - we're in trouble
+ goto Exit;
+ }
+ else
+ {
+ deviceClaimed = TRUE;
+ }
+
+ //
+ // CDROM Queueing Structure
+ //
+ // a. EvtIoInCallerContext (prior to queueing):
+ // This event will be used ONLY to forward down IOCTLs that come in at PASSIVE LEVEL
+ // and need to be forwarded down the stack in the original context.
+ //
+ // b. Main input queue: serial queue for main dispatching
+ // This queue will be used to do all dispatching of requests to serialize
+ // access to the device. Any request that was previously completed in
+ // the Dispatch routines will be completed here. Anything requiring device
+ // I/O will be sent through the serial I/O queue.
+ //
+ // 10. Set up IO queues after device being created.
+ //
+ {
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,
+ WdfIoQueueDispatchSequential);
+
+ queueConfig.PowerManaged = WdfFalse;
+
+#pragma prefast(push)
+#pragma prefast(disable: 28155, "a joint handler for read/write cannot be EVT_WDF_IO_QUEUE_IO_READ and EVT_WDF_IO_QUEUE_IO_WRITE simultaneously")
+#pragma prefast(disable: 28023, "a joint handler for read/write cannot be EVT_WDF_IO_QUEUE_IO_READ and EVT_WDF_IO_QUEUE_IO_WRITE simultaneously")
+ queueConfig.EvtIoRead = SequentialQueueEvtIoReadWrite;
+ queueConfig.EvtIoWrite = SequentialQueueEvtIoReadWrite;
+#pragma prefast(pop)
+
+ queueConfig.EvtIoDeviceControl = SequentialQueueEvtIoDeviceControl;
+ queueConfig.EvtIoCanceledOnQueue = SequentialQueueEvtCanceledOnQueue;
+
+ status = WdfIoQueueCreate(device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &(deviceExtension->SerialIOQueue));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ // this queue is dedicated for file create requests.
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchParallel);
+
+ queueConfig.PowerManaged = WdfFalse;
+ queueConfig.EvtIoDefault = CreateQueueEvtIoDefault;
+
+ //Reuse this structure.
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+ CDROM_REQUEST_CONTEXT);
+
+ attributes.SynchronizationScope = WdfSynchronizationScopeNone;
+ attributes.ExecutionLevel = WdfExecutionLevelPassive;
+
+ status = WdfIoQueueCreate(device,
+ &queueConfig,
+ &attributes,
+ &(deviceExtension->CreateQueue));
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ // Configure the device to use driver created queue for dispatching create.
+ status = WdfDeviceConfigureRequestDispatching(device,
+ deviceExtension->CreateQueue,
+ WdfRequestTypeCreate);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+
+ // 11. Set the alignment requirements for the device based on the host adapter requirements.
+ //
+ // NOTE: this should have been set when device is attached on device stack,
+ // by keeping this legacy code, we could avoid issue that this value was not correctly set at that time.
+ if (deviceExtension->LowerPdo->AlignmentRequirement > deviceExtension->DeviceObject->AlignmentRequirement)
+ {
+ WdfDeviceSetAlignmentRequirement(deviceExtension->Device,
+ deviceExtension->LowerPdo->AlignmentRequirement);
+ }
+
+ // 12. Initialization of miscellaneous internal properties
+
+ // CDROMs are not partitionable so starting offset is 0.
+ deviceExtension->StartingOffset.LowPart = 0;
+ deviceExtension->StartingOffset.HighPart = 0;
+
+ // Set the default geometry for the cdrom to match what NT 4 used.
+ // these values will be used to compute the cylinder count rather
+ // than using it's NT 5.0 defaults.
+ deviceExtension->DiskGeometry.MediaType = RemovableMedia;
+ deviceExtension->DiskGeometry.TracksPerCylinder = 0x40;
+ deviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
+
+ deviceExtension->DeviceAdditionalData.ReadWriteRetryDelay100nsUnits = WRITE_RETRY_DELAY_DVD_1x;
+
+ // Clear the SrbFlags and disable synchronous transfers
+ deviceExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+ // Set timeout value in seconds.
+ deviceExtension->TimeOutValue = DeviceGetTimeOutValueFromRegistry();
+ if ((deviceExtension->TimeOutValue > 30 * 60) || // longer than 30 minutes
+ (deviceExtension->TimeOutValue == 0))
+ {
+ deviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "DriverEvtDeviceAdd: device timeout is set to %x seconds",
+ deviceExtension->TimeOutValue
+ ));
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ deviceExtension->IsVolumeOnlinePending = TRUE;
+
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual);
+
+ queueConfig.PowerManaged = WdfFalse;
+
+ status = WdfIoQueueCreate(device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &deviceExtension->ManualVolumeReadyQueue);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+#endif
+
+ // 13. Initialize the stuff related to media locking
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = deviceExtension->Device;
+ status = WdfWaitLockCreate(&attributes,
+ &deviceExtension->EjectSynchronizationLock);
+
+ deviceExtension->LockCount = 0;
+
+ // 14. Initialize context structures needed for asynchronous release queue and power requests
+
+ if (NT_SUCCESS(status))
+ {
+ status = DeviceInitReleaseQueueContext(deviceExtension);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ status = DeviceInitPowerContext(deviceExtension);
+ }
+
+ // 15. Create external access points other than device name.
+ if (NT_SUCCESS(status))
+ {
+ status = DeviceCreateWellKnownName(deviceExtension);
+ }
+
+ // 16. Query session id from the PDO.
+ if (NT_SUCCESS(status))
+ {
+ status = IoGetDevicePropertyData(deviceExtension->LowerPdo,
+ &DEVPKEY_Device_SessionId,
+ 0,
+ 0,
+ sizeof(devicePropertySessionId),
+ &devicePropertySessionId,
+ &devicePropertySize,
+ &devicePropertyType);
+
+ if (!NT_SUCCESS(status))
+ {
+ // The device is global.
+ devicePropertySessionId = INVALID_SESSION;
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ // 17. Register interfaces for this device.
+ if (NT_SUCCESS(status))
+ {
+ status = DeviceRegisterInterface(deviceExtension, CdRomDeviceInterface);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ // If this is a per-session DO, don't register for mount interface so that
+ // mountmgr will not automatically assign a drive letter.
+ if (devicePropertySessionId == INVALID_SESSION)
+ {
+ status = DeviceRegisterInterface(deviceExtension, MountedDeviceInterface);
+ }
+ }
+
+ // 18. Initialize the shutdown/flush lock
+ if (NT_SUCCESS(status))
+ {
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = deviceExtension->Device;
+
+ status = WdfWaitLockCreate(&attributes, &deviceExtension->ShutdownFlushWaitLock);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+ "DriverEvtDeviceAdd: Cannot create shutdown/flush waitlock, status: 0x%X\n",
+ status));
+ }
+ }
+
+ // 19. Initialize the work item that is used to initiate asynchronous reads/writes
+ if (NT_SUCCESS(status))
+ {
+ WDF_WORKITEM_CONFIG workItemConfig;
+ WDF_OBJECT_ATTRIBUTES workItemAttributes;
+
+ WDF_WORKITEM_CONFIG_INIT(&workItemConfig,
+ ReadWriteWorkItemRoutine
+ );
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&workItemAttributes);
+ workItemAttributes.ParentObject = deviceExtension->Device;
+
+ status = WdfWorkItemCreate(&workItemConfig,
+ &workItemAttributes,
+ &deviceExtension->ReadWriteWorkItem
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
+ "DriverEvtDeviceAdd: Cannot create read/write work item, status: 0x%X\n",
+ status));
+ }
+ }
+
+ // 20. Initialize the work item that is used to process most IOCTLs at PASSIVE_LEVEL.
+ if (NT_SUCCESS(status))
+ {
+ WDF_WORKITEM_CONFIG workItemConfig;
+ WDF_OBJECT_ATTRIBUTES workItemAttributes;
+
+ WDF_WORKITEM_CONFIG_INIT(&workItemConfig,
+ IoctlWorkItemRoutine
+ );
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&workItemAttributes);
+ workItemAttributes.ParentObject = deviceExtension->Device;
+
+ status = WdfWorkItemCreate(&workItemConfig,
+ &workItemAttributes,
+ &deviceExtension->IoctlWorkItem
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
+ "DriverEvtDeviceAdd: Cannot create ioctl work item, status: 0x%X\n",
+ status));
+ }
+ }
+
+
+Exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ FREE_POOL(wideDeviceName);
+
+ if (deviceExtension != NULL)
+ {
+ RtlInitUnicodeString(&deviceExtension->DeviceName, NULL);
+ }
+
+ // Release the device with the port driver, if it was claimed
+ if ((deviceExtension != NULL) && deviceClaimed)
+ {
+ DeviceClaimRelease(deviceExtension, TRUE);
+ }
+ deviceClaimed = FALSE;
+ }
+
+ return status;
+}
+
+
+VOID
+DeviceEvtCleanup(
+ _In_ WDFOBJECT Device
+ )
+/*++
+Routine Description:
+
+ Free all the resources allocated in DriverEvtDeviceAdd.
+
+Arguments:
+
+ Device - handle to a WDF Device object.
+
+Return Value:
+
+ VOID.
+
+--*/
+{
+ WDFDEVICE device = (WDFDEVICE)Device;
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+
+ PAGED_CODE ();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "WDFDEVICE %p cleanup: The device is about to be destroyed.\n",
+ device));
+
+ deviceExtension = DeviceGetExtension(device);
+
+ FREE_POOL(deviceExtension->DeviceName.Buffer);
+ RtlInitUnicodeString(&deviceExtension->DeviceName, NULL);
+
+ if (deviceExtension->DeviceAdditionalData.WellKnownName.Buffer != NULL)
+ {
+ IoDeleteSymbolicLink(&deviceExtension->DeviceAdditionalData.WellKnownName);
+ }
+
+ FREE_POOL(deviceExtension->DeviceAdditionalData.WellKnownName.Buffer);
+ RtlInitUnicodeString(&deviceExtension->DeviceAdditionalData.WellKnownName, NULL);
+
+ return;
+}
+
+
+VOID
+DeviceEvtSelfManagedIoCleanup(
+ _In_ WDFDEVICE Device
+ )
+/*++
+
+Routine Description:
+
+ this function is called when the device is removed.
+ release the ownership of the device, release allocated resources.
+
+Arguments:
+
+ Device - Handle to device object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS status;
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+
+ PAGED_CODE ();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
+ "DeviceEvtSelfManagedIoCleanup: WDFDEVICE %p is being stopped.\n",
+ Device));
+
+ // extract the device and driver extensions
+ deviceExtension = DeviceGetExtension(Device);
+
+ // Purge unprocessed requests, stop the IO queues.
+ // Incoming request will be completed with STATUS_INVALID_DEVICE_STATE status.
+ WdfIoQueuePurge(deviceExtension->SerialIOQueue, WDF_NO_EVENT_CALLBACK, WDF_NO_CONTEXT);
+ WdfIoQueuePurge(deviceExtension->CreateQueue, WDF_NO_EVENT_CALLBACK, WDF_NO_CONTEXT);
+
+ // Close the IoTarget so that we are sure there are no outstanding I/Os in the stack.
+ if (deviceExtension->IoTarget)
+ {
+ WdfIoTargetClose(deviceExtension->IoTarget);
+ }
+
+ // Release the device
+ if (!deviceExtension->SurpriseRemoved)
+ {
+ status = DeviceClaimRelease(deviceExtension, TRUE); //status is mainly for debugging. we don't really care.
+ UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+ }
+
+ // Be sure to flush the DPCs as the READ/WRITE timer routine may still be running
+ // during device removal. This call may take a while to complete.
+ KeFlushQueuedDpcs();
+
+ // Release all the memory that we have allocated.
+
+ DeviceDeallocateMmcResources(Device);
+ ScratchBuffer_Deallocate(deviceExtension);
+ RtlZeroMemory(&(deviceExtension->DeviceAdditionalData.Mmc), sizeof(CDROM_MMC_EXTENSION));
+
+ FREE_POOL(deviceExtension->DeviceDescriptor);
+ FREE_POOL(deviceExtension->AdapterDescriptor);
+ FREE_POOL(deviceExtension->PowerDescriptor);
+ FREE_POOL(deviceExtension->SenseData);
+
+ if (deviceExtension->DeviceAdditionalData.CachedInquiryData != NULL)
+ {
+ FREE_POOL(deviceExtension->DeviceAdditionalData.CachedInquiryData);
+ deviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount = 0;
+ }
+
+ FREE_POOL(deviceExtension->PrivateFdoData);
+
+ DeviceReleaseMcnResources(deviceExtension);
+
+ DeviceReleaseZPODDResources(deviceExtension);
+
+ // Keep the system-wide CDROM count accurate, as programs use this info to
+ // know when they have found all the cdroms in a system.
+ IoGetConfigurationInformation()->CdRomCount--;
+
+ deviceExtension->PartitionLength.QuadPart = 0;
+
+ // All WDF objects related to Device will be automatically released
+ // when the root object is deleted. No need to release them manually.
+
+ return;
+}
+
+NTSTATUS
+DeviceEvtD0Entry(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+
+Routine Description:
+
+ This function is called when the device is coming back from a lower power state to D0.
+ This function cannot be placed in a pageable section.
+
+Arguments:
+
+ Device - Handle to device object
+ PreviousState - Power state the device was in.
+
+Return Value:
+
+ NTSTATUS: alway STATUS_SUCCESS
+
+--*/
+{
+ PCDROM_DEVICE_EXTENSION deviceExtension;
+ NTSTATUS status = STATUS_SUCCESS;
+ PZERO_POWER_ODD_INFO zpoddInfo = NULL;
+ STORAGE_IDLE_POWERUP_REASON powerupReason = {0};
+
+ UNREFERENCED_PARAMETER(PreviousState);
+ deviceExtension = DeviceGetExtension(Device);
+
+ // Make certain not to do anything before properly initialized
+ if (deviceExtension->IsInitialized)
+ {
+ zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+
+ if (zpoddInfo != NULL)
+ {
+ if (zpoddInfo->InZeroPowerState != FALSE)
+ {
+ // We just woke up from Zero Power state
+ zpoddInfo->InZeroPowerState = FALSE;
+ zpoddInfo->RetryFirstCommand = TRUE;
+ zpoddInfo->BecomingReadyRetryCount = BECOMING_READY_RETRY_COUNT;
+
+ status = DeviceZPODDGetPowerupReason(deviceExtension, &powerupReason);
+
+ if (NT_SUCCESS(status) &&
+ (powerupReason.PowerupReason == StoragePowerupDeviceAttention))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceEvtD0Entry: Device has left zero power state due to eject button pressed\n"
+ ));
+
+ // This wake-up is caused by user pressing the eject button.
+ // In case of drawer type, we need to soft eject the tray to emulate the effect.
+ // Note that the first command to the device after power resumed will
+ // be terminated with CHECK CONDITION status with sense code 6/29/00,
+ // but we already have a retry logic to handle this.
+ if ((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0)) // Drawer
+ {
+ DeviceSendIoctlAsynchronously(deviceExtension, IOCTL_STORAGE_EJECT_MEDIA, deviceExtension->DeviceObject);
+ }
+ }
+ else
+ {
+ // This wake-up is caused by non-cached CDB received or a 3rd-party driver
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "DeviceEvtD0Entry: Device has left zero power state due to IO received\n"
+ ));
+
+ }
+ }
+ }
+
+ DeviceEnableMainTimer(deviceExtension);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DeviceEvtD0Exit(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE TargetState
+ )
+/*++
+
+Routine Description:
+
+ This function is called when the device is entering lower powe state from D0 or it's removed.
+ We only care about the case of device entering D3.
+ The purpose of this function is to send SYNC CACHE command and STOP UNIT command if it's necessary.
+
+Arguments:
+
+ Device - Handle to device object
+ TargetState - Power state the device is entering.
+
+Return Value:
+
+ NTSTATUS: alway STATUS_SUCCESS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+ PZERO_POWER_ODD_INFO zpoddInfo = NULL;
+
+ PAGED_CODE ();
+
+ deviceExtension = DeviceGetExtension(Device);
+ zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+
+ // we only process the situation that the device is going into D3.
+ if ((TargetState != WdfPowerDeviceD3) &&
+ (TargetState != WdfPowerDeviceD3Final))
+ {
+ return STATUS_SUCCESS;
+ }
+
+ // Stop the main timer
+ DeviceDisableMainTimer(deviceExtension);
+
+ // note: do not stop CreateQueue as the create request can be handled by port driver even the device is in D3 status.
+
+ // If initialization was not finished or the device was removed, we cannot interact
+ // with it device, so we have to exit
+ if ((!deviceExtension->IsInitialized) || deviceExtension->SurpriseRemoved)
+ {
+ return STATUS_SUCCESS;
+ }
+
+
+#ifdef DBG
+ #if (WINVER >= 0x0601)
+ // this API is introduced in Windows7
+ {
+ ULONG secondsRemaining = 0;
+ BOOLEAN watchdogTimeSupported = FALSE;
+
+ watchdogTimeSupported = PoQueryWatchdogTime(deviceExtension->LowerPdo, &secondsRemaining);
+ UNREFERENCED_PARAMETER(watchdogTimeSupported);
+ }
+ #endif
+#endif
+
+ deviceExtension->PowerDownInProgress = TRUE;
+
+ status = PowerContextBeginUse(deviceExtension);
+
+ deviceExtension->PowerContext.Options.PowerDown = TRUE;
+
+ // Step 1. LOCK QUEUE
+ if (NT_SUCCESS(status) &&
+ (TargetState != WdfPowerDeviceD3Final))
+ {
+ status = DeviceSendPowerDownProcessRequest(deviceExtension, NULL, NULL);
+
+ if (NT_SUCCESS(status))
+ {
+ deviceExtension->PowerContext.Options.LockQueue = TRUE;
+ }
+
+ // Ignore failure.
+ status = STATUS_SUCCESS;
+ }
+
+ deviceExtension->PowerContext.PowerChangeState.PowerDown++;
+
+ // Step 2. QUIESCE QUEUE
+ if (NT_SUCCESS(status) &&
+ (TargetState != WdfPowerDeviceD3Final))
+ {
+ status = DeviceSendPowerDownProcessRequest(deviceExtension, NULL, NULL);
+ UNREFERENCED_PARAMETER(status);
+ // We don't care about the status.
+ status = STATUS_SUCCESS;
+ }
+
+ deviceExtension->PowerContext.PowerChangeState.PowerDown++;
+
+ // Step 3. SYNC CACHE command should be sent to drive if the media is currently writable.
+ if (NT_SUCCESS(status) &&
+ deviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
+ {
+ status = DeviceSendPowerDownProcessRequest(deviceExtension, NULL, NULL);
+ UNREFERENCED_PARAMETER(status);
+ status = STATUS_SUCCESS;
+ }
+
+ deviceExtension->PowerContext.PowerChangeState.PowerDown++;
+
+ // Step 4. STOP UNIT
+ if (NT_SUCCESS(status) &&
+ !TEST_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_DISABLE_SPIN_DOWN))
+ {
+ status = DeviceSendPowerDownProcessRequest(deviceExtension, NULL, NULL);
+ UNREFERENCED_PARAMETER(status);
+ status = STATUS_SUCCESS;
+ }
+
+ if (TargetState == WdfPowerDeviceD3Final)
+ {
+ // We're done with the power context.
+ PowerContextEndUse(deviceExtension);
+ }
+
+ // Bumping the media change count will force the file system to verify the volume when we resume
+ SET_FLAG(deviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+ InterlockedIncrement((PLONG)&deviceExtension->MediaChangeCount);
+
+ // If this power down is caused by Zero Power ODD, we should mark the device as in ZPODD mode.
+ if (zpoddInfo != NULL)
+ {
+ zpoddInfo->InZeroPowerState = TRUE;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+ "Device has entered zero power state\n"
+ ));
+ }
+
+ deviceExtension->PowerDownInProgress = FALSE;
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DeviceEvtSurpriseRemoval(
+ _In_ WDFDEVICE Device
+ )
+/*++
+
+Routine Description:
+
+ this function is called when the device is surprisely removed.
+ Stop all IO queues so that there will be no more request being sent down.
+
+Arguments:
+
+ Device - Handle to device object
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+
+ PAGED_CODE();
+
+ deviceExtension = DeviceGetExtension(Device);
+
+ deviceExtension->SurpriseRemoved = TRUE;
+
+ // Stop the main timer
+ DeviceDisableMainTimer(deviceExtension);
+
+ // legacy behavior to set partition length to be 0.
+ deviceExtension->PartitionLength.QuadPart = 0;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "Surprisely remove a WDFDEVICE %p\n", Device));
+
+ return;
+}
+
+
+VOID
+CreateQueueEvtIoDefault(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request
+ )
+/*++
+
+Routine Description:
+
+ this function is called when CREATE irp comes.
+ setup FileObject context fields, so it can be used to track MCN or exclusive lock/unlock.
+
+Arguments:
+
+ Queue - Handle to device queue
+
+ Request - the creation request
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request);
+ WDFDEVICE device = WdfIoQueueGetDevice(Queue);
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+ PFILE_OBJECT_CONTEXT fileObjectContext = NULL;
+
+ PAGED_CODE();
+
+ if (fileObject == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_QUEUE,
+ "Error: received a file create request with file object set to NULL\n"));
+
+ RequestCompletion(deviceExtension, Request, STATUS_INTERNAL_ERROR, 0);
+ return;
+ }
+
+ fileObjectContext = FileObjectGetContext(fileObject);
+
+ // Initialize this WDFFILEOBJECT's context
+ fileObjectContext->DeviceObject = device;
+ fileObjectContext->FileObject = fileObject;
+ fileObjectContext->LockCount = 0;
+ fileObjectContext->McnDisableCount = 0;
+ fileObjectContext->EnforceStreamingRead = FALSE;
+ fileObjectContext->EnforceStreamingWrite = FALSE;
+
+ // send down the create synchronously
+ status = DeviceSendRequestSynchronously(device, Request, FALSE);
+
+ // Need to complete the request in this routine.
+ RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+
+ return;
+}
+
+VOID
+DeviceEvtFileClose(
+ _In_ WDFFILEOBJECT FileObject
+ )
+/*++
+
+Routine Description:
+
+ this function is called when CLOSE irp comes.
+ clean up MCN / Lock if necessary
+
+Arguments:
+
+ FileObject - WDF file object created for the irp.
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if (FileObject != NULL)
+ {
+ WDFDEVICE device = WdfFileObjectGetDevice(FileObject);
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+ PFILE_OBJECT_CONTEXT fileObjectContext = FileObjectGetContext(FileObject);
+
+ // cleanup locked media tray
+ status = DeviceCleanupProtectedLocks(deviceExtension, fileObjectContext);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "Failed to cleanup protected locks for WDFDEVICE %p, %!STATUS!\n", device, status));
+ }
+
+ // cleanup disabled MCN
+ status = DeviceCleanupDisableMcn(deviceExtension, fileObjectContext);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "Failed to disable MCN for WDFDEVICE %p, %!STATUS!\n", device, status));
+ }
+
+ // cleanup exclusive access
+ if (EXCLUSIVE_MODE(cdData) && EXCLUSIVE_OWNER(cdData, FileObject))
+ {
+ status = DeviceUnlockExclusive(deviceExtension, FileObject, FALSE);
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "Failed to release exclusive lock for WDFDEVICE %p, %!STATUS!\n", device, status));
+ }
+ }
+ }
+
+ return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceCleanupProtectedLocks(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ PFILE_OBJECT_CONTEXT FileObjectContext
+ )
+/*++
+
+Routine Description:
+
+ this function removes protected locks for the handle
+
+Arguments:
+
+ DeviceExtension - device context
+
+ FileObject - WDF file object created for the irp.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "CleanupProtectedLocks called for WDFDEVICE %p, WDFFILEOBJECT %p, locked %d times.\n",
+ DeviceExtension->Device, FileObjectContext->FileObject, FileObjectContext->LockCount));
+
+ // Synchronize with ejection and ejection control requests.
+ WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
+
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ if (FileObjectContext->LockCount != 0)
+ {
+ DeviceExtension->ProtectedLockCount -= FileObjectContext->LockCount;
+ FileObjectContext->LockCount = 0;
+
+ // If the new lock count has been dropped to zero then issue a lock
+ // command to the device.
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "FDO secured lock count = %d "
+ "lock count = %d\n",
+ DeviceExtension->ProtectedLockCount,
+ DeviceExtension->LockCount));
+
+ if ((DeviceExtension->ProtectedLockCount == 0) && (DeviceExtension->LockCount == 0))
+ {
+ SCSI_REQUEST_BLOCK srb = {0};
+ PCDB cdb = (PCDB) &(srb.Cdb);
+
+ srb.CdbLength = 6;
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+
+ // TRUE - prevent media removal.
+ // FALSE - allow media removal.
+ cdb->MEDIA_REMOVAL.Prevent = FALSE;
+
+ // Set timeout value.
+ srb.TimeOutValue = DeviceExtension->TimeOutValue;
+ status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+ &srb,
+ NULL,
+ 0,
+ FALSE,
+ NULL);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "Allow media removal (unlock) request to drive returned %!STATUS!\n",
+ status));
+ }
+ }
+
+ WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceCleanupDisableMcn(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ PFILE_OBJECT_CONTEXT FileObjectContext
+ )
+/*++
+
+Routine Description:
+
+ cleanup the MCN disable count for the handle
+
+Arguments:
+
+ DeviceExtension - device context
+
+ FileObject - WDF file object created for the irp.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+ "CleanupDisableMcn called for WDFDEVICE %p, WDFFILEOBJECT %p, locked %d times.\n",
+ DeviceExtension->Device, FileObjectContext->FileObject, FileObjectContext->McnDisableCount));
+
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ while (FileObjectContext->McnDisableCount != 0)
+ {
+ DeviceEnableMediaChangeDetection(DeviceExtension, FileObjectContext, TRUE);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+NormalizeIoctl(
+ _Inout_ PWDF_REQUEST_PARAMETERS requestParameters
+ )
+{
+ ULONG ioctlCode;
+ ULONG baseCode;
+ ULONG functionCode;
+
+ // if this is a class driver ioctl then we need to change the base code
+ // to IOCTL_STORAGE_BASE so that the switch statement can handle it.
+ //
+ // WARNING - currently the scsi class ioctl function codes are between
+ // 0x200 & 0x300. this routine depends on that fact
+ ioctlCode = requestParameters->Parameters.DeviceIoControl.IoControlCode;
+ baseCode = DEVICE_TYPE_FROM_CTL_CODE(ioctlCode);
+ functionCode = (ioctlCode & (~0xffffc003)) >> 2;
+
+ if ((baseCode == IOCTL_SCSI_BASE) ||
+ (baseCode == IOCTL_DISK_BASE) ||
+ (baseCode == IOCTL_TAPE_BASE) ||
+ (baseCode == IOCTL_DVD_BASE) ||
+ (baseCode == IOCTL_CDROM_BASE))
+ //IOCTL_STORAGE_BASE does not need to be converted.
+ {
+ if((functionCode >= 0x200) && (functionCode <= 0x300))
+ {
+ ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_STORAGE_BASE, 0, 0, 0);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+ "IOCTL code recalibrate, New ioctl code is %lx\n",
+ ioctlCode));
+
+ // Set the code into request parameters, then "requestParameters" needs to be used for dispatch functions.
+ requestParameters->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
+ }
+ }
+}
+
+VOID
+DeviceEvtIoInCallerContext(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine Description:
+
+ Responds to EvtIoInCallerContext events from KMDF
+ It calls different functions to process different type of IOCTLs.
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ VOID.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+ WDF_REQUEST_PARAMETERS requestParameters;
+
+ requestContext->DeviceExtension = deviceExtension;
+
+ // set the received time
+ RequestSetReceivedTime(Request);
+
+ // get the request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ if (requestParameters.Type == WdfRequestTypeDeviceControl)
+ {
+ BOOLEAN processed = FALSE;
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+ PMEDIA_CHANGE_DETECTION_INFO info = deviceExtension->MediaChangeDetectionInfo;
+
+ if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL)
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "Receiving IOCTL: %lx\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+ "Receiving IOCTL: %lx\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ }
+
+ // If the device is in exclusive mode, check whether the request is from
+ // the handle that locked the device.
+ if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
+ {
+ BOOLEAN isBlocked = FALSE;
+
+ status = RequestIsIoctlBlockedByExclusiveAccess(Request, &isBlocked);
+ UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+
+ if (isBlocked)
+ {
+ if ((requestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EVENT_NOTIFICATION) &&
+ (info != NULL) && (info->AsynchronousNotificationSupported != FALSE))
+ {
+ // If AN is supported and we receive a signal but we can't send down GESN
+ // due to exclusive lock, we should save it and fire a GESN when it's unlocked.
+ // We just need true/false here and don't need count because we will keep sending
+ // GESN until we deplete all events.
+ info->ANSignalPendingDueToExclusiveLock = TRUE;
+ }
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+ "Access Denied! Device in exclusive mode.Failing Ioctl %lx\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ RequestCompletion(deviceExtension, Request, STATUS_ACCESS_DENIED, 0);
+
+ return;
+ }
+ }
+
+ NormalizeIoctl(&requestParameters);
+
+ // 1. All requests that don't need to access device can be processed immediately
+ if (!processed)
+ {
+ processed = RequestDispatchProcessDirectly(Device, Request, requestParameters);
+ }
+
+ // 2. Requests that should be put in sequential queue.
+ if (!processed)
+ {
+ processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
+ }
+
+ // 3. Requests that need to be processed sequentially and in caller's context.
+ if (!processed)
+ {
+ processed = RequestDispatchSyncWithSequentialQueue(Device, Request, requestParameters);
+ }
+
+ // 4. Special requests that needs different process in different cases.
+ if (!processed)
+ {
+ processed = RequestDispatchSpecialIoctls(Device, Request, requestParameters);
+ }
+
+ // 5. This is default behavior for unknown IOCTLs. To pass it to lower level.
+ if (!processed)
+ {
+ processed = RequestDispatchUnknownRequests(Device, Request, requestParameters);
+ }
+
+ // All requests should be processed already.
+ NT_ASSERT(processed);
+ UNREFERENCED_PARAMETER(processed); //defensive coding, avoid PREFAST warning.
+ }
+ else if (requestParameters.Type == WdfRequestTypeDeviceControlInternal)
+ {
+ RequestProcessInternalDeviceControl(Request, deviceExtension);
+ }
+ else
+ {
+ // Requests other than IOCTLs will be forwarded to default queue.
+ status = WdfDeviceEnqueueRequest(Device, Request);
+ if (!NT_SUCCESS(status))
+ {
+ RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+ }
+ }
+
+ return;
+}
+
+
+BOOLEAN
+RequestDispatchProcessDirectly(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request,
+ _In_ WDF_REQUEST_PARAMETERS RequestParameters
+ )
+/*++
+Routine Description:
+
+ These requests can be processed in a non-serialized manner, most of them don't need to access device.
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+ RequestParameters - request parameters
+
+Return Value:
+
+ BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN processed = FALSE;
+ size_t dataLength = 0;
+
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+ switch (ioctlCode)
+ {
+
+ case IOCTL_CDROM_GET_INQUIRY_DATA:
+ {
+ status = RequestHandleGetInquiryData(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:
+ {
+ status = RequestHandleGetMediaTypeEx(deviceExtension, Request, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
+ {
+ status = RequestHandleMountQueryUniqueId(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+ {
+ status = RequestHandleMountQueryDeviceName(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
+ {
+ status = RequestHandleMountQuerySuggestedLinkName(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_GET_DEVICE_NUMBER:
+ {
+ status = RequestHandleGetDeviceNumber(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_GET_HOTPLUG_INFO:
+ {
+ status = RequestHandleGetHotPlugInfo(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_SET_HOTPLUG_INFO:
+ {
+ status = RequestHandleSetHotPlugInfo(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_EVENT_NOTIFICATION:
+ {
+ status = RequestHandleEventNotification(deviceExtension, Request, &RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ case IOCTL_VOLUME_ONLINE:
+ {
+ //
+ // Mount manager and volume manager will
+ // follow this online with a post online
+ // but other callers may not. In those
+ // cases, we process this request right
+ // away. We approximate that these other
+ // callers are from user mode
+ //
+
+ if (WdfRequestGetRequestorMode(Request) == KernelMode)
+ {
+ processed = TRUE;
+ }
+ break;
+ }
+#endif
+
+ default:
+ {
+ processed = FALSE;
+ break;
+ }
+
+ } //end of switch (ioctlCode)
+
+ if (processed)
+ {
+ UCHAR currentStackLocationFlags = 0;
+ currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+ if ((status == STATUS_VERIFY_REQUIRED) &&
+ (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+ {
+ // If the status is verified required and this request
+ // should bypass verify required then retry the request.
+ status = STATUS_IO_DEVICE_ERROR;
+ UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+ processed = RequestDispatchProcessDirectly(Device, Request, RequestParameters);
+ }
+ else
+ {
+ // Complete the request after processing it.
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+ }
+
+ return processed;
+}
+
+
+BOOLEAN
+RequestDispatchToSequentialQueue(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request,
+ _In_ WDF_REQUEST_PARAMETERS RequestParameters
+ )
+/*++
+Routine Description:
+
+ These requests can be processed in a non-serialized manner, most of them don't need to access device.
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+ RequestParameters - request parameters
+
+Return Value:
+
+ BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN processed = FALSE;
+ size_t dataLength = 0;
+ BOOLEAN inZeroPowerState = FALSE;
+
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+ PZERO_POWER_ODD_INFO zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+
+ if ((zpoddInfo != NULL) &&
+ (zpoddInfo->InZeroPowerState != FALSE))
+ {
+ inZeroPowerState = TRUE;
+ }
+
+ switch (ioctlCode)
+ {
+
+ case IOCTL_CDROM_RAW_READ:
+ {
+ status = RequestValidateRawRead(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get drive geometryEx\n"));
+ if ( RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ (ULONG)FIELD_OFFSET(DISK_GEOMETRY_EX, Data))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
+ }
+ else if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get drive geometry\n"));
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(DISK_GEOMETRY))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(DISK_GEOMETRY);
+ }
+ else if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_READ_TOC_EX:
+ {
+ status = RequestValidateReadTocEx(deviceExtension, Request, RequestParameters, &dataLength);
+
+ if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_READ_TOC:
+ {
+ status = RequestValidateReadToc(deviceExtension, RequestParameters, &dataLength);
+
+ if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_GET_LAST_SESSION:
+ {
+ status = RequestValidateGetLastSession(deviceExtension, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_PLAY_AUDIO_MSF:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Play audio MSF\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_PLAY_AUDIO_MSF))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_SEEK_AUDIO_MSF:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Seek audio MSF\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_SEEK_AUDIO_MSF))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_PAUSE_AUDIO:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Pause audio\n"));
+
+ status = STATUS_SUCCESS;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_RESUME_AUDIO:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Resume audio\n"));
+
+ status = STATUS_SUCCESS;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_READ_Q_CHANNEL:
+ {
+ status = RequestValidateReadQChannel(Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_GET_VOLUME:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get volume control\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(VOLUME_CONTROL))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(VOLUME_CONTROL);
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_SET_VOLUME:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Set volume control\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(VOLUME_CONTROL))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_STOP_AUDIO:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Stop audio\n"));
+
+ status = STATUS_SUCCESS;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_STORAGE_CHECK_VERIFY2:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] Check Verify\n", Request));
+
+ // Following check will let the condition "OutputBufferLength == 0" pass.
+ // Since it's legacy behavior in classpnp, we need to keep it.
+ if ((RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0) &&
+ (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(ULONG);
+ }
+ else if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_GET_REGION:
+ {
+ // validation will be done when process it.
+ status = STATUS_SUCCESS;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_READ_STRUCTURE:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_READ_STRUCTURE\n", Request));
+
+ status = RequestValidateDvdReadStructure(deviceExtension, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_READ_KEY:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_READ_KEY\n", Request));
+
+ status = RequestValidateDvdReadKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_START_SESSION:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_START_SESSION\n", Request));
+
+ status = RequestValidateDvdStartSession(deviceExtension, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_SEND_KEY:
+ case IOCTL_DVD_SEND_KEY2:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_SEND_KEY\n", Request));
+
+ status = RequestValidateDvdSendKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_SET_READ_AHEAD:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] SetReadAhead\n", Request));
+
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(STORAGE_SET_READ_AHEAD))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_IS_WRITABLE:
+ {
+ status = STATUS_SUCCESS;
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_LAYOUT:
+ {
+ ULONG requiredSize = 0;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get drive layout\n"));
+
+ requiredSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ requiredSize)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = requiredSize;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
+ {
+ ULONG requiredSize = 0;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get drive layoutEx\n"));
+
+ requiredSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ requiredSize)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = requiredSize;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_PARTITION_INFO:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get Partition Info\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(PARTITION_INFORMATION))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(PARTITION_INFORMATION);
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_PARTITION_INFO_EX:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Get Partition InfoEx\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(PARTITION_INFORMATION_EX))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(PARTITION_INFORMATION_EX);
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_VERIFY:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: IOCTL_DISK_VERIFY to device %p through request %p\n",
+ Device,
+ Request));
+
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(VERIFY_INFORMATION))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DISK_GET_LENGTH_INFO:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: Disk Get Length InfoEx\n"));
+
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(GET_LENGTH_INFORMATION))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ dataLength = sizeof(GET_LENGTH_INFORMATION);
+ }
+ else if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_GET_CONFIGURATION:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_GET_CONFIGURATION\n", Request));
+
+ status = RequestValidateGetConfiguration(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_SET_SPEED:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_SET_SPEED\n", Request));
+
+ status = RequestValidateSetSpeed(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_DVD_END_SESSION:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_END_SESSION\n", Request));
+
+ status = RequestValidateDvdEndSession(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_END_SESSION:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_AACS_END_SESSION\n", Request));
+
+ status = RequestValidateAacsEndSession(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_READ_MEDIA_KEY_BLOCK:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Querying full MKB with bufferSize of %x bytes\n",
+ (int)RequestParameters.Parameters.DeviceIoControl.OutputBufferLength
+ ));
+
+ status = RequestValidateAacsReadMediaKeyBlock(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_START_SESSION:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Requesting AGID\n"
+ ));
+
+ status = RequestValidateAacsStartSession(deviceExtension, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_SEND_CERTIFICATE:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Sending host certificate to drive\n"
+ ));
+
+ status = RequestValidateAacsSendCertificate(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_GET_CERTIFICATE:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Querying drive certificate\n"
+ ));
+
+ status = RequestValidateAacsGetCertificate(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_GET_CHALLENGE_KEY:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Querying drive challenge key\n"
+ ));
+
+ status = RequestValidateAacsGetChallengeKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_SEND_CHALLENGE_KEY:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Sending drive challenge key\n"
+ ));
+
+ status = RequestValidateAacsSendChallengeKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_READ_VOLUME_ID:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Reading volume ID\n"
+ ));
+
+ status = RequestValidateAacsReadVolumeId(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_READ_SERIAL_NUMBER:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Reading Serial Number\n"
+ ));
+
+ status = RequestValidateAacsReadSerialNumber(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_READ_MEDIA_ID:
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Reading media ID\n"
+ ));
+
+ status = RequestValidateAacsReadMediaId(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_AACS_READ_BINDING_NONCE:
+ case IOCTL_AACS_GENERATE_BINDING_NONCE:
+ {
+ if (ioctlCode == IOCTL_AACS_GENERATE_BINDING_NONCE)
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Generating new binding nonce\n"
+ ));
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "AACS: Reading existing binding nonce\n"
+ ));
+ }
+
+ status = RequestValidateAacsBindingNonce(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_ENABLE_STREAMING:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_ENABLE_STREAMING\n", Request));
+
+ status = RequestValidateEnableStreaming(Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_SEND_OPC_INFORMATION:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_SEND_OPC_INFORMATION\n", Request));
+
+ status = RequestValidateSendOpcInformation(Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_CDROM_GET_PERFORMANCE:
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_GET_PERFORMANCE\n", Request));
+
+ status = RequestValidateGetPerformance(Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_MEDIA_REMOVAL:
+ case IOCTL_STORAGE_EJECTION_CONTROL:
+ {
+ if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_MCN_CONTROL:
+ {
+ if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_RESERVE:
+ case IOCTL_STORAGE_RELEASE:
+ {
+ // there is no validate check currently.
+ status = STATUS_SUCCESS;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT:
+ {
+ status = RequestValidatePersistentReserve(deviceExtension, Request, RequestParameters, &dataLength);
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_EJECT_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA2:
+ {
+ status = STATUS_SUCCESS;
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_FIND_NEW_DEVICES:
+ {
+ // process it.
+ IoInvalidateDeviceRelations(deviceExtension->LowerPdo, BusRelations);
+
+ status = STATUS_SUCCESS;
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_READ_CAPACITY:
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_READ_CAPACITY))
+ {
+ dataLength = sizeof(STORAGE_READ_CAPACITY);
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else if (inZeroPowerState != FALSE)
+ {
+ status = STATUS_NO_MEDIA_IN_DEVICE;
+ }
+ else
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ case IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT:
+ {
+ // for disk.sys only in original classpnp
+ status = STATUS_NOT_SUPPORTED;
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ case IOCTL_DISK_ARE_VOLUMES_READY:
+ {
+ // this request doesn't access device at all, so seemingly it can be processed
+ // directly; however, in case volume online is not received, we will need to
+ // park these requests in a queue, and the only way a request can be queued is
+ // if the request came out of another queue.
+ status = STATUS_SUCCESS;
+
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_VOLUME_ONLINE:
+ case IOCTL_VOLUME_POST_ONLINE:
+ {
+ status = STATUS_SUCCESS;
+
+ processed = TRUE;
+ break;
+ }
+#endif
+
+ default:
+ {
+ processed = FALSE;
+ break;
+ }
+ } //end of switch (ioctlCode)
+
+ if (processed)
+ {
+ UCHAR currentStackLocationFlags = 0;
+ currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+ if ((status == STATUS_VERIFY_REQUIRED) &&
+ (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+ {
+ // If the status is verified required and this request
+ // should bypass verify required then retry the request.
+ status = STATUS_IO_DEVICE_ERROR;
+ UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+ processed = RequestDispatchToSequentialQueue(Device, Request, RequestParameters);
+ }
+ else
+ {
+ if (NT_SUCCESS(status))
+ {
+ // Forward the request to serialized queue.
+ status = WdfDeviceEnqueueRequest(Device, Request);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ // Validation failed / forward failed, complete the request.
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+ }
+ }
+
+ return processed;
+}
+
+
+BOOLEAN
+RequestDispatchSyncWithSequentialQueue(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request,
+ _In_ WDF_REQUEST_PARAMETERS RequestParameters
+ )
+/*++
+Routine Description:
+
+ These requests need to stay in caller's context and be processed in serialized manner.
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+ RequestParameters - request parameters
+
+Return Value:
+
+ BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN processed = FALSE;
+ size_t dataLength = 0;
+
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+ switch (ioctlCode)
+ {
+
+ case IOCTL_CDROM_EXCLUSIVE_ACCESS:
+ {
+ //1. Validate
+ status = RequestValidateExclusiveAccess(Request, RequestParameters, &dataLength);
+
+ //2. keep the request in serialized manner and stay in user's context.
+ if (NT_SUCCESS(status))
+ {
+ PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+ &exclusiveAccess,
+ NULL);
+
+ if (NT_SUCCESS(status))
+ {
+ // do not need to check "status" as it passed validation and cannot fail in WdfRequestRetrieveInputBuffer()
+ switch (exclusiveAccess->RequestType)
+ {
+
+ case ExclusiveAccessQueryState:
+ {
+ status = RequestSetContextFields(Request, RequestHandleExclusiveAccessQueryLockState);
+ break;
+ }
+
+ case ExclusiveAccessLockDevice:
+ {
+ status = RequestSetContextFields(Request, RequestHandleExclusiveAccessLockDevice);
+ break;
+ }
+
+ case ExclusiveAccessUnlockDevice:
+ {
+ status = RequestSetContextFields(Request, RequestHandleExclusiveAccessUnlockDevice);
+ break;
+ }
+ default:
+ {
+ // already valicated in RequestValidateExclusiveAccess()
+ NT_ASSERT(FALSE);
+ break;
+ }
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ // now, put the special synchronization information into the context
+ status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+
+ // "status" is used for debugging in above statement, reset to success to avoid further work in this function.
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ processed = TRUE;
+ break; // complete the irp
+ }
+
+ default:
+ {
+ processed = FALSE;
+ break;
+ }
+ } //end of switch (ioctlCode)
+
+ // Following process is only valid if the request is not really processed. (failed in validation)
+ if (processed && !NT_SUCCESS(status))
+ {
+ UCHAR currentStackLocationFlags = 0;
+ currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+ if ((status == STATUS_VERIFY_REQUIRED) &&
+ (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+ {
+ //
+ // If the status is verified required and this request
+ // should bypass verify required then retry the request.
+ //
+ status = STATUS_IO_DEVICE_ERROR;
+ UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+ processed = RequestDispatchSyncWithSequentialQueue(Device, Request, RequestParameters);
+ }
+ else
+ {
+ // Validation failed / forward failed, complete the request.
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+ }
+
+ return processed;
+}
+
+
+BOOLEAN
+RequestDispatchSpecialIoctls(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request,
+ _In_ WDF_REQUEST_PARAMETERS RequestParameters
+ )
+/*++
+Routine Description:
+
+ These requests need to be processed in different manner according to input parameters
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+ RequestParameters - request parameters
+
+Return Value:
+
+ BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN processed = FALSE;
+ size_t dataLength = 0;
+ BOOLEAN requestCompleted = FALSE;
+
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+ ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+ switch (ioctlCode)
+ {
+ case IOCTL_SCSI_PASS_THROUGH:
+ case IOCTL_SCSI_PASS_THROUGH_DIRECT:
+ case IOCTL_SCSI_PASS_THROUGH_EX:
+ case IOCTL_SCSI_PASS_THROUGH_DIRECT_EX:
+ {
+ // SPTI is considered special case as we need to set the MinorFunction before pass to low level.
+
+#if defined (_WIN64)
+ if (WdfRequestIsFrom32BitProcess(Request))
+ {
+ if ((ioctlCode == IOCTL_SCSI_PASS_THROUGH) || (ioctlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT))
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32_EX))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+#endif
+ {
+ if ((ioctlCode == IOCTL_SCSI_PASS_THROUGH) || (ioctlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT))
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH_EX))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ // validation failed.
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+ else
+ {
+ // keep the request in serialized manner and stay in user's context.
+ status = RequestSetContextFields(Request, RequestHandleScsiPassThrough);
+
+ if (NT_SUCCESS(status))
+ {
+ status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+ }
+ else
+ {
+ RequestCompletion(deviceExtension, Request, status, 0);
+ }
+ }
+
+ requestCompleted = TRUE;
+ processed = TRUE;
+ break;
+ }
+
+ case IOCTL_STORAGE_QUERY_PROPERTY:
+ {
+ if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ PSTORAGE_PROPERTY_QUERY inputBuffer = NULL;
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+ &inputBuffer,
+ NULL);
+
+ if (NT_SUCCESS(status))
+ {
+ if (!EXCLUSIVE_MODE(cdData) || // not locked
+ EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)) || // request is from lock owner
+ (inputBuffer->QueryType == PropertyExistsQuery)) // request not access device
+ {
+ if (inputBuffer->PropertyId == StorageDeviceUniqueIdProperty)
+ {
+ // previously handled in classpnp
+ // keep the request in serialized manner and stay in user's context.
+ status = RequestSetContextFields(Request, RequestHandleQueryPropertyDeviceUniqueId);
+
+ if (NT_SUCCESS(status))
+ {
+ status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+ // remeber that the request has been completed.
+ requestCompleted = TRUE;
+ }
+ }
+ else if (inputBuffer->PropertyId == StorageDeviceWriteCacheProperty)
+ {
+ // previously handled in classpnp
+ // keep the request in serialized manner and stay in user's context.
+ status = RequestSetContextFields(Request, RequestHandleQueryPropertyWriteCache);
+
+ if (NT_SUCCESS(status))
+ {
+ status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+ // remeber that the request has been completed.
+ requestCompleted = TRUE;
+ }
+ }
+ else
+ {
+ // Pass to port driver for handling
+ RequestDispatchUnknownRequests(Device, Request, RequestParameters);
+
+ // remeber that the request has been completed.
+ requestCompleted = TRUE;
+ }
+ }
+ else
+ {
+ // If cached data exists, return cached data. Otherwise, fail the request.
+ if ((inputBuffer->QueryType == PropertyStandardQuery) &&
+ ((inputBuffer->PropertyId == StorageDeviceProperty) || (inputBuffer->PropertyId == StorageAdapterProperty)) )
+ {
+ status = RequestHandleQueryPropertyRetrieveCachedData(deviceExtension, Request, RequestParameters, &dataLength);
+ }
+ else
+ {
+ status = STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+ }
+
+ processed = TRUE;
+ break;
+ }
+
+ // this IOCTL is a fake one, used for MCN process sync-ed with serial queue.
+ case IOCTL_MCN_SYNC_FAKE_IOCTL:
+ {
+ PIRP irp = WdfRequestWdmGetIrp(Request);
+
+ if ((deviceExtension->MediaChangeDetectionInfo != NULL) &&
+ (irp == deviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp) &&
+ (WdfRequestGetRequestorMode(Request) == KernelMode) &&
+ (RequestParameters.Parameters.Others.Arg1 == RequestSetupMcnSyncIrp) &&
+ (RequestParameters.Parameters.Others.Arg2 == RequestSetupMcnSyncIrp) &&
+ (RequestParameters.Parameters.Others.Arg4 == RequestSetupMcnSyncIrp))
+ {
+ // This is the requset we use to sync Media Change Detection with sequential queue.
+ status = WdfDeviceEnqueueRequest(Device, Request);
+
+ if (!NT_SUCCESS(status))
+ {
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+
+ requestCompleted = TRUE;
+ processed = TRUE;
+ }
+ else
+ {
+ // process as an unknown request.
+ processed = FALSE;
+ }
+ break;
+ }
+
+ default:
+ {
+ processed = FALSE;
+ break;
+ }
+ } //end of switch (ioctlCode)
+
+ if (processed && !requestCompleted)
+ {
+ UCHAR currentStackLocationFlags = 0;
+ currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+ if ((status == STATUS_VERIFY_REQUIRED) &&
+ (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+ {
+ // If the status is verified required and this request
+ // should bypass verify required then retry the request.
+ status = STATUS_IO_DEVICE_ERROR;
+ UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+ processed = RequestDispatchSpecialIoctls(Device, Request, RequestParameters);
+ }
+ else
+ {
+ RequestCompletion(deviceExtension, Request, status, dataLength);
+ }
+ }
+
+ return processed;
+}
+
+
+BOOLEAN
+RequestDispatchUnknownRequests(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request,
+ _In_ WDF_REQUEST_PARAMETERS RequestParameters
+ )
+/*++
+Routine Description:
+
+ All unknown requests will be pass to lower level driver.
+ If IRQL is PASSIVE_LEVEL, the request will be serialized;
+ Otherwise, it'll be sent and forget.
+
+Arguments:
+
+ Device - handle to a WDF Device object
+
+ Request - handle to the incoming WDF Request object
+
+ RequestParameters - request parameters
+
+Return Value:
+
+ BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+
+ ULONG baseCode = DEVICE_TYPE_FROM_CTL_CODE(RequestParameters.Parameters.DeviceIoControl.IoControlCode);
+
+ if ((KeGetCurrentIrql() != PASSIVE_LEVEL) ||
+ (baseCode == FILE_DEVICE_ACPI))
+ {
+ // 1. When IRQL is higher than PASSIVE_LEVEL,
+ // 2. ataport sends IOCTL_ACPI_ASYNC_EVAL_METHOD before queue starts,
+ // send request directly to lower driver.
+ status = RequestHandleUnknownIoctl(Device, Request);
+ }
+ else
+ {
+ // keep the request in serialized manner and stay in user's context.
+ status = RequestSetContextFields(Request, RequestHandleUnknownIoctl);
+
+ if (NT_SUCCESS(status))
+ {
+ status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+ }
+ else
+ {
+ RequestCompletion(deviceExtension, Request, status, 0);
+ }
+ }
+
+ UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+
+ // All unknown IOCTLs are processed in this function.
+ return TRUE; //processed
+}
+
+VOID
+RequestProcessInternalDeviceControl(
+ _In_ WDFREQUEST Request,
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+Routine Description:
+
+ all internal IOCTL will be send to lower driver asynchronously.
+
+Arguments:
+
+ Request - handle to the incoming WDF Request object
+ DeviceExtension - device extension structure
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIRP irp = NULL;
+ PIO_STACK_LOCATION irpStack = NULL;
+ PIO_STACK_LOCATION nextStack = NULL;
+ BOOLEAN requestSent = FALSE;
+
+ irp = WdfRequestWdmGetIrp(Request);
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+ nextStack = IoGetNextIrpStackLocation(irp);
+
+ // Set the parameters in the next stack location.
+ nextStack->Parameters.Scsi.Srb = irpStack->Parameters.Scsi.Srb;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+ nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
+
+ WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL);
+
+ status = RequestSend(DeviceExtension,
+ Request,
+ DeviceExtension->IoTarget,
+ 0,
+ &requestSent);
+
+ // send the request straight down (asynchronously)
+ if (!requestSent)
+ {
+ // fail the request
+ RequestCompletion(DeviceExtension, Request, status, WdfRequestGetInformation(Request));
+ }
+
+ return;
+}
+
+
+
+//
+// Serial I/O Queue Event callbacks
+//
+
+VOID
+SequentialQueueEvtIoReadWrite(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t Length
+ )
+/*++
+Routine Description:
+
+ validate and process read/write request.
+
+Arguments:
+
+ Queue - parallel queue itself
+
+ Request - handle to the incoming WDF Request object
+
+ Length - read / write lenght
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WDFDEVICE device = WdfIoQueueGetDevice(Queue);
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+ WDF_REQUEST_PARAMETERS requestParameters;
+ PIRP wdmIrp = WdfRequestWdmGetIrp(Request);
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(wdmIrp);
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+
+ // Get the request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ if (requestParameters.Type == WdfRequestTypeRead)
+ {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
+ "Receiving READ, Length %Ix\n", (ULONG) Length));
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
+ "Receiving WRITE, Length %Ix\n", (ULONG) Length));
+ }
+
+ // Check if a verify is required before a READ/WRITE
+ if (TEST_FLAG(deviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
+ (requestParameters.MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
+ !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
+ {
+ // DO_VERIFY_VOLUME is set for the device object,
+ // but this request is not itself a verify request.
+ // So fail this request.
+ RequestCompletion(deviceExtension, Request, STATUS_VERIFY_REQUIRED, 0);
+ }
+ else
+ {
+ // Since we've bypassed the verify-required tests we don't need to repeat
+ // them with this IRP - in particular we don't want to worry about
+ // hitting them at the partition 0 level if the request has come through
+ // a non-zero partition.
+ currentIrpStack->MinorFunction = CDROM_VOLUME_VERIFY_CHECKED;
+
+ // Fail READ/WRITE requests when music is playing
+ if (deviceExtension->DeviceAdditionalData.PlayActive)
+ {
+ RequestCompletion(deviceExtension, Request, STATUS_DEVICE_BUSY, 0);
+
+ return;
+ }
+
+ // Fail READ/WRITE requests from non-owners if the drive is locked
+ if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
+ {
+ RequestCompletion(deviceExtension, Request, STATUS_ACCESS_DENIED, 0);
+
+ return;
+ }
+
+ // Succeed READ/WRITE requests of length 0
+ if (Length == 0)
+ {
+ // Several parts of the code turn 0 into 0xffffffff,
+ // so don't process a zero-length request any further.
+ RequestCompletion(deviceExtension, Request, STATUS_SUCCESS, Length);
+
+ return;
+ }
+
+ // If there is an unexpected write request, we want to rediscover MMC capabilities
+ if (!deviceExtension->DeviceAdditionalData.Mmc.WriteAllowed &&
+ (requestParameters.Type == WdfRequestTypeWrite))
+ {
+ // Schedule MMC capabilities update now, but perform it later in a work item
+ deviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
+ }
+
+ // If MMC capabilities update is required, we create a separate work item to avoid blocking
+ // the current thread; otherwise, we initiate an async read/write in the current thread.
+ if (DeviceIsMmcUpdateRequired(deviceExtension->Device))
+ {
+ deviceExtension->ReadWriteWorkItemContext.OriginalRequest = Request;
+ WdfWorkItemEnqueue(deviceExtension->ReadWriteWorkItem);
+
+ status = STATUS_SUCCESS;
+ }
+ else
+ {
+ status = RequestValidateReadWrite(deviceExtension, Request, requestParameters);
+
+ if (NT_SUCCESS(status))
+ {
+ status = RequestHandleReadWrite(deviceExtension, Request, requestParameters);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ RequestCompletion(deviceExtension, Request, status, 0);
+ }
+ }
+
+ return;
+}
+
+
+VOID
+ReadWriteWorkItemRoutine(
+ _In_ WDFWORKITEM WorkItem
+ )
+/*++
+
+Routine Description:
+
+ Work item routine for validating and initiating read and write requests.
+ The reason why we do that from a work item is because we may need to update MMC
+ capabilities before validating a read/write request and that is a sync operation.
+
+Arguments:
+
+ WorkItem - WDF work item
+
+Return Value:
+
+ none
+
+--*/
+{
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+ WDFREQUEST readWriteRequest = NULL;
+ WDF_REQUEST_PARAMETERS readWriteRequestParameters;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE ();
+
+ deviceExtension = WdfObjectGetTypedContext(WdfWorkItemGetParentObject(WorkItem), CDROM_DEVICE_EXTENSION);
+ readWriteRequest = deviceExtension->ReadWriteWorkItemContext.OriginalRequest;
+ deviceExtension->ReadWriteWorkItemContext.OriginalRequest = NULL;
+
+ WDF_REQUEST_PARAMETERS_INIT(&readWriteRequestParameters);
+ WdfRequestGetParameters(readWriteRequest, &readWriteRequestParameters);
+
+ if (DeviceIsMmcUpdateRequired(deviceExtension->Device))
+ {
+ // Issue command to update the drive capabilities.
+ // The failure of MMC update is not considered critical, so we'll
+ // continue to process the request even if MMC update fails.
+ (VOID) DeviceUpdateMmcCapabilities(deviceExtension->Device);
+ }
+
+ // Now verify and process the request
+ if (NT_SUCCESS(status))
+ {
+ status = RequestValidateReadWrite(deviceExtension, readWriteRequest, readWriteRequestParameters);
+ }
+ if (NT_SUCCESS(status))
+ {
+ status = RequestHandleReadWrite(deviceExtension, readWriteRequest, readWriteRequestParameters);
+ }
+
+ // Complete the request immediately on failure
+ if (!NT_SUCCESS(status))
+ {
+ RequestCompletion(deviceExtension, readWriteRequest, status, 0);
+ }
+}
+
+
+VOID
+SequentialQueueEvtIoDeviceControl(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+Routine Description:
+
+ validate and process IOCTL request.
+
+Arguments:
+
+ Queue - sequential queue
+
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ WDFDEVICE device = WdfIoQueueGetDevice(Queue);
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+ PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+ WDF_REQUEST_PARAMETERS requestParameters;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(IoControlCode);
+
+ // get the request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ // If the device is in exclusive mode, check whether the request is from
+ // the handle that locked the device
+ if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
+ {
+ BOOLEAN isBlocked = FALSE;
+
+ status = RequestIsIoctlBlockedByExclusiveAccess(Request, &isBlocked);
+ if (NT_SUCCESS(status) && isBlocked)
+ {
+ if (requestContext->SyncRequired)
+ {
+ // set the following event, so RequestSynchronizeProcessWithSerialQueue() can contintue run to process the real request.
+ // this function will wait for the request process finishes.
+ KeSetEvent(requestContext->SyncEvent, IO_CD_ROM_INCREMENT, FALSE);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+ "DeviceEvtIoInCallerContext: Access Denied! Device in exclusive mode.Failing Ioctl %lx\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ RequestCompletion(deviceExtension, Request, STATUS_ACCESS_DENIED, 0);
+ }
+
+ return;
+ }
+ }
+
+ if (!cdData->Mmc.WriteAllowed &&
+ ((requestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_IS_WRITABLE) ||
+ (requestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_VERIFY)))
+ {
+ cdData->Mmc.UpdateState = CdromMmcUpdateRequired;
+ }
+
+ // check if this is a synchronized ioctl
+ if (requestContext->SyncRequired)
+ {
+ // set the following event, so RequestSynchronizeProcessWithSerialQueue() can contintue run to process the real request.
+ // this function will wait for the request process finishes.
+ KeSetEvent(requestContext->SyncEvent, IO_CD_ROM_INCREMENT, FALSE);
+ }
+ else
+ {
+ deviceExtension->IoctlWorkItemContext.OriginalRequest = Request;
+
+ // all other IOCTL processing is currently processed via a
+ // work item running at PASSIVE_LEVEL.
+ WdfWorkItemEnqueue(deviceExtension->IoctlWorkItem);
+ }
+
+ return;
+}
+
+
+VOID
+IoctlWorkItemRoutine(
+ _In_ WDFWORKITEM WorkItem
+ )
+/*++
+
+Routine Description:
+
+ Work item routine for processing ioctl requests.
+ This is needed because event callbacks are called at DISPATCH_LEVEL and ioctl
+ requests are currently processed synchronously and not asynchronously.
+
+Arguments:
+
+ WorkItem - WDF work item
+
+Return Value:
+
+ none
+
+--*/
+{
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+
+ PAGED_CODE ();
+
+ deviceExtension = WdfObjectGetTypedContext(WdfWorkItemGetParentObject(WorkItem), CDROM_DEVICE_EXTENSION);
+
+ if (DeviceIsMmcUpdateRequired(deviceExtension->Device))
+ {
+ // Issue command to update the drive capabilities.
+ // The failure of MMC update is not considered critical,
+ // so that we'll continue to process I/O even MMC update fails.
+ DeviceUpdateMmcCapabilities(deviceExtension->Device);
+ }
+
+ RequestProcessSerializedIoctl(deviceExtension, deviceExtension->IoctlWorkItemContext.OriginalRequest);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestProcessSerializedIoctl(
+ _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine Description:
+
+ a dispatch routine for all functions to process IOCTLs.
+
+Arguments:
+
+ DeviceExtension - device context
+
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ size_t information = 0;
+ WDF_REQUEST_PARAMETERS requestParameters;
+ BOOLEAN completeRequest = TRUE;
+
+ PAGED_CODE ();
+
+ // Get the Request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ NormalizeIoctl(&requestParameters);
+
+ // process IOCTLs
+ switch (requestParameters.Parameters.DeviceIoControl.IoControlCode)
+ {
+ case IOCTL_CDROM_READ_TOC:
+ case IOCTL_CDROM_GET_LAST_SESSION:
+ status = RequestHandleReadTOC(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_READ_TOC_EX:
+ status = RequestHandleReadTocEx(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_GET_CONFIGURATION:
+ status = RequestHandleGetConfiguration(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_RAW_READ:
+ status = DeviceHandleRawRead(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DISK_GET_LENGTH_INFO:
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
+ case IOCTL_STORAGE_READ_CAPACITY:
+ status = RequestHandleGetDriveGeometry(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DISK_VERIFY:
+ status = RequestHandleDiskVerify(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ // IOCTL_STORAGE_CHECK_VERIFY2 was processed including send a Test Unit Read
+ // with srb flag SRB_CLASS_FLAGS_LOW_PRIORITY to port driver asynchronizelly.
+ // The original request was completed after TUR finishes.
+ // As CDROM.SYS serializes IOs need accessing device, it's not a big difference from above behavior to
+ // just process it in serialized manner. So I put it here and treat it as same as IOCTL_STORAGE_CHECK_VERIFY.
+ case IOCTL_STORAGE_CHECK_VERIFY2:
+ status = RequestHandleCheckVerify(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DISK_GET_DRIVE_LAYOUT:
+ case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
+ case IOCTL_DISK_GET_PARTITION_INFO:
+ case IOCTL_DISK_GET_PARTITION_INFO_EX:
+ status = RequestHandleFakePartitionInfo(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DISK_IS_WRITABLE:
+ //
+ // Even though this media is writable, the requester of this IOCTL really
+ // wants to know if thw media behaves like any other disk or not. This is
+ // so if FeatureDefectManagement and FeatureRandomWritable are current on
+ // the drive-represented by the FeatureDefectManagement validation schema
+ //
+ if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed &&
+ (DeviceExtension->DeviceAdditionalData.Mmc.ValidationSchema == FeatureDefectManagement))
+ {
+ status = STATUS_SUCCESS;
+ }
+ else
+ {
+ status = STATUS_MEDIA_WRITE_PROTECTED;
+ }
+ information = 0;
+ break;
+
+ case IOCTL_CDROM_PLAY_AUDIO_MSF:
+ status = DeviceHandlePlayAudioMsf(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_READ_Q_CHANNEL:
+ status = DeviceHandleReadQChannel(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_PAUSE_AUDIO:
+ status = DeviceHandlePauseAudio(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_CDROM_RESUME_AUDIO:
+ status = DeviceHandleResumeAudio(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_CDROM_SEEK_AUDIO_MSF:
+ status = DeviceHandleSeekAudioMsf(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_STOP_AUDIO:
+ status = DeviceHandleStopAudio(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_CDROM_GET_VOLUME:
+ case IOCTL_CDROM_SET_VOLUME:
+ status = DeviceHandleGetSetVolume(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DVD_GET_REGION:
+ status = RequestHandleGetDvdRegion(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_DVD_READ_STRUCTURE:
+ status = DeviceHandleReadDvdStructure(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DVD_END_SESSION:
+ status = DeviceHandleDvdEndSession(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DVD_START_SESSION:
+ case IOCTL_DVD_READ_KEY:
+ status = DeviceHandleDvdStartSessionReadKey(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_DVD_SEND_KEY:
+ case IOCTL_DVD_SEND_KEY2:
+ status = DeviceHandleDvdSendKey(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_STORAGE_SET_READ_AHEAD:
+ status = DeviceHandleSetReadAhead(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_SET_SPEED:
+ status = DeviceHandleSetSpeed(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE:
+ case IOCTL_AACS_READ_MEDIA_KEY_BLOCK:
+ status = DeviceHandleAacsReadMediaKeyBlock(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_START_SESSION:
+ status = DeviceHandleAacsStartSession(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_END_SESSION:
+ status = DeviceHandleAacsEndSession(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_SEND_CERTIFICATE:
+ status = DeviceHandleAacsSendCertificate(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_GET_CERTIFICATE:
+ status = DeviceHandleAacsGetCertificate(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_GET_CHALLENGE_KEY:
+ status = DeviceHandleAacsGetChallengeKey(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_SEND_CHALLENGE_KEY:
+ status = DeviceHandleSendChallengeKey(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_READ_VOLUME_ID:
+ status = DeviceHandleReadVolumeId(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_READ_SERIAL_NUMBER:
+ status = DeviceHandleAacsReadSerialNumber(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_READ_MEDIA_ID:
+ status = DeviceHandleAacsReadMediaId(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_READ_BINDING_NONCE:
+ status = DeviceHandleAacsReadBindingNonce(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_AACS_GENERATE_BINDING_NONCE:
+ status = DeviceHandleAacsGenerateBindingNonce(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ case IOCTL_CDROM_ENABLE_STREAMING:
+ status = RequestHandleEnableStreaming(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_CDROM_SEND_OPC_INFORMATION:
+ status = RequestHandleSendOpcInformation(DeviceExtension, Request, &information);
+ break;
+
+ case IOCTL_CDROM_GET_PERFORMANCE:
+ status = RequestHandleGetPerformance(DeviceExtension, Request, requestParameters, &information);
+ break;
+
+ // This IOCTL is a fake one, used for MCN process sync-ed with serial queue.
+ case IOCTL_MCN_SYNC_FAKE_IOCTL:
+ status = RequestHandleMcnSyncFakeIoctl(DeviceExtension, &information);
+ break;
+
+ case IOCTL_STORAGE_MEDIA_REMOVAL:
+ case IOCTL_STORAGE_EJECTION_CONTROL:
+ {
+ status = RequestHandleEjectionControl(DeviceExtension, Request, requestParameters, &information);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_EJECT_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA2:
+ {
+ status = RequestHandleLoadEjectMedia(DeviceExtension, Request, requestParameters, &information);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_MCN_CONTROL:
+ {
+ status = RequestHandleMcnControl(DeviceExtension, Request, &information);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_RESERVE:
+ case IOCTL_STORAGE_RELEASE:
+ {
+ status = RequestHandleReserveRelease(DeviceExtension, Request, requestParameters, &information);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT:
+ {
+ status = RequestHandlePersistentReserve(DeviceExtension, Request, requestParameters, &information);
+
+ break;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ case IOCTL_DISK_ARE_VOLUMES_READY:
+ {
+ status = RequestHandleAreVolumesReady(DeviceExtension, Request, requestParameters, &information);
+
+ completeRequest = FALSE;
+
+ break;
+ }
+
+ case IOCTL_VOLUME_ONLINE:
+ case IOCTL_VOLUME_POST_ONLINE:
+ {
+ status = RequestHandleVolumeOnline(DeviceExtension, Request, requestParameters, &information);
+
+ break;
+ }
+#endif
+
+ default:
+ {
+ status = STATUS_ACCESS_DENIED;
+ break;
+ }
+ } // end of switch(ioctl)
+
+ if (completeRequest)
+ {
+ RequestCompletion(DeviceExtension, Request, status, information);
+ }
+
+ return status;
+}
+
+VOID
+SequentialQueueEvtCanceledOnQueue(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine Description:
+
+ Perform cancellation when request is still in queue.
+
+ If request is sych-ed in another thread, signal the event to let that thread be able to complete the request.
+ Otherwise, complete the request.
+
+Arguments:
+
+ Queue - serial queue
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+
+ if (requestContext->SyncRequired)
+ {
+ KeSetEvent(requestContext->SyncEvent, IO_CD_ROM_INCREMENT, FALSE);
+ }
+ else
+ {
+ PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+ WDFDEVICE device = WdfIoQueueGetDevice(Queue);
+
+ deviceExtension = DeviceGetExtension(device);
+
+ RequestCompletion(deviceExtension, Request, STATUS_CANCELLED, 0);
+
+ }
+
+ return;
+}
+
+
+NTSTATUS
+RequestSynchronizeProcessWithSerialQueue(
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine Description:
+
+ This is the mechanism to sync a request process in original thread with serialize queue.
+ initialize a EVENT and put the request inot serialize queue;
+ waiting for serialize queue processes to this request and signal the EVENT;
+ call the request handler to process this request.
+
+Arguments:
+
+ DeviceExtension - device context
+
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+ PKEVENT bufferToFree = requestContext->SyncEvent;
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ // cannot block at or above DISPATCH_LEVEL
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+ "RequestSynchronousProcessWithSerialQueue called at DISPATCH_LEVEL or above"));
+ NT_ASSERT(FALSE);
+ RequestCompletion(deviceExtension, Request, STATUS_INVALID_LEVEL, 0);
+ return STATUS_INVALID_LEVEL;
+ }
+
+ // init the synchronization event
+ KeInitializeEvent(requestContext->SyncEvent, NotificationEvent, FALSE);
+
+ // do we still need to do something like this?
+ // SET_FLAG(nextStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
+
+ // NOTE: this mechanism relies on that KMDF will not complete request by itself.
+ // Doing that will cause the syncEvent not fired thus this thread will stuck.
+ // This should not really happen: our EvtCanceledOnQueue callbacks should be
+ // called even if queues are purged for some reason. The only case when these
+ // callbacks are not called is when a request is owned by the driver (i.e. has
+ // already been passed to one of the registered handlers). In this case, it is
+ // our responsibility to cancel such requests properly.
+ status = WdfDeviceEnqueueRequest(Device, Request);
+
+ if (!NT_SUCCESS(status))
+ {
+ // Failed to forward request! Pretend the sync event already occured, otherwise we'll hit
+ // an assert in RequestEvtCleanup.
+ KeSetEvent(requestContext->SyncEvent, IO_CD_ROM_INCREMENT, FALSE);
+ RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+ }
+ else
+ {
+ NTSTATUS waitStatus = STATUS_UNSUCCESSFUL;
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+ BOOLEAN fCallSyncCallback = FALSE;
+ PIRP irp = WdfRequestWdmGetIrp(Request);
+
+ // ok, now wait on the event
+ while (waitStatus != STATUS_SUCCESS)
+ {
+ waitStatus = KeWaitForSingleObject(requestContext->SyncEvent, Executive, KernelMode, TRUE, NULL);
+ if (waitStatus == STATUS_SUCCESS) // must check equality -- STATUS_ALERTED is success code
+ {
+ // do nothing
+ }
+ else if (waitStatus != STATUS_ALERTED)
+ {
+ // do nothing
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL,
+ "Request %p on device object %p had a non-alert, non-success result from wait (%!HRESULT!)\n",
+ Request, Device, waitStatus));
+ NT_ASSERT(FALSE);
+ }
+ else if (PsIsThreadTerminating(PsGetCurrentThread()))
+ {
+ // the thread was alerted and is terminating, so cancel the irp
+ // this will cause EvtIoCanceledOnQueue to be called, which will signal the event,
+ // so we will get out of the while loop and eventually complete the request.
+ if (IoCancelIrp(irp))
+ {
+ // cancellation routine was called
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "Sychronize Ioctl: request %p cancelled from device %p\n",
+ Request, Device));
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "Sychronize Ioctl: request %p could not be cancelled from device %p\n",
+ Request, Device));
+ }
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "SPURIOUS ALERT waiting for Request %p on device %p (%!STATUS!)\n",
+ Request, Device, status));
+ }
+ } // end of wait loop on the event
+
+ // because we've waited an unknown amount of time, should check
+ // the cancelled flag to immediately fail the irp as appropriate
+ if (WdfRequestIsCanceled(Request))
+ {
+ // the request was cancelled, thus we should always stop
+ // processing here if possible.
+ status = STATUS_CANCELLED;
+ RequestCompletion(deviceExtension, Request, status, 0);
+ }
+ else if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
+ {
+ WDF_REQUEST_PARAMETERS requestParameters;
+ BOOLEAN isBlocked = FALSE;
+
+ // get the request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ status = RequestIsIoctlBlockedByExclusiveAccess(Request, &isBlocked);
+ if (isBlocked)
+ {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+ "Access Denied! Device in exclusive mode.Failing Ioctl %lx\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ RequestCompletion(deviceExtension, Request, STATUS_ACCESS_DENIED, 0);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "Ioctl %lx not blocked by cdrom being in exclusive mode\n",
+ requestParameters.Parameters.DeviceIoControl.IoControlCode));
+ fCallSyncCallback = TRUE;
+ }
+ }
+ else
+ {
+ fCallSyncCallback = TRUE;
+ }
+
+ if (fCallSyncCallback)
+ {
+ // Synchronization completed successfully. Call the requested routine
+ status = requestContext->SyncCallback(Device, Request);
+ }
+ }
+
+ // The next SequentialQueue evt routine will not be triggered until the current request is completed.
+
+ // clean up the request context setting.
+ FREE_POOL(bufferToFree);
+
+ return status;
+}
+
+NTSTATUS
+RequestIsIoctlBlockedByExclusiveAccess(
+ _In_ WDFREQUEST Request,
+ _Out_ PBOOLEAN IsBlocked
+ )
+/*++
+Routine Description:
+
+ Check if the IOCTL request should be blocked or not according to
+ the exclusive lock stat.
+
+Arguments:
+
+ Request - handle to the incoming WDF Request object
+
+Return Value:
+
+ NTSTATUS
+
+ IsBlocked - TRUE (be blocked); FALSE (not blocked)
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG ioctlCode = 0;
+ ULONG baseCode = 0;
+ WDF_REQUEST_PARAMETERS requestParameters;
+
+ // Get the Request parameters
+ WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+ WdfRequestGetParameters(Request, &requestParameters);
+
+ // check and initialize parameter
+ if (IsBlocked == NULL)
+ {
+ //This is an internal function and this parameter must be supplied.
+ NT_ASSERT(FALSE);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ *IsBlocked = FALSE;
+ }
+
+ // check if this is an IOCTL
+ if ((requestParameters.Type == WdfRequestTypeDeviceControl) ||
+ (requestParameters.Type == WdfRequestTypeDeviceControlInternal))
+ {
+ //
+ // Allow minimum set of commands that are required for the disk manager
+ // to show the CD device, while in exclusive mode.
+ // Note: These commands should not generate any requests to the device,
+ // and thus must be handled directly in StartIO during exclusive
+ // access (except for the exclusive owner, of course).
+ //
+ ioctlCode = requestParameters.Parameters.DeviceIoControl.IoControlCode;
+ baseCode = DEVICE_TYPE_FROM_CTL_CODE(ioctlCode);
+
+ if (ioctlCode == IOCTL_SCSI_GET_ADDRESS ||
+ ioctlCode == IOCTL_STORAGE_GET_HOTPLUG_INFO ||
+ ioctlCode == IOCTL_STORAGE_GET_DEVICE_NUMBER ||
+ ioctlCode == IOCTL_STORAGE_GET_MEDIA_TYPES_EX ||
+ ioctlCode == IOCTL_CDROM_EXCLUSIVE_ACCESS ||
+ ioctlCode == IOCTL_CDROM_GET_INQUIRY_DATA
+ )
+ {
+ *IsBlocked = FALSE;
+ }
+
+ //
+ // Handle IOCTL_STORAGE_QUERY_PROPERTY special because:
+ // (1) PropertyExistsQuery should not generate device i/o
+ // (2) Queries for StorageDeviceProperty and StorageAdapterDescriptor
+ // will return cache'd data
+ else if (ioctlCode == IOCTL_STORAGE_QUERY_PROPERTY)
+ {
+ PSTORAGE_PROPERTY_QUERY query = NULL;
+ status = WdfRequestRetrieveInputBuffer(Request,
+ requestParameters.Parameters.DeviceIoControl.InputBufferLength,
+ (PVOID*)&query,
+ NULL);
+
+ if (NT_SUCCESS(status))
+ {
+ if (query != NULL)
+ {
+ if (query->QueryType == PropertyExistsQuery)
+ {
+ *IsBlocked = FALSE;
+ }
+ else if ((query->QueryType == PropertyStandardQuery) &&
+ ((query->PropertyId == StorageDeviceProperty) ||
+ (query->PropertyId == StorageAdapterProperty)))
+ {
+ *IsBlocked = FALSE;
+ }
+ }
+ }
+ }
+
+ // Return TRUE for unknown IOCTLs with STORAGE bases
+ else if (baseCode == IOCTL_SCSI_BASE ||
+ baseCode == IOCTL_DISK_BASE ||
+ baseCode == IOCTL_CDROM_BASE ||
+ baseCode == IOCTL_STORAGE_BASE ||
+ baseCode == IOCTL_DVD_BASE )
+ {
+ *IsBlocked = TRUE;
+ }
+ }
+ else
+ {
+ // this should only be called with an IOCTL
+ NT_ASSERT(FALSE);
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ return status;
+}
+
+BOOLEAN
+DeviceIsMmcUpdateRequired(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ Check if the device needs to update its MMC information.
+
+Arguments:
+
+ Device - device to be checked.
+
+Return Value:
+
+ TRUE (require update); FALSE (not require update)
+
+--*/
+{
+ PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+ PCDROM_DATA cdData = &(deviceExtension->DeviceAdditionalData);
+
+ if ((cdData->Mmc.IsMmc) &&
+ (cdData->Mmc.UpdateState == CdromMmcUpdateRequired))
+ {
+ return TRUE;
+ }
+ else
+ {
+ // no update required: just proceed
+ return FALSE;
+ }
+}
+
+VOID
+RequestEvtCleanup(
+ _In_ WDFOBJECT Request
+ )
+/*++
+Routine Description:
+
+ Request cleanup callback.
+
+Arguments:
+
+ Request - request to clean up.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFREQUEST request = (WDFREQUEST)Request;
+ PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(request);
+
+ if (requestContext->SyncRequired)
+ {
+ // the event should have been signaled, just check that
+ NT_ASSERT(KeReadStateEvent(requestContext->SyncEvent) != 0);
+ }
+}
+