Sample Code

Windows Driver Samples/ CDROM Storage Class Driver/ C++/ src/ cdrom.c/

/*--

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 <
             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);
    }
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS