[CDROM_NEW] Import Microsoft CDROM class driver from GitHub
authorVictor Perevertkin <victor.perevertkin@reactos.org>
Fri, 16 Oct 2020 01:37:10 +0000 (04:37 +0300)
committerVictor Perevertkin <victor.perevertkin@reactos.org>
Fri, 16 Oct 2020 01:37:10 +0000 (04:37 +0300)
The source code is licensed under MS-PL license, taken from Windows Driver Samples
repository (microsoft/Windows-driver-samples@master/storage/class/cdrom/)
Synched with commit 96eb96dfb613e4c745db6bd1f53a92fe7e2290fc
The driver is written for Windows 10 and uses KMDF so we compile it with ntoskrnl_vista
and wdf01000 statically linked (for wdf01000 this will likely be changed in future)

CORE-17129

23 files changed:
drivers/storage/class/cdrom_new/CMakeLists.txt [new file with mode: 0644]
drivers/storage/class/cdrom_new/aacs.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/autorun.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/cdrom.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/cdrom.h [new file with mode: 0644]
drivers/storage/class/cdrom_new/cdrom.inf [new file with mode: 0644]
drivers/storage/class/cdrom_new/cdrom.rc [new file with mode: 0644]
drivers/storage/class/cdrom_new/cdromp.h [new file with mode: 0644]
drivers/storage/class/cdrom_new/common.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/data.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/guid.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/init.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/ioctl.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/ioctl.h [new file with mode: 0644]
drivers/storage/class/cdrom_new/license.txt [new file with mode: 0644]
drivers/storage/class/cdrom_new/localwpp.ini [new file with mode: 0644]
drivers/storage/class/cdrom_new/mmc.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/mmc.h [new file with mode: 0644]
drivers/storage/class/cdrom_new/pnppower.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/scratch.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/scratch.h [new file with mode: 0644]
drivers/storage/class/cdrom_new/sense.c [new file with mode: 0644]
drivers/storage/class/cdrom_new/zpodd.c [new file with mode: 0644]

diff --git a/drivers/storage/class/cdrom_new/CMakeLists.txt b/drivers/storage/class/cdrom_new/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7def3bc
--- /dev/null
@@ -0,0 +1,41 @@
+
+remove_definitions(-D_WIN32_WINNT=0x502)
+
+list(APPEND SOURCE
+    aacs.c
+    autorun.c
+    cdrom.c
+    common.c
+    data.c
+    guid.c
+    init.c
+    ioctl.c
+    mmc.c
+    pnppower.c
+    scratch.c
+    sense.c
+    zpodd.c
+    cdrom.h)
+
+add_library(cdrom MODULE ${SOURCE})
+set_module_type(cdrom kernelmodedriver)
+
+if(GCC OR CLANG)
+    target_compile_options(cdrom PRIVATE -Wno-format -Wno-unused-variable -Wno-pointer-sign)
+endif()
+
+if(GCC)
+    target_compile_options(cdrom PRIVATE -Wno-unknown-pragmas -Wno-incompatible-pointer-types -Wno-switch)
+endif()
+
+if(CLANG)
+    target_compile_options(cdrom PRIVATE -Wno-enum-conversion -Wno-tautological-constant-compare)
+endif()
+
+target_compile_definitions(cdrom PRIVATE DEBUG_USE_KDPRINT)
+
+target_link_libraries(cdrom wdf01000 ntoskrnl_vista libcntpr ${PSEH_LIB})
+add_importlibs(cdrom ntoskrnl hal)
+# add_pch(cdrom cdrom.h SOURCE)
+add_cd_file(TARGET cdrom DESTINATION reactos/system32/drivers NO_CAB FOR all)
+add_driver_inf(cdrom cdrom.inf)
diff --git a/drivers/storage/class/cdrom_new/aacs.c b/drivers/storage/class/cdrom_new/aacs.c
new file mode 100644 (file)
index 0000000..f81eacb
--- /dev/null
@@ -0,0 +1,1481 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    aacs.c
+
+Abstract:
+
+    The CDROM class driver implementation of handling AACS IOCTLs.
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "ioctl.h"
+#include "scratch.h"
+
+#ifdef DEBUG_USE_WPP
+#include "aacs.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceHandleAacsReadMediaKeyBlock)
+#pragma alloc_text(PAGE, DeviceHandleAacsStartSession)
+#pragma alloc_text(PAGE, DeviceHandleAacsEndSession)
+#pragma alloc_text(PAGE, DeviceHandleAacsSendCertificate)
+#pragma alloc_text(PAGE, DeviceHandleAacsGetCertificate)
+#pragma alloc_text(PAGE, DeviceHandleAacsGetChallengeKey)
+#pragma alloc_text(PAGE, DeviceHandleAacsReadSerialNumber)
+#pragma alloc_text(PAGE, DeviceHandleAacsReadMediaId)
+#pragma alloc_text(PAGE, DeviceHandleAacsReadBindingNonce)
+#pragma alloc_text(PAGE, DeviceHandleAacsGenerateBindingNonce)
+#pragma alloc_text(PAGE, DeviceHandleReadVolumeId)
+#pragma alloc_text(PAGE, DeviceHandleSendChallengeKey)
+
+#endif
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadMediaKeyBlock(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTLs:
+        IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE
+        IOCTL_AACS_READ_MEDIA_KEY_BLOCK
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PAACS_LAYER_NUMBER  layerNumber = NULL;
+    PVOID               outputBuffer = NULL;
+    ULONG               transferSize = sizeof(READ_DVD_STRUCTURES_HEADER);
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&layerNumber,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
+        {
+            // maximum size for this transfer is one pack + header
+            transferSize += AACS_MKB_PACK_SIZE;
+        }
+
+        if (transferSize > DeviceExtension->ScratchContext.ScratchBufferSize)
+        {
+            // rare case. normally the size of scratch buffer is 64k.
+            status = STATUS_INTERNAL_ERROR;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        UCHAR                       rmdBlockNumber = 0;
+        BOOLEAN                     sendChangedCommand = TRUE;
+        BOOLEAN                     shouldRetry = TRUE;
+        CDB                         cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+        // cdb->AsByte[1] = 0x01; // AACS sub-command not required for this
+
+        cdb.READ_DVD_STRUCTURE.LayerNumber = (UCHAR)(*layerNumber);
+        cdb.READ_DVD_STRUCTURE.Format = 0x83; // MKB
+        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(transferSize >> (8*1));
+        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(transferSize >> (8*0));
+
+        while (sendChangedCommand)
+        {
+            // RMDBlockNumber is set to zero....
+            // RMDBlockNumber[3] maybe changed for other blocks.
+            cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = rmdBlockNumber;
+
+            if (shouldRetry)
+            {
+                status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 12);
+            }
+
+    #ifdef ENABLE_AACS_TESTING
+            if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE)
+            {
+                static const UCHAR results[] = { 0x80, 0x02, 0x00, 0x02 };
+                RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+                status = STATUS_SUCCESS;
+            }
+            else if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
+            {
+                static const UCHAR results[] = { 0x80, 0x02, 0x00, 0x02 };
+                static const UCHAR defaultFill = 0x30; // '0'
+                RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x8004, defaultFill);
+                RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+                status = STATUS_SUCCESS;
+            }
+    #endif //ENABLE_AACS_TESTING
+
+            if (NT_SUCCESS(status))
+            {
+                // command succeeded, process data...
+                PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+                UCHAR                   thisPackNumber = cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3];
+                UCHAR                   otherPacks = header->Reserved[1];
+
+                // validate and zero-base the otherPacks
+                if (otherPacks == 0)
+                {
+                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                                "AACS: Device is reporting zero total packs (invalid)\n"));
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+                else
+                {
+                    otherPacks--;
+
+                    if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE)
+                    {
+                        // if not already requested last pack, do so now
+                        if (otherPacks != thisPackNumber)
+                        {
+                            // re-send the command for the other pack number.
+                            // this is safe here because NT_SUCCESS() is TRUE,
+                            // and all the rest of this routine does is SetHardError()
+                            // and release of resources we're still about to use.
+
+                            // re-zero the output buffer
+                            RtlZeroMemory(DeviceExtension->ScratchContext.ScratchBuffer, sizeof(READ_DVD_STRUCTURES_HEADER));
+
+                            // modify the CDB to get the very last pack of the MKB
+                            rmdBlockNumber = otherPacks;
+
+                            transferSize = sizeof(READ_DVD_STRUCTURES_HEADER);
+
+                            // keep items clean
+                            ScratchBuffer_ResetItems(DeviceExtension, TRUE);
+
+                            // make sure the loop will be executed for modified command.
+                            sendChangedCommand = TRUE;
+                            shouldRetry = TRUE;
+                        }
+                        else
+                        {
+                            // this request already got the last pack
+                            // so just interpret the data
+                            REVERSE_SHORT(&header->Length);
+                            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+                            {
+                                *DataLength = 0;
+                                status = STATUS_IO_DEVICE_ERROR;
+                            }
+                            else
+                            {
+                                ULONG totalSize = header->Length;
+                                // subtract out any remaining bytes in the header
+                                // to get the number of usable bytes in this pack
+                                totalSize -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+                                totalSize += otherPacks * AACS_MKB_PACK_SIZE;
+
+                                // save the result and complete the request
+                                *((PULONG)outputBuffer) = totalSize;
+                                *DataLength = sizeof(ULONG);
+                                status = STATUS_SUCCESS;
+                            }
+                            // This will exit the loop of sendChangedCommand
+                            sendChangedCommand = FALSE;
+                            shouldRetry = FALSE;
+                        }
+                    }
+                    else if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
+                    {
+                        // make length field native byte ordering
+                        REVERSE_SHORT(&header->Length);
+
+                        // exit if getting invalid data from the drive
+                        if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+                        {
+                            *DataLength = 0;
+                            status = STATUS_IO_DEVICE_ERROR;
+                        }
+                        else
+                        {
+                            // success, how many bytes to copy for this pack?
+                            ULONG totalSize = header->Length;
+                            size_t originalBufferSize;
+
+                            // subtract out any remaining bytes in the header
+                            // to get the number of usable bytes in this pack
+                            totalSize -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                            // if not the final pack, this should be a full transfer per spec
+                            NT_ASSERT( (totalSize == AACS_MKB_PACK_SIZE) || (thisPackNumber == otherPacks) );
+
+                            // validate the user's buffer is large enough to accept the full data
+                            originalBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+
+                            if (originalBufferSize < (totalSize + (AACS_MKB_PACK_SIZE*thisPackNumber)))
+                            {
+                                // just return a slightly bigger-than-normal size
+                                *DataLength = (otherPacks + 1)*AACS_MKB_PACK_SIZE;
+                                status = STATUS_BUFFER_TOO_SMALL;
+                            }
+                            else
+                            {
+                                PUCHAR whereToCopy;
+                                // determine where to copy to the user's memory
+                                whereToCopy = outputBuffer;
+                                whereToCopy += AACS_MKB_PACK_SIZE * thisPackNumber;
+
+                                RtlCopyMemory(whereToCopy, header->Data, totalSize);
+
+                                // update the Information field here because we already
+                                // have calculated the size of the block
+                                *DataLength = totalSize + (AACS_MKB_PACK_SIZE * thisPackNumber);
+                                status = STATUS_SUCCESS;
+
+                                // if there are more packs to get from the device, send it again....
+                                if (thisPackNumber != otherPacks)
+                                {
+                                    // re-send the command for the next pack number.
+                                    // this is safe here because NT_SUCCESS() is TRUE,
+                                    // and all the rest of this routine does is SetHardError()
+                                    // and release of resources we're still about to use.
+
+                                    // re-zero the output buffer
+                                    RtlZeroMemory(DeviceExtension->ScratchContext.ScratchBuffer, sizeof(READ_DVD_STRUCTURES_HEADER));
+
+                                    // modify the CDB to get the next pack of the MKB
+                                    rmdBlockNumber = cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3]++;
+
+                                    // modify the SRB to be resent
+                                    //
+                                    transferSize = AACS_MKB_PACK_SIZE + sizeof(READ_DVD_STRUCTURES_HEADER);
+
+                                    // keep items clean
+                                    ScratchBuffer_ResetItems(DeviceExtension, FALSE);
+
+                                    // make sure the loop will be executed for modified command.
+                                    sendChangedCommand = TRUE;
+                                    shouldRetry = TRUE;
+                                }
+                                else
+                                {
+                                    // else, that was the end of the transfer, so just complete the request
+                                    sendChangedCommand = FALSE;
+                                }
+                            }
+                        }
+
+                    } // end of IOCTL_AACS_READ_MEDIA_KEY_BLOCK
+                }
+            } // end of NT_SUCCESS(status)
+
+            if (!NT_SUCCESS(status))
+            {
+                // command failed.
+                sendChangedCommand = FALSE;
+            }
+        } //end of while (sendChangedCommand)
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_START_SESSION
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsGetAgid
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID sessionId = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveOutputBuffer(Request,
+                                            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                            (PVOID*)&sessionId,
+                                            NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.REPORT_KEY.KeyFormat = 0x00; // DVD_REPORT_AGID?
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+        static const UCHAR results[] = { 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 };
+        RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+        status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchBuffer;
+            PCDVD_REPORT_AGID_DATA  keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data;
+
+            *sessionId = (DVD_SESSION_ID)(keyData->AGID);
+            *DataLength = sizeof(DVD_SESSION_ID);
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_END_SESSION
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsReleaseAgid
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID sessionId = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&sessionId,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG                       transferSize = 0;
+        CDB                         cdb;
+        DVD_SESSION_ID              currentSession = 0;
+        DVD_SESSION_ID              limitSession = 0;
+
+        if(*sessionId == DVD_END_ALL_SESSIONS)
+        {
+            currentSession = 0;
+            limitSession = MAX_COPY_PROTECT_AGID - 1;
+        }
+        else
+        {
+            currentSession = *sessionId;
+            limitSession = *sessionId;
+        }
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        do
+        {
+            RtlZeroMemory(&cdb, sizeof(CDB));
+            // Set up the CDB
+            cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
+            cdb.AsByte[7] = 0x02; // AACS key class
+            cdb.SEND_KEY.AGID = (UCHAR)(currentSession);
+            cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
+
+            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
+
+            currentSession++;
+        } while ((currentSession <= limitSession) && NT_SUCCESS(status));
+
+#ifdef ENABLE_AACS_TESTING
+        status = STATUS_SUCCESS;
+#endif
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsSendCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_SEND_CERTIFICATE
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsSendHostCertificate
+
+    NTSTATUS                status = STATUS_SUCCESS;
+    PAACS_SEND_CERTIFICATE  input = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CERTIFICATE);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        // copy the input buffer to the data buffer for the transfer
+        {
+            PCDVD_KEY_HEADER header = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG            tmp = dataTransferLength;
+
+            tmp -= RTL_SIZEOF_THROUGH_FIELD(CDVD_KEY_HEADER, DataLength);
+
+            header->DataLength[0] = (UCHAR)(tmp >> (8*1));
+            header->DataLength[1] = (UCHAR)(tmp >> (8*0));
+            RtlCopyMemory(header->Data, &(input->Certificate), sizeof(AACS_CERTIFICATE));
+        }
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.SEND_KEY.ParameterListLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.SEND_KEY.ParameterListLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.SEND_KEY.AGID = (UCHAR)( input->SessionId );
+        cdb.SEND_KEY.KeyFormat = 0x01; // Send Host Challenge Certificate
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, FALSE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+        status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = 0;
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGetCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_GET_CERTIFICATE
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsGetDriveCertificate
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID input = NULL;
+    PVOID           outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CERTIFICATE);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.REPORT_KEY.AGID = (UCHAR)(*input);
+        cdb.REPORT_KEY.KeyFormat = 0x01; // Return a drive certificate challenge
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x72, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x31; // '1'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0074, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_CERTIFICATE);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < (sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length)))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGetChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_GET_CHALLENGE_KEY
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsGetChallengeKey
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID input = NULL;
+    PVOID           outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CHALLENGE_KEY);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.REPORT_KEY.AGID = (UCHAR)(*input);
+        cdb.REPORT_KEY.KeyFormat = 0x02; // Return a drive certificate challenge
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x52, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x32; // '2'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0054, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_CHALLENGE_KEY);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSendChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_SEND_CHALLENGE_KEY
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsSendChallengeKey
+
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PAACS_SEND_CHALLENGE_KEY input = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CHALLENGE_KEY);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        // copy the input buffer to the data buffer for the transfer
+        {
+            PCDVD_KEY_HEADER header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG tmp = dataTransferLength;
+            tmp -= RTL_SIZEOF_THROUGH_FIELD(CDVD_KEY_HEADER, DataLength);
+
+            header->DataLength[0] = (UCHAR)(tmp >> (8*1));
+            header->DataLength[1] = (UCHAR)(tmp >> (8*0));
+            RtlCopyMemory(header->Data, &(input->ChallengeKey), sizeof(AACS_CHALLENGE_KEY));
+        }
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.SEND_KEY.ParameterListLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.SEND_KEY.ParameterListLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.SEND_KEY.AGID = (UCHAR)( input->SessionId );
+        cdb.SEND_KEY.KeyFormat = 0x02; // Send Host Challenge Certificate
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, FALSE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = 0;
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadVolumeId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_READ_VOLUME_ID
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsReadVolumeID
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID input = NULL;
+    PVOID           outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_VOLUME_ID);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+        cdb.READ_DVD_STRUCTURE.Format = 0x80; // Return the AACS volumeID
+        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x33; // '3'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_VOLUME_ID);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadSerialNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_READ_SERIAL_NUMBER
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsReadSerialNumber
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID input = NULL;
+    PVOID           outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_SERIAL_NUMBER);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+        cdb.READ_DVD_STRUCTURE.Format = 0x81; // Return the AACS volumeID
+        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x34; // '4'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_SERIAL_NUMBER);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadMediaId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_READ_MEDIA_ID
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsReadMediaID
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID input = NULL;
+    PVOID           outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_MEDIA_ID);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+        cdb.READ_DVD_STRUCTURE.Format = 0x82; // Return the AACS volumeID
+        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x35; // '5'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_MEDIA_ID);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_READ_BINDING_NONCE
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsReadBindingNonce
+
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PAACS_READ_BINDING_NONCE input = NULL;
+    PVOID                    outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_BINDING_NONCE);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+        cdb.REPORT_KEY.LogicalBlockAddress[0] = (UCHAR)( input->StartLba >> (3*8) );
+        cdb.REPORT_KEY.LogicalBlockAddress[1] = (UCHAR)( input->StartLba >> (2*8) );
+        cdb.REPORT_KEY.LogicalBlockAddress[2] = (UCHAR)( input->StartLba >> (1*8) );
+        cdb.REPORT_KEY.LogicalBlockAddress[3] = (UCHAR)( input->StartLba >> (0*8) );
+        cdb.AsByte[6] = (UCHAR)( input->NumberOfSectors );
+        cdb.AsByte[7] = 0x02; // AACS key class
+        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
+        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
+        cdb.REPORT_KEY.AGID = (UCHAR)( input->SessionId );
+        cdb.REPORT_KEY.KeyFormat = 0x21; // Return an existing binding nonce
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x36; // '6'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_BINDING_NONCE);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGenerateBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+    This routine is used to process IOCTL:
+        IOCTL_AACS_GENERATE_BINDING_NONCE
+Arguments:
+    DeviceExtension - device context
+
+    Request - the request that will be formatted
+
+    RequestParameters - request parameter structur
+
+    DataLength - data transferred length
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //AacsGenerateBindingNonce
+
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PAACS_READ_BINDING_NONCE input = NULL;
+    PVOID                    outputBuffer = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&input,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                (PVOID*)&outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_BINDING_NONCE);
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);
+
+#ifdef ENABLE_AACS_TESTING
+            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
+            static const UCHAR defaultFill = 0x37; // '7'
+            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
+            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
+            status = STATUS_SUCCESS;
+#endif
+        if (NT_SUCCESS(status))
+        {
+            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
+            ULONG                   dataLengthToCopy = sizeof(AACS_BINDING_NONCE);
+
+            // make length field native byte ordering
+            REVERSE_SHORT(&header->Length);
+
+            // exit if getting invalid data from the drive
+            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
+            {
+                *DataLength = 0;
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // adjust data length to reflect only the addition data
+                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
+
+                // exit if the drive is returning an unexpected data size
+                if (header->Length != dataLengthToCopy)
+                {
+                    *DataLength = 0;
+                    status = STATUS_IO_DEVICE_ERROR;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // else copy the data to the user's buffer
+                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
+                *DataLength = dataLengthToCopy;
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
diff --git a/drivers/storage/class/cdrom_new/autorun.c b/drivers/storage/class/cdrom_new/autorun.c
new file mode 100644 (file)
index 0000000..e5c9b11
--- /dev/null
@@ -0,0 +1,3080 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    autorun.c
+
+Abstract:
+
+    Code for support of media change detection in the cd/dvd driver
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "mmc.h"
+#include "ioctl.h"
+
+#include "ntstrsafe.h"
+
+#ifdef DEBUG_USE_WPP
+#include "autorun.tmh"
+#endif
+
+#define GESN_TIMEOUT_VALUE (0x4)
+#define GESN_BUFFER_SIZE (0x8)
+#define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS   (2)
+
+#define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
+#define MCN_REG_SUBKEY_NAME                   (L"MediaChangeNotification")
+#define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
+#define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME  (L"AlwaysEnableMCN")
+
+//
+// Only send polling irp when device is fully powered up and a
+// power down irp is not in progress.
+//
+// NOTE:   This helps close a window in time where a polling irp could cause
+//         a drive to spin up right after it has powered down. The problem is
+//         that SCSIPORT, ATAPI and SBP2 will be in the process of powering
+//         down (which may take a few seconds), but won't know that. It would
+//         then get a polling irp which will be put into its queue since it
+//         the disk isn't powered down yet. Once the disk is powered down it
+//         will find the polling irp in the queue and then power up the
+//         device to do the poll. They do not want to check if the polling
+//         irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
+//         path and would slow down all I/Os. A better way to fix this
+//         would be to serialize the polling and power down irps so that
+//         only one of them is sent to the device at a time.
+//
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceIsMediaChangeDisabledDueToHardwareLimitation(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceIsMediaChangeDisabledForClass(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceMediaChangeDeviceInstanceOverride(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _Out_ PBOOLEAN                Enabled
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeMcn(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ BOOLEAN                  AllowDriveToSleep
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeGesn(
+    _In_ PCDROM_DEVICE_EXTENSION      DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+GesnDataInterpret(
+    _In_  PCDROM_DEVICE_EXTENSION             DeviceExtension,
+    _In_  PNOTIFICATION_EVENT_STATUS_HEADER   Header,
+    _Out_ PBOOLEAN                            ResendImmediately
+    );
+
+RTL_QUERY_REGISTRY_ROUTINE DeviceMediaChangeRegistryCallBack;
+
+EVT_WDF_WORKITEM DeviceDisableGesn;
+
+#if ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceInitializeMediaChangeDetection)
+#pragma alloc_text(PAGE, DeviceEnableMediaChangeDetection)
+#pragma alloc_text(PAGE, DeviceDisableMediaChangeDetection)
+#pragma alloc_text(PAGE, DeviceSendDelayedMediaChangeNotifications)
+#pragma alloc_text(PAGE, DeviceReleaseMcnResources)
+#pragma alloc_text(PAGE, DeviceMediaChangeRegistryCallBack)
+#pragma alloc_text(PAGE, DeviceInitializeMcn)
+#pragma alloc_text(PAGE, DeviceDisableGesn)
+
+#pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledDueToHardwareLimitation)
+#pragma alloc_text(PAGE, DeviceMediaChangeDeviceInstanceOverride)
+#pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledForClass)
+
+#pragma alloc_text(PAGE, DeviceDisableMainTimer)
+
+#pragma alloc_text(PAGE, GesnDataInterpret)
+
+#pragma alloc_text(PAGE, RequestSetupMcnRequest)
+#pragma alloc_text(PAGE, RequestPostWorkMcnRequest)
+#pragma alloc_text(PAGE, RequestSendMcnRequest)
+
+//
+// DeviceEnableMainTimer is called by EvtDeviceD0Entry which can't be made pageable
+// so neither is DeviceEnableMainTimer
+//
+//#pragma alloc_text(PAGE, DeviceEnableMainTimer)
+
+#pragma alloc_text(PAGE, DeviceInitializeGesn)
+
+#pragma alloc_text(PAGE, RequestHandleMcnControl)
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+GesnDataInterpret(
+    _In_  PCDROM_DEVICE_EXTENSION             DeviceExtension,
+    _In_  PNOTIFICATION_EVENT_STATUS_HEADER   Header,
+    _Out_ PBOOLEAN                            ResendImmediately
+    )
+/*++
+
+Routine Description:
+
+    This routine will interpret the data returned for a GESN command, and
+    (if appropriate) set the media change event, and broadcast the
+    appropriate events to user mode for applications who care.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+    Header - the resulting data from a GESN event.
+        requires at least EIGHT valid bytes (header == 4, data == 4)
+
+    ResendImmediately - whether or not to immediately resend the request.
+        this should be FALSE if there was no event, FALSE if the reported
+        event was of the DEVICE BUSY class, else true.
+
+Return Value:
+
+    STATUS_SUCCESS if successful, an error code otherwise
+
+Notes:
+
+    DataBuffer must be at least four bytes of valid data (header == 4 bytes),
+    and have at least eight bytes of allocated memory (all events == 4 bytes).
+
+    The call to StartNextPacket may occur before this routine is completed.
+    the operational change notifications are informational in nature, and
+    while useful, are not neccessary to ensure proper operation.  For example,
+    if the device morphs to no longer supporting WRITE commands, all further
+    write commands will fail.  There exists a small timing window wherein
+    IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response.  If
+    a device supports software write protect, it is expected that the
+    application can handle such a case.
+
+    NOTE: perhaps setting the updaterequired byte to one should be done here.
+    if so, it relies upon the setting of a 32-byte value to be an atomic
+    operation.  unfortunately, there is no simple way to notify a class driver
+    which wants to know that the device behavior requires updating.
+
+    Not ready events may be sent every second.  For example, if we were
+    to minimize the number of asynchronous notifications, an application may
+    register just after a large busy time was reported.  This would then
+    prevent the application from knowing the device was busy until some
+    arbitrarily chosen timeout has occurred.  Also, the GESN request would
+    have to still occur, since it checks for non-busy events (such as user
+    keybutton presses and media change events) as well.  The specification
+    states that the lower-numered events get reported first, so busy events,
+    while repeating, will only be reported when all other events have been
+    cleared from the device.
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
+    LONG                            dataLength = 0;
+    LONG                            requiredLength = 0;
+    BOOLEAN                         inHomePosition = FALSE;
+
+    PAGED_CODE();
+
+    // note: don't allocate anything in this routine so that we can
+    //       always just 'return'.
+    *ResendImmediately = FALSE;
+
+    if (Header->NEA)
+    {
+        return status;
+    }
+    if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS)
+    {
+        return status;
+    }
+
+    // HACKHACK - REF #0001
+    // This loop is only taken initially, due to the inability to reliably
+    // auto-detect drives that report events correctly at boot.  When we
+    // detect this behavior during the normal course of running, we will
+    // disable the hack, allowing more efficient use of the system.  This
+    // should occur "nearly" instantly, as the drive should have multiple
+    // events queue'd (ie. power, morphing, media).
+    if (info->Gesn.HackEventMask)
+    {
+        // all events use the low four bytes of zero to indicate
+        // that there was no change in status.
+        UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
+        UCHAR lowestSetBit;
+        UCHAR thisEventBit = (1 << Header->NotificationClass);
+
+        if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit))
+        {
+            // The drive is reporting an event that wasn't requested
+            return STATUS_DEVICE_PROTOCOL_ERROR;
+        }
+
+        // some bit magic here... this results in the lowest set bit only
+        lowestSetBit = info->Gesn.EventMask;
+        lowestSetBit &= (info->Gesn.EventMask - 1);
+        lowestSetBit ^= (info->Gesn.EventMask);
+
+        if (thisEventBit != lowestSetBit)
+        {
+            // HACKHACK - REF #0001
+            // the first time we ever see an event set that is not the lowest
+            // set bit in the request (iow, highest priority), we know that the
+            // hack is no longer required, as the device is ignoring "no change"
+            // events when a real event is waiting in the other requested queues.
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN::NONE: Compliant drive found, "
+                       "removing GESN hack (%x, %x)\n",
+                       thisEventBit, info->Gesn.EventMask));
+
+            info->Gesn.HackEventMask = FALSE;
+        }
+        else if (thisEvent == 0)  // NOTIFICATION_*_EVENT_NO_CHANGE
+        {
+            // HACKHACK - REF #0001
+            // note: this hack prevents poorly implemented firmware from constantly
+            //       returning "No Event".  we do this by cycling through the
+            //       supported list of events here.
+            SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
+            CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);
+
+            // if we have cycled through all supported event types, then
+            // we need to reset the events we are asking about. else we
+            // want to resend this request immediately in case there was
+            // another event pending.
+            if (info->Gesn.EventMask == 0)
+            {
+                info->Gesn.EventMask         = info->Gesn.NoChangeEventMask;
+                info->Gesn.NoChangeEventMask = 0;
+            }
+            else
+            {
+                *ResendImmediately = TRUE;
+            }
+            return status;
+        }
+
+    } // end if (info->Gesn.HackEventMask)
+
+    dataLength = (Header->EventDataLength[0] << 8) |
+                 (Header->EventDataLength[1] & 0xff);
+    dataLength -= 2;
+    requiredLength = 4; // all events are four bytes
+
+    if (dataLength < requiredLength)
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "error - GESN returned only %x bytes data for fdo %p\n",
+                   dataLength, DeviceExtension->DeviceObject));
+
+        return STATUS_DEVICE_PROTOCOL_ERROR;
+    }
+
+    if (dataLength > requiredLength)
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "error - GESN returned too many (%x) bytes data for fdo %p\n",
+                   dataLength, DeviceExtension->DeviceObject));
+    }
+
+    if ((Header->ClassEventData[0] & 0xf) == 0)
+    {
+        // a zero event is a "no change event, so do not retry
+        return status;
+    }
+
+    // because a event other than "no change" occurred,
+    // we should immediately resend this request.
+    *ResendImmediately = TRUE;
+
+    switch (Header->NotificationClass)
+    {
+
+    case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS:  // 0x01
+    {
+        PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo =
+                                            (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData);
+        ULONG event;
+
+        if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED)
+        {
+            break;
+        }
+
+        event = (opChangeInfo->Operation[0] << 8) |
+                (opChangeInfo->Operation[1]     ) ;
+
+        // Workaround some hardware that is buggy but prevalent in the market
+        // This hardware has the property that it will report OpChange events repeatedly,
+        // causing us to retry immediately so quickly that we will eventually disable
+        // GESN to prevent an infinite loop.
+        // (only one valid OpChange event type now, only two ever defined)
+        if (info->MediaChangeRetryCount >= 4)
+        {
+            //
+            // HACKHACK - REF #0002
+            // Some drives incorrectly report OpChange/Change (001b/0001h) events
+            // continuously when the tray has been ejected.  This causes this routine
+            // to set ResendImmediately to "TRUE", and that results in our cycling
+            // 32 times immediately resending.  At that point, we give up detecting
+            // the infinite retry loop, and disable GESN on these drives.  This
+            // prevents Media Eject Request (from eject button) from being reported.
+            // Thus, instead we should attempt to workaround this issue by detecting
+            // this behavior.
+            //
+
+            static UCHAR const OpChangeMask = 0x02;
+
+            // At least one device reports "temporarily busy" (which is useless) on eject
+            // At least one device reports "OpChange" repeatedly when re-inserting media
+            // All seem to work well using this workaround
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN,
+                        "GESN OpChange events are broken.  Working around this problem in software (for WDFDEVICE %p)\n",
+                        DeviceExtension->Device));
+
+            // OpChange is not the only bit set -- Media class is required....
+            NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1);
+
+            // Force the use of the hackhack (ref #0001) to workaround the
+            // issue noted this hackhack (ref #0002).
+            SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask);
+            CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask);
+            info->Gesn.HackEventMask = TRUE;
+
+            // don't request the opChange event again.  use the method
+            // defined by hackhack (ref #0001) as the workaround.
+            if (info->Gesn.EventMask == 0)
+            {
+                info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
+                info->Gesn.NoChangeEventMask = 0;
+                *ResendImmediately = FALSE;
+            }
+            else
+            {
+                *ResendImmediately = TRUE;
+            }
+
+            break;
+        }
+
+
+        if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) |
+            (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN says features added/changed for WDFDEVICE %p\n",
+                       DeviceExtension->Device));
+
+            // don't notify that new media arrived, just set the
+            // DO_VERIFY to force a FS reload.
+
+            if (IsVolumeMounted(DeviceExtension->DeviceObject))
+            {
+                SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+            }
+
+            // Call error handler with
+            // a "fake" media change error in case it needs to update
+            // internal structures as though a media change occurred.
+            {
+                SCSI_REQUEST_BLOCK  srb = {0};
+                SENSE_DATA          sense = {0};
+                NTSTATUS            tempStatus;
+                BOOLEAN             retry;
+
+                tempStatus = STATUS_MEDIA_CHANGED;
+                retry = FALSE;
+
+                srb.CdbLength = 6;
+                srb.Length    = sizeof(SCSI_REQUEST_BLOCK);
+                srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
+                srb.SenseInfoBuffer = &sense;
+                srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+
+                sense.AdditionalSenseLength = sizeof(SENSE_DATA) -
+                                            RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
+
+                sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION;
+                sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
+
+                if (DeviceExtension->DeviceAdditionalData.ErrorHandler)
+                {
+                    DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension,
+                                                                       &srb,
+                                                                       &tempStatus,
+                                                                       &retry);
+                }
+            } // end error handler
+
+        }
+        break;
+    }
+
+    case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS:  // 0x3
+    {
+        PNOTIFICATION_EXTERNAL_STATUS externalInfo =
+                                        (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
+        DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0};
+
+        // unfortunately, due to time constraints, we will only notify
+        // about keys being pressed, and not released.  this makes keys
+        // single-function, but simplifies the code significantly.
+        if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN)
+        {
+            break;
+        }
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "GESN::EXTERNAL: Event: %x Status %x Req %x\n",
+                   externalInfo->ExternalEvent, externalInfo->ExternalStatus,
+                   (externalInfo->Request[0] << 8) | externalInfo->Request[1]
+                   ));
+
+        externalData.Version = 1;
+        externalData.DeviceClass = 0;
+        externalData.ButtonStatus = externalInfo->ExternalEvent;
+        externalData.Request = (externalInfo->Request[0] << 8) |
+                               (externalInfo->Request[1] & 0xff);
+        KeQuerySystemTime(&(externalData.SystemTime));
+        externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();
+
+        DeviceSendNotification(DeviceExtension,
+                               &GUID_IO_DEVICE_EXTERNAL_REQUEST,
+                               sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
+                               &externalData);
+
+        return status;
+    }
+
+    case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS:  // 0x4
+    {
+        PNOTIFICATION_MEDIA_STATUS mediaInfo =
+                                    (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);
+
+        if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
+            (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN::MEDIA ARRIVAL, Status %x\n",
+                       mediaInfo->MediaStatus));
+
+            if (IsVolumeMounted(DeviceExtension->DeviceObject))
+            {
+                SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+            }
+            DeviceSetMediaChangeStateEx(DeviceExtension,
+                                        MediaPresent,
+                                        NULL);
+
+            // If media is inserted into slot loading type, mark the device active
+            // to not power off.
+            if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
+                (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) &&
+                (DeviceExtension->ZeroPowerODDInfo->Load == 0))                                     // Slot
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                           "GesnDataInterpret: MediaArrival event detected, device marked as active\n"));
+
+                DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+            }
+        }
+        else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN::MEDIA REMOVAL, Status %x\n",
+                       mediaInfo->MediaStatus));
+
+            DeviceSetMediaChangeStateEx(DeviceExtension,
+                                        MediaNotPresent,
+                                        NULL);
+
+            // If media is removed from slot loading type, start powering off the device
+            // if it is ZPODD capable.
+            if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
+                (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) &&
+                (DeviceExtension->ZeroPowerODDInfo->Load == 0))                                    // Slot
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                           "GesnDataInterpret: MediaRemoval event detected, device marked as idle\n"));
+
+                DeviceMarkActive(DeviceExtension, FALSE, FALSE);
+            }
+        }
+        else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN::MEDIA EJECTION, Status %x\n",
+                       mediaInfo->MediaStatus));
+
+            DeviceSendNotification(DeviceExtension,
+                                   &GUID_IO_MEDIA_EJECT_REQUEST,
+                                   0,
+                                   NULL);
+        }
+
+        break;
+    }
+
+    case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS:  // lowest priority events...
+    {
+        PNOTIFICATION_BUSY_STATUS busyInfo =
+                                    (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
+        DEVICE_EVENT_BECOMING_READY busyData = {0};
+
+        // else we want to report the approximated time till it's ready.
+        busyData.Version = 1;
+        busyData.Reason = busyInfo->DeviceBusyStatus;
+        busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
+                                         (busyInfo->Time[1] & 0xff);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "GESN::BUSY: Event: %x Status %x Time %x\n",
+                   busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
+                   busyData.Estimated100msToReady
+                   ));
+
+        // Ignore the notification if the time is small
+        if (busyData.Estimated100msToReady >= GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GesnDataInterpret: media BECOMING_READY\n"));
+
+            DeviceSendNotification(DeviceExtension,
+                                   &GUID_IO_DEVICE_BECOMING_READY,
+                                   sizeof(DEVICE_EVENT_BECOMING_READY),
+                                   &busyData);
+        }
+
+        // If manual loading operation is observed for slot loading type, start powering off the device
+        // if it is ZPODD capable.
+        if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
+            (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
+            (DeviceExtension->ZeroPowerODDInfo->Load == 0) &&                                   // Drawer
+            (busyInfo->DeviceBusyEvent == NOTIFICATION_BUSY_EVENT_LO_CHANGE) &&
+            (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT))
+        {
+            inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension);
+
+            if (inHomePosition == FALSE)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                           "GesnDataInterpret: LoChange event detected, device marked as active\n"));
+
+                DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+            }
+            else
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                           "GesnDataInterpret: LoChange event detected, device marked as idle\n"));
+
+                DeviceMarkActive(DeviceExtension, FALSE, FALSE);
+            }
+        }
+
+        break;
+    }
+
+    default:
+    {
+        break;
+    }
+
+    } // end switch on notification class
+
+    return status;
+}
+
+
+VOID
+DeviceInternalSetMediaChangeState(
+    _In_        PCDROM_DEVICE_EXTENSION       DeviceExtension,
+    _In_        MEDIA_CHANGE_DETECTION_STATE  NewState,
+    _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState
+    )
+/*++
+
+Routine Description:
+
+    This routine will (if appropriate) set the media change event for the
+    device.  The event will be set if the media state is changed and
+    media change events are enabled.  Otherwise the media state will be
+    tracked but the event will not be set.
+
+    This routine will lock out the other media change routines if possible
+    but if not a media change notification may be lost after the enable has
+    been completed.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+    NewState - new state for setting
+
+    OldState - optional storage for the old state
+
+Return Value:
+
+    none
+
+--*/
+{
+#if DBG
+    LPCSTR states[] = {"Unknown", "Present", "Not Present", "Unavailable"};
+#endif
+    MEDIA_CHANGE_DETECTION_STATE oldMediaState;
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+    CLASS_MEDIA_CHANGE_CONTEXT   mcnContext;
+
+    if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable)))
+    {
+        return;
+    }
+
+    if (info == NULL)
+    {
+        return;
+    }
+
+    oldMediaState = info->LastKnownMediaDetectionState;
+    if (OldState)
+    {
+        *OldState = oldMediaState;
+    }
+
+    info->LastKnownMediaDetectionState = NewState;
+
+    // Increment MediaChangeCount on transition to MediaPresent
+    if (NewState == MediaPresent && oldMediaState != NewState)
+    {
+        InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
+    }
+
+    if (info->MediaChangeDetectionDisableCount != 0)
+    {
+#if DBG
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceInternalSetMediaChangeState: MCN not enabled, state "
+                    "changed from %s to %s\n",
+                    states[oldMediaState], states[NewState]));
+#endif
+        return;
+    }
+#if DBG
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                "DeviceInternalSetMediaChangeState: State change from %s to %s\n",
+                states[oldMediaState], states[NewState]));
+#endif
+
+    if (info->LastReportedMediaDetectionState == info->LastKnownMediaDetectionState)
+    {
+        // Media is in the same state as we reported last time, no need to report again.
+        return;
+    }
+
+    // make the data useful -- it used to always be zero.
+    mcnContext.MediaChangeCount = DeviceExtension->MediaChangeCount;
+    mcnContext.NewState = NewState;
+
+    if (NewState == MediaPresent)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceInternalSetMediaChangeState: Reporting media ARRIVAL\n"));
+
+        DeviceSendNotification(DeviceExtension,
+                               &GUID_IO_MEDIA_ARRIVAL,
+                               sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
+                               &mcnContext);
+    }
+    else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceInternalSetMediaChangeState: Reporting media REMOVAL\n"));
+        DeviceSendNotification(DeviceExtension,
+                               &GUID_IO_MEDIA_REMOVAL,
+                               sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
+                               &mcnContext);
+    }
+    else
+    {
+        // Don't notify of changed going to unknown.
+        return;
+    }
+
+    info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState;
+
+    return;
+} // end DeviceInternalSetMediaChangeState()
+
+
+VOID
+DeviceSetMediaChangeStateEx(
+    _In_        PCDROM_DEVICE_EXTENSION       DeviceExtension,
+    _In_        MEDIA_CHANGE_DETECTION_STATE  NewState,
+    _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState
+    )
+/*++
+
+Routine Description:
+
+    This routine will (if appropriate) set the media change event for the
+    device.  The event will be set if the media state is changed and
+    media change events are enabled.  Otherwise the media state will be
+    tracked but the event will not be set.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+    NewState - new state for setting
+
+    OldState - optional storage for the old state
+
+Return Value:
+
+    none
+
+--*/
+{
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+    LARGE_INTEGER                zero;
+    NTSTATUS                     status;
+
+    // timeout value must be 0, as this function can be called at DISPATCH_LEVEL.
+    zero.QuadPart = 0;
+
+    if (info == NULL)
+    {
+        return;
+    }
+
+    status = KeWaitForMutexObject(&info->MediaChangeMutex,
+                                Executive,
+                                KernelMode,
+                                FALSE,
+                                &zero);
+
+    if (status == STATUS_TIMEOUT)
+    {
+        // Someone else is in the process of setting the media state.
+        return;
+    }
+
+    // Change the media present state and signal an event, if applicable
+    DeviceInternalSetMediaChangeState(DeviceExtension, NewState, OldState);
+
+    KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+    return;
+} // end DeviceSetMediaChangeStateEx()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceSendDelayedMediaChangeNotifications(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine sends the so-called delayed media change notifications.
+    These notifications get accumulated while the MCN mechanism is disabled
+    and need to be sent to the application on MCN enabling, if MCN enabling
+    happens as a part of exclusive access unlock and the application has not
+    requested us explicitly to not send the delayed notifications.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+Return Value:
+
+    none
+
+--*/
+{
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+    LARGE_INTEGER                zero;
+    NTSTATUS                     status;
+
+    PAGED_CODE();
+
+    zero.QuadPart = 0;
+
+    if (info == NULL)
+    {
+        return;
+    }
+
+    status = KeWaitForMutexObject(&info->MediaChangeMutex,
+                                  Executive,
+                                  KernelMode,
+                                  FALSE,
+                                  &zero);
+
+    if (status == STATUS_TIMEOUT)
+    {
+        // Someone else is in the process of setting the media state.
+        // That's totally okay, we'll send delayed notifications later.
+        return;
+    }
+
+    // If the last reported state and the last known state are different and
+    // MCN is enabled, generate a notification based on the last known state.
+    if ((info->LastKnownMediaDetectionState != info->LastReportedMediaDetectionState) &&
+        (info->MediaChangeDetectionDisableCount == 0))
+    {
+        DeviceInternalSetMediaChangeState(DeviceExtension, info->LastKnownMediaDetectionState, NULL);
+    }
+
+    KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+    return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestSetupMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION      DeviceExtension,
+    _In_ BOOLEAN                      UseGesn
+)
+/*++
+
+Routine Description:
+
+    This routine sets up the fields of the request for MCN
+
+Arguments:
+    DeviceExtension - device context
+
+    UseGesn - If TRUE, the device supports GESN and it's currently the mechanism for MCN
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                     status = STATUS_SUCCESS;
+    PSCSI_REQUEST_BLOCK          srb;
+    PIRP                         irp;
+    PIO_STACK_LOCATION           nextIrpStack;
+    PCDB                         cdb;
+    PVOID                        buffer;
+    WDF_REQUEST_REUSE_PARAMS     params;
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+
+    PAGED_CODE();
+
+    irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
+    NT_ASSERT(irp != NULL);
+
+    // deassign the MdlAddress, this is the value we assign explicitly.
+    // this is to prevent WdfRequestReuse to release the Mdl unexpectly.
+    if (irp->MdlAddress)
+    {
+        irp->MdlAddress = NULL;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Setup the IRP to perform a test unit ready.
+        WDF_REQUEST_REUSE_PARAMS_INIT(&params,
+                                      WDF_REQUEST_REUSE_NO_FLAGS,
+                                      STATUS_NOT_SUPPORTED);
+
+        status = WdfRequestReuse(info->MediaChangeRequest, &params);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Format the request.
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                                info->MediaChangeRequest,
+                                                                IOCTL_SCSI_EXECUTE_IN,
+                                                                NULL, NULL,
+                                                                NULL, NULL,
+                                                                NULL, NULL);
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "RequestSetupMcnRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
+                       status));
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RequestClearSendTime(info->MediaChangeRequest);
+
+        nextIrpStack = IoGetNextIrpStackLocation(irp);
+
+        nextIrpStack->Flags = SL_OVERRIDE_VERIFY_VOLUME;
+        nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+        nextIrpStack->Parameters.Scsi.Srb = &(info->MediaChangeSrb);
+
+        // Prepare the SRB for execution.
+        srb    = nextIrpStack->Parameters.Scsi.Srb;
+        buffer = info->SenseBuffer;
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+        RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
+
+        srb->QueueTag        = SP_UNTAGGED;
+        srb->QueueAction     = SRB_SIMPLE_TAG_REQUEST;
+        srb->Length          = sizeof(SCSI_REQUEST_BLOCK);
+        srb->Function        = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->SenseInfoBuffer = buffer;
+        srb->SrbStatus       = 0;
+        srb->ScsiStatus      = 0;
+        srb->OriginalRequest = irp;
+        srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+        srb->SrbFlags        = DeviceExtension->SrbFlags;
+        SET_FLAG(srb->SrbFlags, info->SrbFlags);
+
+        if (!UseGesn)
+        {
+            srb->TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
+            srb->CdbLength = 6;
+            srb->DataTransferLength = 0;
+            SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+            nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
+            srb->DataBuffer = NULL;
+            srb->DataTransferLength = 0;
+            irp->MdlAddress = NULL;
+
+            cdb = (PCDB) &srb->Cdb[0];
+            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+        }
+        else
+        {
+            NT_ASSERT(info->Gesn.Buffer);
+
+            srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
+
+            srb->CdbLength = 10;
+            SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+            nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+            srb->DataBuffer = info->Gesn.Buffer;
+            srb->DataTransferLength = info->Gesn.BufferSize;
+            irp->MdlAddress = info->Gesn.Mdl;
+
+            cdb = (PCDB) &srb->Cdb[0];
+            cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = SCSIOP_GET_EVENT_STATUS;
+            cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
+            cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = (UCHAR)((info->Gesn.BufferSize) >> 8);
+            cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = (UCHAR)((info->Gesn.BufferSize) & 0xff);
+            cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = info->Gesn.EventMask;
+        }
+    }
+
+    return status;
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceDisableGesn(
+    _In_ WDFWORKITEM  WorkItem
+    )
+/*++
+
+Routine Description:
+
+    Work item routine to set the hack flag in the registry to disable GESN
+    This routine is invoked when the device reports TOO many events that affects system
+
+Arguments:
+    WorkItem - the work item be perfromed.
+
+Return Value:
+    None
+
+--*/
+{
+    WDFDEVICE               device = WdfWorkItemGetParentObject(WorkItem);
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+
+    PAGED_CODE();
+
+    //
+    // Set the hack flag in the registry
+    //
+    DeviceSetParameter(deviceExtension,
+                       CLASSP_REG_SUBKEY_NAME,
+                       CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                       CdromDetectionUnsupported);
+
+    WdfObjectDelete(WorkItem);
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+RequestPostWorkMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION      DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine handles the completion of the test unit ready irps used to
+    determine if the media has changed.  If the media has changed, this code
+    signals the named event to wake up other system services that react to
+    media change (aka AutoPlay).
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+
+    BOOLEAN - TRUE (needs retry); FALSE (shoule not retry)
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
+    PIRP                            irp;
+    BOOLEAN                         retryImmediately = FALSE;
+
+    PAGED_CODE();
+
+    NT_ASSERT(info->MediaChangeRequest != NULL);
+    irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
+
+    NT_ASSERT(!TEST_FLAG(info->MediaChangeSrb.SrbStatus, SRB_STATUS_QUEUE_FROZEN));
+
+    // use InterpretSenseInfo routine to check for media state, and also
+    // to call ClassError() with correct parameters.
+    if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS)
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "MCN request - failed - srb status=%x, sense=%x/%x/%x.\n",
+                   info->MediaChangeSrb.SrbStatus,
+                   ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->SenseKey,
+                   ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCode,
+                   ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCodeQualifier));
+
+        if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_NOT_POWERED)
+        {
+            // Release the queue if it is frozen.
+            if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
+            {
+                DeviceReleaseQueue(DeviceExtension->Device);
+            }
+
+            RequestSenseInfoInterpret(DeviceExtension,
+                                      info->MediaChangeRequest,
+                                      &info->MediaChangeSrb,
+                                      0,
+                                      &status,
+                                      NULL);
+        }
+    }
+    else
+    {
+        DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = FALSE;
+
+        if (!info->Gesn.Supported)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "MCN request - succeeded (GESN NOT supported, setting MediaPresent).\n"));
+
+            // success != media for GESN case
+            DeviceSetMediaChangeStateEx(DeviceExtension,
+                                        MediaPresent,
+                                        NULL);
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+                       "MCN request - succeeded (GESN supported).\n"));
+        }
+    }
+
+    if (info->Gesn.Supported)
+    {
+        if (status == STATUS_DATA_OVERRUN)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request - Data Overrun\n"));
+            status = STATUS_SUCCESS;
+        }
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: GESN failed with status %x\n", status));
+        }
+        else
+        {
+            // for GESN, need to interpret the results of the data.
+            // this may also require an immediate retry
+            if (irp->IoStatus.Information == 8 )
+            {
+                GesnDataInterpret(DeviceExtension,
+                                  (PVOID)info->Gesn.Buffer,
+                                  &retryImmediately);
+            }
+
+        } // end of NT_SUCCESS(status)
+
+    } // end of Info->Gesn.Supported
+
+    // free port-allocated sense buffer, if any.
+    if (PORT_ALLOCATED_SENSE(DeviceExtension, &info->MediaChangeSrb))
+    {
+        FREE_PORT_ALLOCATED_SENSE_BUFFER(DeviceExtension, &info->MediaChangeSrb);
+    }
+
+    // Remember the IRP and SRB for use the next time.
+    NT_ASSERT(IoGetNextIrpStackLocation(irp));
+    IoGetNextIrpStackLocation(irp)->Parameters.Scsi.Srb = &info->MediaChangeSrb;
+
+    // run a sanity check to make sure we're not recursing continuously
+    if (retryImmediately)
+    {
+        info->MediaChangeRetryCount++;
+
+        if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES)
+        {
+            // Disable GESN on this device.
+            // Create a work item to set the value in the registry
+            WDF_OBJECT_ATTRIBUTES   attributes;
+            WDF_WORKITEM_CONFIG     workitemConfig;
+            WDFWORKITEM             workItem;
+
+            WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+            attributes.ParentObject = DeviceExtension->Device;
+
+            WDF_WORKITEM_CONFIG_INIT(&workitemConfig, DeviceDisableGesn);
+            workitemConfig.AutomaticSerialization = FALSE;
+
+            status = WdfWorkItemCreate(&workitemConfig,
+                                       &attributes,
+                                       &workItem);
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: Disabling GESN for WDFDEVICE %p\n", DeviceExtension->Device));
+
+            if (NT_SUCCESS(status))
+            {
+                WdfWorkItemEnqueue(workItem);
+            }
+
+            info->Gesn.Supported  = FALSE;
+            info->Gesn.EventMask  = 0;
+            info->Gesn.BufferSize = 0;
+            info->MediaChangeRetryCount = 0;
+            retryImmediately = FALSE;
+            // should we log an error in event log?
+        }
+    }
+    else
+    {
+        info->MediaChangeRetryCount = 0;
+    }
+
+    return retryImmediately;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+RequestSendMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine sends the formatted MCN request sychronizely to lower driver.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+    BOOLEAN - TRUE (requst successfully sent); FALSE (request failed to send)
+
+--*/
+{
+    BOOLEAN                      requestSent = FALSE;
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+
+    PAGED_CODE();
+
+    RequestSend(DeviceExtension,
+                info->MediaChangeRequest,
+                DeviceExtension->IoTarget,
+                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+                &requestSent);
+
+    return requestSent;
+} // end RequestSendMcnRequest()
+
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeMcn(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ BOOLEAN                  AllowDriveToSleep
+    )
+/*++
+
+Routine Description:
+
+    This routine initialize the contents of MCN structure.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+    AllowDriveToSleep - for CDROM, this parameter should be always FALSE
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PMEDIA_CHANGE_DETECTION_INFO    mediaChangeInfo = NULL;
+    PIRP                            irp = NULL;
+    PVOID                           senseBuffer = NULL;
+    WDF_OBJECT_ATTRIBUTES           attributes;
+
+    PAGED_CODE();
+
+    if (DeviceExtension->MediaChangeDetectionInfo != NULL)
+    {
+        //Already initialized.
+        return STATUS_SUCCESS;
+    }
+
+    DeviceExtension->KernelModeMcnContext.FileObject      = (PVOID)-1;
+    DeviceExtension->KernelModeMcnContext.DeviceObject    = (PVOID)-1;
+    DeviceExtension->KernelModeMcnContext.LockCount       = 0;
+    DeviceExtension->KernelModeMcnContext.McnDisableCount = 0;
+
+    mediaChangeInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                            sizeof(MEDIA_CHANGE_DETECTION_INFO),
+                                            CDROM_TAG_MEDIA_CHANGE_DETECTION);
+
+    if (mediaChangeInfo == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        RtlZeroMemory(mediaChangeInfo, sizeof(MEDIA_CHANGE_DETECTION_INFO));
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if ((DeviceExtension->PowerDescriptor != NULL) &&
+            (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported != FALSE) &&
+            (!TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_ASYNCHRONOUS_NOTIFICATION)))
+        {
+            mediaChangeInfo->AsynchronousNotificationSupported = TRUE;
+        }
+    }
+
+    //  Allocate an IRP to carry the IOCTL_MCN_SYNC_FAKE_IOCTL.
+    if (NT_SUCCESS(status))
+    {
+        irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE);
+
+        if (irp == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                                CDROM_REQUEST_CONTEXT);
+        attributes.ParentObject = DeviceExtension->Device;
+        status = WdfRequestCreate(&attributes,
+                                  DeviceExtension->IoTarget,
+                                  &mediaChangeInfo->MediaChangeRequest);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Preformat the media change request. With this being done, we never need to worry about
+        // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later.
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                                mediaChangeInfo->MediaChangeRequest,
+                                                                IOCTL_SCSI_EXECUTE_IN,
+                                                                NULL, NULL,
+                                                                NULL, NULL,
+                                                                NULL, NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        senseBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                            SENSE_BUFFER_SIZE,
+                                            CDROM_TAG_MEDIA_CHANGE_DETECTION);
+        if (senseBuffer == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        mediaChangeInfo->MediaChangeSyncIrp = irp;
+        mediaChangeInfo->SenseBuffer = senseBuffer;
+
+        // Set default values for the media change notification
+        // configuration.
+        mediaChangeInfo->MediaChangeDetectionDisableCount = 0;
+
+        // Assume that there is initially no media in the device
+        // only notify upper layers if there is something there
+        mediaChangeInfo->LastKnownMediaDetectionState = MediaUnknown;
+        mediaChangeInfo->LastReportedMediaDetectionState = MediaUnknown;
+
+        // setup all extra flags we'll be setting for this irp
+        mediaChangeInfo->SrbFlags = 0;
+
+        SET_FLAG(mediaChangeInfo->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
+        SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+        SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+        if (AllowDriveToSleep)  //FALSE for CD/DVD devices
+        {
+            SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+        }
+
+        KeInitializeMutex(&mediaChangeInfo->MediaChangeMutex, 0x100);
+
+        // It is ok to support media change events on this device.
+        DeviceExtension->MediaChangeDetectionInfo = mediaChangeInfo;
+
+        // check the device supports GESN or not, initialize GESN structure if it supports.
+        {
+            // This is only valid for type5 devices.
+            NTSTATUS tempStatus = STATUS_SUCCESS;
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "DeviceInitializeMcn: Testing for GESN\n"));
+            tempStatus = DeviceInitializeGesn(DeviceExtension);
+
+            if (NT_SUCCESS(tempStatus))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                           "DeviceInitializeMcn: GESN available for %p\n",
+                           DeviceExtension->DeviceObject));
+                NT_ASSERT(mediaChangeInfo->Gesn.Supported );
+                NT_ASSERT(mediaChangeInfo->Gesn.Buffer     != NULL);
+                NT_ASSERT(mediaChangeInfo->Gesn.BufferSize != 0);
+                NT_ASSERT(mediaChangeInfo->Gesn.EventMask  != 0);
+            }
+            else
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                           "DeviceInitializeMcn: GESN *NOT* available for %p\n",
+                           DeviceExtension->DeviceObject));
+                NT_ASSERT(!mediaChangeInfo->Gesn.Supported);
+                NT_ASSERT(mediaChangeInfo->Gesn.Buffer == NULL);
+                NT_ASSERT(mediaChangeInfo->Gesn.BufferSize == 0);
+                NT_ASSERT(mediaChangeInfo->Gesn.EventMask  == 0);
+                mediaChangeInfo->Gesn.Supported = FALSE; // just in case....
+            }
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Register for display state change on AOAC capable systems so we can put the
+        // device to low power state when not required.
+        if (mediaChangeInfo->DisplayStateCallbackHandle == NULL)
+        {
+            POWER_PLATFORM_INFORMATION PlatformInfo = {0};
+
+            status = ZwPowerInformation(PlatformInformation,
+                                        NULL,
+                                        0,
+                                        &PlatformInfo,
+                                        sizeof(PlatformInfo));
+
+            if (NT_SUCCESS(status) && PlatformInfo.AoAc)
+            {
+                PoRegisterPowerSettingCallback(DeviceExtension->DeviceObject,
+                                               &GUID_CONSOLE_DISPLAY_STATE,
+                                               &DevicePowerSettingCallback,
+                                               DeviceExtension,
+                                               &mediaChangeInfo->DisplayStateCallbackHandle);
+            }
+
+            // Ignore any failures above.
+            status = STATUS_SUCCESS;
+        }
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        if (irp != NULL)
+        {
+            IoFreeIrp(irp);
+        }
+        FREE_POOL(senseBuffer);
+        FREE_POOL(mediaChangeInfo);
+    }
+
+    return status;
+
+} // end DeviceInitializeMcn()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeGesn(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine initialize the contents of GESN structure.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                            status = STATUS_SUCCESS;
+    PNOTIFICATION_EVENT_STATUS_HEADER   header = NULL;
+    CDROM_DETECTION_STATE               detectionState = CdromDetectionUnknown;
+    PSTORAGE_DEVICE_DESCRIPTOR          deviceDescriptor = DeviceExtension->DeviceDescriptor;
+    BOOLEAN                             retryImmediately = TRUE;
+    ULONG                               i = 0;
+    ULONG                               atapiResets = 0;
+    PMEDIA_CHANGE_DETECTION_INFO        info = DeviceExtension->MediaChangeDetectionInfo;
+
+    PAGED_CODE();
+
+    NT_ASSERT(info != NULL);
+
+    // read if we already know the abilities of the device
+    DeviceGetParameter(DeviceExtension,
+                       CLASSP_REG_SUBKEY_NAME,
+                       CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                       (PULONG)&detectionState);
+
+    if (detectionState == CdromDetectionUnsupported)
+    {
+        status = STATUS_NOT_SUPPORTED;
+    }
+
+    // check if the device has a hack flag saying never to try this.
+    if (NT_SUCCESS(status) &&
+        (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_GESN_IS_BAD)) )
+    {
+        DeviceSetParameter(DeviceExtension,
+                           CLASSP_REG_SUBKEY_NAME,
+                           CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                           CdromDetectionUnsupported);
+        status = STATUS_NOT_SUPPORTED;
+    }
+
+    // else go through the process since we allocate buffers and
+    // get all sorts of device settings.
+    if (NT_SUCCESS(status))
+    {
+        if (info->Gesn.Buffer == NULL)
+        {
+            info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                                      GESN_BUFFER_SIZE,
+                                                      CDROM_TAG_GESN);
+        }
+
+        if (info->Gesn.Buffer == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (info->Gesn.Mdl != NULL)
+        {
+            IoFreeMdl(info->Gesn.Mdl);
+        }
+
+        info->Gesn.Mdl = IoAllocateMdl(info->Gesn.Buffer,
+                                       GESN_BUFFER_SIZE,
+                                       FALSE,
+                                       FALSE,
+                                       NULL);
+        if (info->Gesn.Mdl == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        MmBuildMdlForNonPagedPool(info->Gesn.Mdl);
+        info->Gesn.BufferSize = GESN_BUFFER_SIZE;
+        info->Gesn.EventMask = 0;
+
+        // all items are prepared to use GESN (except the event mask, so don't
+        // optimize this part out!).
+        //
+        // now see if it really works. we have to loop through this because
+        // many SAMSUNG (and one COMPAQ) drives timeout when requesting
+        // NOT_READY events, even when the IMMEDIATE bit is set. :(
+        //
+        // using a drive list is cumbersome, so this might fix the problem.
+        for (i = 0; (i < 16) && retryImmediately; i++)
+        {
+            status = RequestSetupMcnRequest(DeviceExtension, TRUE);
+
+            if (!NT_SUCCESS(status))
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                           "Setup Mcn request failed %x for WDFDEVICE %p\n",
+                           status, DeviceExtension->Device));
+                break;
+            }
+
+            NT_ASSERT(TEST_FLAG(info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
+
+            status = DeviceSendRequestSynchronously(DeviceExtension->Device, info->MediaChangeRequest, TRUE);
+
+            if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS)
+            {
+                // Release the queue if it is frozen.
+                if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
+                {
+                    DeviceReleaseQueue(DeviceExtension->Device);
+                }
+
+                RequestSenseInfoInterpret(DeviceExtension,
+                                          info->MediaChangeRequest,
+                                          &(info->MediaChangeSrb),
+                                          0,
+                                          &status,
+                                          NULL);
+            }
+
+            if ((deviceDescriptor->BusType == BusTypeAtapi) &&
+                (info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET))
+            {
+                //
+                // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
+                // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
+                // the two.  if we get this status four time consecutively,
+                // stop trying this command.  it is too late to change ATAPI
+                // at this point, so special-case this here. (07/10/2001)
+                // NOTE: any value more than 4 may cause the device to be
+                //       marked missing.
+                //
+                atapiResets++;
+                if (atapiResets >= 4)
+                {
+                    status = STATUS_IO_DEVICE_ERROR;
+                    break;
+                }
+            }
+
+            if (status == STATUS_DATA_OVERRUN)
+            {
+                status = STATUS_SUCCESS;
+            }
+
+            if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
+                (status == STATUS_TIMEOUT) ||
+                (status == STATUS_IO_DEVICE_ERROR) ||
+                (status == STATUS_IO_TIMEOUT))
+            {
+                // with these error codes, we don't ever want to try this command
+                // again on this device, since it reacts poorly.
+                DeviceSetParameter( DeviceExtension,
+                                    CLASSP_REG_SUBKEY_NAME,
+                                    CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                                    CdromDetectionUnsupported);
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                           "GESN test failed %x for WDFDEVICE %p\n",
+                           status, DeviceExtension->Device));
+                break;
+            }
+
+            if (!NT_SUCCESS(status))
+            {
+                // this may be other errors that should not disable GESN
+                // for all future start_device calls.
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                           "GESN test failed %x for WDFDEVICE %p\n",
+                           status, DeviceExtension->Device));
+                break;
+            }
+            else if (i == 0)
+            {
+                // the first time, the request was just retrieving a mask of
+                // available bits.  use this to mask future requests.
+                header = (PNOTIFICATION_EVENT_STATUS_HEADER)(info->Gesn.Buffer);
+
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                           "WDFDEVICE %p supports event mask %x\n",
+                           DeviceExtension->Device, header->SupportedEventClasses));
+
+                if (TEST_FLAG(header->SupportedEventClasses,
+                              NOTIFICATION_MEDIA_STATUS_CLASS_MASK))
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                               "GESN supports MCN\n"));
+                }
+                if (TEST_FLAG(header->SupportedEventClasses,
+                              NOTIFICATION_DEVICE_BUSY_CLASS_MASK))
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                               "GESN supports DeviceBusy\n"));
+                }
+                if (TEST_FLAG(header->SupportedEventClasses,
+                              NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK))
+                {
+                    if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags,
+                                  FDO_HACK_GESN_IGNORE_OPCHANGE))
+                    {
+                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                                    "GESN supports OpChange, but must ignore these events for compatibility\n"));
+                        CLEAR_FLAG(header->SupportedEventClasses,
+                                   NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK);
+                    }
+                    else
+                    {
+                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                                    "GESN supports OpChange\n"));
+                    }
+                }
+                info->Gesn.EventMask = header->SupportedEventClasses;
+
+                //
+                // realistically, we are only considering the following events:
+                //    EXTERNAL REQUEST - this is being tested for play/stop/etc.
+                //    MEDIA STATUS - autorun and ejection requests.
+                //    DEVICE BUSY - to allow us to predict when media will be ready.
+                // therefore, we should not bother querying for the other,
+                // unknown events. clear all but the above flags.
+                //
+                info->Gesn.EventMask &= NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK |
+                                        NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK   |
+                                        NOTIFICATION_MEDIA_STATUS_CLASS_MASK       |
+                                        NOTIFICATION_DEVICE_BUSY_CLASS_MASK        ;
+
+
+                //
+                // HACKHACK - REF #0001
+                // Some devices will *never* report an event if we've also requested
+                // that it report lower-priority events.  this is due to a
+                // misunderstanding in the specification wherein a "No Change" is
+                // interpreted to be a real event.  what should occur is that the
+                // device should ignore "No Change" events when multiple event types
+                // are requested unless there are no other events waiting.  this
+                // greatly reduces the number of requests that the host must send
+                // to determine if an event has occurred. Since we must work on all
+                // drives, default to enabling the hack until we find evidence of
+                // proper firmware.
+                //
+                if (info->Gesn.EventMask == 0)
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                               "GESN supported, but not mask we care about (%x) for FDO %p\n",
+                               header->SupportedEventClasses,
+                               DeviceExtension->DeviceObject));
+                    // NOTE: the status is still status_sucess.
+                    break;
+                }
+                else if (CountOfSetBitsUChar(info->Gesn.EventMask) == 1)
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                               "GESN hack not required for FDO %p\n",
+                               DeviceExtension->DeviceObject));
+                }
+                else
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                               "GESN hack enabled for FDO %p\n",
+                               DeviceExtension->DeviceObject));
+                    info->Gesn.HackEventMask = 1;
+                }
+            }
+            else
+            {
+                // i > 0; not the first time looping through, so interpret the results.
+                status = GesnDataInterpret(DeviceExtension,
+                                           (PVOID)info->Gesn.Buffer,
+                                           &retryImmediately);
+
+                if (!NT_SUCCESS(status))
+                {
+                    // This drive does not support GESN correctly
+                    DeviceSetParameter( DeviceExtension,
+                                        CLASSP_REG_SUBKEY_NAME,
+                                        CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                                        CdromDetectionUnsupported);
+                    break;
+                }
+            }
+        } // end 'for' loop of GESN requests....
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        //
+        // we can only use this if it can be relied upon for media changes,
+        // since we are (by definition) no longer going to be polling via
+        // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
+        // for this command (although a filter driver, such as one for burning
+        // cd's, might still fake those errors).
+        //
+        // since we also rely upon NOT_READY events to change the cursor
+        // into a "wait" cursor; GESN is still more reliable than other
+        // methods, and includes eject button requests, so we'll use it
+        // without DEVICE_BUSY in Windows Vista.
+        //
+
+        if (TEST_FLAG(info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "Enabling GESN support for WDFDEVICE %p\n",
+                       DeviceExtension->Device));
+            info->Gesn.Supported = TRUE;
+
+            DeviceSetParameter( DeviceExtension,
+                                CLASSP_REG_SUBKEY_NAME,
+                                CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+                                CdromDetectionSupported);
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                       "GESN available but not enabled for WDFDEVICE %p\n",
+                       DeviceExtension->Device));
+            status = STATUS_NOT_SUPPORTED;
+        }
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "GESN support detection failed  for WDFDEVICE %p with status %08x\n",
+                   DeviceExtension->Device, status));
+
+        if (info->Gesn.Mdl)
+        {
+            PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
+
+            IoFreeMdl(info->Gesn.Mdl);
+            info->Gesn.Mdl = NULL;
+            irp->MdlAddress = NULL;
+        }
+
+        FREE_POOL(info->Gesn.Buffer);
+        info->Gesn.Supported  = FALSE;
+        info->Gesn.EventMask  = 0;
+        info->Gesn.BufferSize = 0;
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeMediaChangeDetection(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine checks to see if it is safe to initialize MCN (the back end
+    to autorun) for a given device.  It will then check the device-type wide
+    key "Autorun" in the service key (for legacy reasons), and then look in
+    the device-specific key to potentially override that setting.
+
+    If MCN is to be enabled, all neccessary structures and memory are
+    allocated and initialized.
+
+    This routine MUST be called only from the DeviceInit...() .
+
+Arguments:
+
+    DeviceExtension - the device to initialize MCN for, if appropriate
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    BOOLEAN     disabled = FALSE;
+    BOOLEAN     instanceOverride;
+
+    PAGED_CODE();
+
+    // NOTE: This assumes that DeviceInitializeMediaChangeDetection is always
+    //       called in the context of the DeviceInitDevicePhase2. If called
+    //       after then this check will have already been made and the
+    //       once a second timer will not have been enabled.
+
+    disabled = DeviceIsMediaChangeDisabledDueToHardwareLimitation(DeviceExtension);
+
+    if (disabled)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceInitializeMediaChangeDetection: Disabled due to hardware"
+                    "limitations for this device\n"));
+    }
+    else
+    {
+        // autorun should now be enabled by default for all media types.
+        disabled = DeviceIsMediaChangeDisabledForClass(DeviceExtension);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceInitializeMediaChangeDetection: MCN is %s\n",
+                    (disabled ? "disabled" : "enabled")));
+
+        status = DeviceMediaChangeDeviceInstanceOverride(DeviceExtension,
+                                                         &instanceOverride);  // default value
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                        "DeviceInitializeMediaChangeDetection: Instance using default\n"));
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                        "DeviceInitializeMediaChangeDetection: Instance override: %s MCN\n",
+                        (instanceOverride ? "Enabling" : "Disabling")));
+            disabled = !instanceOverride;
+        }
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceInitializeMediaChangeDetection: Instance MCN is %s\n",
+                    (disabled ? "disabled" : "enabled")));
+    }
+
+    if (!disabled)
+    {
+        // if the drive is not a CDROM, allow the drive to sleep
+        status = DeviceInitializeMcn(DeviceExtension, FALSE);
+    }
+
+    return status;
+} // end DeviceInitializeMediaChangeDetection()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceMediaChangeDeviceInstanceOverride(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _Out_ PBOOLEAN                Enabled
+    )
+/*++
+
+Routine Description:
+
+    The user can override the global setting to enable or disable Autorun on a
+    specific cdrom device via the control panel.  This routine checks and/or
+    sets this value.
+
+Arguments:
+
+    DeviceExtension - the device to set/get the value for
+
+    Enabled - TRUE (Autorun is enabled)
+              FALSE (Autorun is disabled)
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_UNSUCCESSFUL;
+    WDFKEY          deviceKey = NULL;
+    WDFKEY          subKey = NULL;
+
+    UNICODE_STRING  subkeyName;
+    UNICODE_STRING  enableMcnValueName;
+    UNICODE_STRING  disableMcnValueName;
+    ULONG           alwaysEnable = 0;
+    ULONG           alwaysDisable = 0;
+
+    PAGED_CODE();
+
+    status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
+                                      PLUGPLAY_REGKEY_DEVICE,
+                                      KEY_ALL_ACCESS,
+                                      WDF_NO_OBJECT_ATTRIBUTES,
+                                      &deviceKey);
+    if (!NT_SUCCESS(status))
+    {
+        // this can occur when a new device is added to the system
+        // this is due to cdrom.sys being an 'essential' driver
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: "
+                    "Could not open device registry key [%lx]\n", status));
+    }
+    else
+    {
+        RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
+
+        status = WdfRegistryOpenKey(deviceKey,
+                                    &subkeyName,
+                                    KEY_READ,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    &subKey);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: "
+                    "subkey could not be created. %lx\n", status));
+    }
+    else
+    {
+        // Default to not changing autorun behavior, based upon setting
+        // registryValue to zero.
+        RtlInitUnicodeString(&enableMcnValueName, MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME);
+        RtlInitUnicodeString(&disableMcnValueName, MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME);
+
+        // Ignore failures on reading of subkeys
+        (VOID) WdfRegistryQueryULong(subKey,
+                                     &enableMcnValueName,
+                                     &alwaysEnable);
+        (VOID) WdfRegistryQueryULong(subKey,
+                                     &disableMcnValueName,
+                                     &alwaysDisable);
+    }
+
+    // set return value and cleanup
+
+    if (subKey != NULL)
+    {
+        WdfRegistryClose(subKey);
+    }
+
+    if (deviceKey != NULL)
+    {
+        WdfRegistryClose(deviceKey);
+    }
+
+    if (alwaysEnable && alwaysDisable)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
+                    "Both Enable and Disable set -- DISABLE"));
+        NT_ASSERT(NT_SUCCESS(status));
+        status = STATUS_SUCCESS;
+        *Enabled = FALSE;
+    }
+    else if (alwaysDisable)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
+                    "DISABLE"));
+        NT_ASSERT(NT_SUCCESS(status));
+        status = STATUS_SUCCESS;
+        *Enabled = FALSE;
+    }
+    else if (alwaysEnable)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
+                    "ENABLE"));
+        NT_ASSERT(NT_SUCCESS(status));
+        status = STATUS_SUCCESS;
+        *Enabled = TRUE;
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeDeviceInstanceOverride: %s selected\n",
+                    "DEFAULT"));
+        status = STATUS_UNSUCCESSFUL;
+    }
+
+    return status;
+} // end DeviceMediaChangeDeviceInstanceOverride()
+
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceMediaChangeRegistryCallBack(
+    _In_z_ PWSTR ValueName,
+    _In_ ULONG ValueType,
+    _In_reads_bytes_opt_(ValueLength) PVOID ValueData,
+    _In_ ULONG ValueLength,
+    _In_opt_ PVOID Context,
+    _In_opt_ PVOID EntryContext
+    )
+/*++
+
+Routine Description:
+
+    This callback for a registry SZ or MULTI_SZ is called once for each
+    SZ in the value.  It will attempt to match the data with the
+    UNICODE_STRING passed in as Context, and modify EntryContext if a
+    match is found.  Written for ClasspCheckRegistryForMediaChangeCompletion
+
+Arguments:
+
+    ValueName     - name of the key that was opened
+    ValueType     - type of data stored in the value (REG_SZ for this routine)
+    ValueData     - data in the registry, in this case a wide string
+    ValueLength   - length of the data including the terminating null
+    Context       - unicode string to compare against ValueData
+    EntryContext  - should be initialized to 0, will be set to 1 if match found
+
+Return Value:
+
+    STATUS_SUCCESS
+    EntryContext will be 1 if found
+
+--*/
+{
+    PULONG          valueFound;
+    PUNICODE_STRING deviceString;
+    PWSTR           keyValue;
+
+    PAGED_CODE();
+
+    UNREFERENCED_PARAMETER(ValueName);
+
+    if ((Context == NULL) || (EntryContext == NULL))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "DeviceMediaChangeRegistryCallBack: NULL context should never be passed to registry call-back!\n"));
+
+        return STATUS_SUCCESS;
+    }
+
+    // if we have already set the value to true, exit
+    valueFound = EntryContext;
+    if ((*valueFound) != 0)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceMediaChangeRegistryCallBack: already set to true\n"));
+        return STATUS_SUCCESS;
+    }
+
+    if (ValueLength == sizeof(WCHAR))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "DeviceMediaChangeRegistryCallBack: NULL string should never be passed to registry call-back!\n"));
+        return STATUS_SUCCESS;
+    }
+
+    // if the data is not a terminated string, exit
+    if (ValueType != REG_SZ)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    deviceString = Context;
+    keyValue = ValueData;
+    ValueLength -= sizeof(WCHAR); // ignore the null character
+
+    // do not compare more memory than is in deviceString
+    if (ValueLength > deviceString->Length)
+    {
+        ValueLength = deviceString->Length;
+    }
+
+    if (keyValue == NULL)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    // if the strings match, disable autorun
+    if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength)
+    {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: Match found\n"));
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: DeviceString at %p\n",
+                    deviceString->Buffer));
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+                    "DeviceMediaChangeRegistryCallBack: KeyValue at %p\n",
+                    keyValue));
+        (*valueFound) = TRUE;
+    }
+
+    return STATUS_SUCCESS;
+} // end DeviceMediaChangeRegistryCallBack()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceIsMediaChangeDisabledDueToHardwareLimitation(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
+    which to never enable MediaChangeNotification.
+
+    The user can override the global setting to enable or disable Autorun on a
+    specific cdrom device via the control panel.
+
+    NOTE: It's intended that not use WdfRegistryQueryMultiString in this funciton,
+          as it's much more complicated.(needs WDFCOLLECTION, WDFSTRING other than
+          UNICODE_STRING)
+
+Arguments:
+
+    FdoExtension -
+    RegistryPath - pointer to the unicode string inside
+                   ...\CurrentControlSet\Services\Cdrom
+
+Return Value:
+
+    TRUE - no autorun.
+    FALSE - Autorun may be enabled
+
+--*/
+{
+    NTSTATUS                    status;
+
+    PSTORAGE_DEVICE_DESCRIPTOR  deviceDescriptor = DeviceExtension->DeviceDescriptor;
+    WDFKEY                      wdfKey;
+    HANDLE                      serviceKey = NULL;
+    RTL_QUERY_REGISTRY_TABLE    parameters[2] = {0};
+
+    UNICODE_STRING              deviceUnicodeString = {0};
+    ANSI_STRING                 deviceString = {0};
+    ULONG                       mediaChangeNotificationDisabled = 0;
+
+    PAGED_CODE();
+
+    // open the service key.
+    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+                                                KEY_ALL_ACCESS,
+                                                WDF_NO_OBJECT_ATTRIBUTES,
+                                                &wdfKey);
+
+    if(!NT_SUCCESS(status))
+    {
+        // NT_ASSERT(FALSE); __REACTOS__ : allow to fail (for 1st stage setup)
+
+        // always take the safe path.  if we can't open the service key, disable autorun
+        return TRUE;
+    }
+
+    if(NT_SUCCESS(status))
+    {
+        // Determine if drive is in a list of those requiring
+        // autorun to be disabled.  this is stored in a REG_MULTI_SZ
+        // named AutoRunAlwaysDisable.  this is required as some autochangers
+        // must load the disc to reply to ChkVerify request, causing them
+        // to cycle discs continuously.
+
+        PWSTR   nullMultiSz;
+        PUCHAR  vendorId = NULL;
+        PUCHAR  productId = NULL;
+        PUCHAR  revisionId = NULL;
+        size_t  length;
+        size_t  offset;
+
+        deviceString.Buffer        = NULL;
+        deviceUnicodeString.Buffer = NULL;
+
+        serviceKey = WdfRegistryWdmGetHandle(wdfKey);
+
+        // there may be nothing to check against
+        if ((deviceDescriptor->VendorIdOffset == 0) &&
+            (deviceDescriptor->ProductIdOffset == 0))
+        {
+            // no valid data in device extension.
+            status = STATUS_INTERNAL_ERROR;
+        }
+
+        // build deviceString using VendorId, Model and Revision.
+        // this string will be used to checked if it's one of devices in registry disable list.
+        if (NT_SUCCESS(status))
+        {
+            length = 0;
+
+            if (deviceDescriptor->VendorIdOffset == 0)
+            {
+                vendorId = NULL;
+            }
+            else
+            {
+                vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
+                length = strlen((LPCSTR)vendorId);
+            }
+
+            if ( deviceDescriptor->ProductIdOffset == 0 )
+            {
+                productId = NULL;
+            }
+            else
+            {
+                productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset;
+                length += strlen((LPCSTR)productId);
+            }
+
+            if ( deviceDescriptor->ProductRevisionOffset == 0 )
+            {
+                revisionId = NULL;
+            }
+            else
+            {
+                revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
+                length += strlen((LPCSTR)revisionId);
+            }
+
+            // allocate a buffer for the string
+            deviceString.Length = (USHORT)( length );
+            deviceString.MaximumLength = deviceString.Length + 1;
+            deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx,
+                                                                 deviceString.MaximumLength,
+                                                                 CDROM_TAG_AUTORUN_DISABLE
+                                                                 );
+            if (deviceString.Buffer == NULL)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                            "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Unable to alloc string buffer\n" ));
+                status = STATUS_INTERNAL_ERROR;
+            }
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            // copy strings to the buffer
+            offset = 0;
+
+            if (vendorId != NULL)
+            {
+                RtlCopyMemory(deviceString.Buffer + offset,
+                              vendorId,
+                              strlen((LPCSTR)vendorId));
+                offset += strlen((LPCSTR)vendorId);
+            }
+
+            if ( productId != NULL )
+            {
+                RtlCopyMemory(deviceString.Buffer + offset,
+                              productId,
+                              strlen((LPCSTR)productId));
+                offset += strlen((LPCSTR)productId);
+            }
+            if ( revisionId != NULL )
+            {
+                RtlCopyMemory(deviceString.Buffer + offset,
+                              revisionId,
+                              strlen((LPCSTR)revisionId));
+                offset += strlen((LPCSTR)revisionId);
+            }
+
+            NT_ASSERT(offset == deviceString.Length);
+
+            #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1)
+            deviceString.Buffer[deviceString.Length] = '\0';  // Null-terminated
+
+            // convert to unicode as registry deals with unicode strings
+            status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
+                                                   &deviceString,
+                                                   TRUE
+                                                   );
+            if (!NT_SUCCESS(status))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                            "DeviceIsMediaChangeDisabledDueToHardwareLimitation: cannot convert "
+                            "to unicode %lx\n", status));
+            }
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            // query the value, setting valueFound to true if found
+            nullMultiSz = L"\0";
+            parameters[0].QueryRoutine  = DeviceMediaChangeRegistryCallBack;
+            parameters[0].Flags         = RTL_QUERY_REGISTRY_REQUIRED;
+            parameters[0].Name          = L"AutoRunAlwaysDisable";
+            parameters[0].EntryContext  = &mediaChangeNotificationDisabled;
+            parameters[0].DefaultType   = REG_MULTI_SZ;
+            parameters[0].DefaultData   = nullMultiSz;
+            parameters[0].DefaultLength = 0;
+
+            status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+                                            serviceKey,
+                                            parameters,
+                                            &deviceUnicodeString,
+                                            NULL);
+            UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+        }
+    }
+
+    // Cleanup
+    {
+
+        FREE_POOL( deviceString.Buffer );
+        if (deviceUnicodeString.Buffer != NULL)
+        {
+            RtlFreeUnicodeString( &deviceUnicodeString );
+        }
+
+        // handle serviceKey will be closed by framework while it closes registry key.
+        WdfRegistryClose(wdfKey);
+    }
+
+    if (mediaChangeNotificationDisabled > 0)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Device is on MCN disable list\n"));
+    }
+
+    return (mediaChangeNotificationDisabled > 0);
+
+} // end DeviceIsMediaChangeDisabledDueToHardwareLimitation()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceIsMediaChangeDisabledForClass(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    The user must specify that AutoPlay is to run on the platform
+    by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
+    Services\<SERVICE>\Autorun:REG_DWORD:1.
+
+    The user can override the global setting to enable or disable Autorun on a
+    specific cdrom device via the control panel.
+
+Arguments:
+
+    DeviceExtension - device extension
+
+Return Value:
+
+    TRUE - Autorun is disabled for this class
+    FALSE - Autorun is enabled for this class
+
+--*/
+{
+    NTSTATUS                 status;
+    WDFKEY                   serviceKey = NULL;
+    WDFKEY                   parametersKey = NULL;
+
+    UNICODE_STRING           parameterKeyName;
+    UNICODE_STRING           valueName;
+
+    //  Default to ENABLING MediaChangeNotification (!)
+    ULONG                    mcnRegistryValue = 1;
+
+    PAGED_CODE();
+
+    // open the service key.
+    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+                                                KEY_ALL_ACCESS,
+                                                WDF_NO_OBJECT_ATTRIBUTES,
+                                                &serviceKey);
+    if(!NT_SUCCESS(status))
+    {
+        // return the default value, which is the inverse of the registry setting default
+        // since this routine asks if it's disabled
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceIsMediaChangeDisabledForClass: Defaulting to %s\n",
+                   (mcnRegistryValue ? "Enabled" : "Disabled")));
+        return (BOOLEAN)(mcnRegistryValue == 0);
+    }
+    else
+    {
+        // Open the parameters key (if any) beneath the services key.
+        RtlInitUnicodeString(&parameterKeyName, L"Parameters");
+
+        status = WdfRegistryOpenKey(serviceKey,
+                                    &parameterKeyName,
+                                    KEY_READ,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    &parametersKey);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        parametersKey = NULL;
+    }
+
+    RtlInitUnicodeString(&valueName, L"Autorun");
+    // ignore failures
+    status = WdfRegistryQueryULong(serviceKey,
+                                   &valueName,
+                                   &mcnRegistryValue);
+
+    if (NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+                   "DeviceIsMediaChangeDisabledForClass: <Service>/Autorun flag = %d\n",
+                   mcnRegistryValue));
+    }
+
+    if (parametersKey != NULL)
+    {
+        status = WdfRegistryQueryULong(parametersKey,
+                                       &valueName,
+                                       &mcnRegistryValue);
+
+        if (NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+                       "DeviceIsMediaChangeDisabledForClass: <Service>/Parameters/Autorun flag = %d\n",
+                        mcnRegistryValue));
+        }
+
+        WdfRegistryClose(parametersKey);
+    }
+
+    WdfRegistryClose(serviceKey);
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+               "DeviceIsMediaChangeDisabledForClass: Autoplay for device %p is %s\n",
+               DeviceExtension->DeviceObject,
+               (mcnRegistryValue ? "on" : "off")
+                ));
+
+    // return if it is _disabled_, which is the
+    // inverse of the registry setting
+
+    return (BOOLEAN)(!mcnRegistryValue);
+} // end DeviceIsMediaChangeDisabledForClass()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceEnableMediaChangeDetection(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _Inout_ PFILE_OBJECT_CONTEXT    FileObjectContext,
+    _In_    BOOLEAN                 IgnorePreviousMediaChanges
+    )
+/*++
+
+Routine Description:
+
+    When the disable count decrease to 0, enable the MCN
+
+Arguments:
+
+    DeviceExtension - the device context
+
+    FileObjectContext - the file object context
+
+    IgnorePreviousMediaChanges - ignore all previous media changes
+
+Return Value:
+    None.
+
+--*/
+{
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+    LONG                         oldCount;
+
+    PAGED_CODE();
+
+    if (FileObjectContext)
+    {
+        InterlockedDecrement((PLONG)&(FileObjectContext->McnDisableCount));
+    }
+
+    if (info == NULL)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                    "DeviceEnableMediaChangeDetection: not initialized\n"));
+        return;
+    }
+
+    (VOID) KeWaitForMutexObject(&info->MediaChangeMutex,
+                                UserRequest,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+
+    oldCount = --info->MediaChangeDetectionDisableCount;
+
+    NT_ASSERT(oldCount >= 0);
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+               "DeviceEnableMediaChangeDetection: Disable count reduced to %d - \n",
+               info->MediaChangeDetectionDisableCount));
+
+    if (oldCount == 0)
+    {
+        if (IgnorePreviousMediaChanges)
+        {
+            info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState;
+        }
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN is enabled\n"));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "MCD still disabled\n"));
+    }
+
+    // Let something else run.
+    KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+    return;
+} // end DeviceEnableMediaChangeDetection()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceDisableMediaChangeDetection(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _Inout_ PFILE_OBJECT_CONTEXT    FileObjectContext
+    )
+/*++
+
+Routine Description:
+
+    Increase the disable count.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+    FileObjectContext - the file object context
+
+Return Value:
+    None.
+
+--*/
+{
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+
+    PAGED_CODE();
+
+    if (FileObjectContext)
+    {
+        InterlockedIncrement((PLONG)&(FileObjectContext->McnDisableCount));
+    }
+
+    if (info == NULL)
+    {
+        return;
+    }
+
+    (VOID) KeWaitForMutexObject(&info->MediaChangeMutex,
+                                UserRequest,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+
+    info->MediaChangeDetectionDisableCount++;
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+               "DisableMediaChangeDetection: disable count is %d\n",
+               info->MediaChangeDetectionDisableCount));
+
+    KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+    return;
+} // end DeviceDisableMediaChangeDetection()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceReleaseMcnResources(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine will cleanup any resources allocated for MCN.  It is called
+    by classpnp during remove device, and therefore is not typically required
+    by external drivers.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+    None.
+
+--*/
+{
+    PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
+
+    PAGED_CODE()
+
+    if(info == NULL)
+    {
+        return;
+    }
+
+    if (info->Gesn.Mdl)
+    {
+        PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest);
+        IoFreeMdl(info->Gesn.Mdl);
+        irp->MdlAddress = NULL;
+    }
+    IoFreeIrp(info->MediaChangeSyncIrp);
+    FREE_POOL(info->Gesn.Buffer);
+    FREE_POOL(info->SenseBuffer);
+
+    if (info->DisplayStateCallbackHandle)
+    {
+        PoUnregisterPowerSettingCallback(info->DisplayStateCallbackHandle);
+        info->DisplayStateCallbackHandle = NULL;
+    }
+
+    FREE_POOL(info);
+
+    DeviceExtension->MediaChangeDetectionInfo = NULL;
+
+    return;
+} // end DeviceReleaseMcnResources()
+
+
+IO_COMPLETION_ROUTINE RequestMcnSyncIrpCompletion;
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestMcnSyncIrpCompletion(
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PIRP Irp,
+    _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
+    )
+/*++
+
+Routine Description:
+
+    The MCN work finishes, reset the fields to allow another MCN request
+    be scheduled.
+
+Arguments:
+
+    DeviceObject - device that the completion routine fires on.
+
+    Irp - The irp to be completed.
+
+    Context - IRP context
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION DeviceExtension = NULL;
+    PMEDIA_CHANGE_DETECTION_INFO info = NULL;
+
+    if (Context == NULL)
+    {
+        // this will never happen, but code must be there to prevent OACR warnings.
+        return STATUS_MORE_PROCESSING_REQUIRED;
+    }
+
+    DeviceExtension = (PCDROM_DEVICE_EXTENSION) Context;
+    info = DeviceExtension->MediaChangeDetectionInfo;
+
+#ifndef DEBUG
+    UNREFERENCED_PARAMETER(Irp);
+#endif
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    NT_ASSERT(Irp == info->MediaChangeSyncIrp);
+
+    IoReuseIrp(info->MediaChangeSyncIrp, STATUS_NOT_SUPPORTED);
+
+    // reset the value to let timer routine be able to send the next request.
+    InterlockedCompareExchange((PLONG)&(info->MediaChangeRequestInUse), 0, 1);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+VOID
+RequestSetupMcnSyncIrp(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    setup the MCN synchronization irp.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+    None
+
+--*/
+{
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  irpStack = NULL;
+    PIO_STACK_LOCATION  nextIrpStack = NULL;
+
+    irp = DeviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp;
+    NT_ASSERT(irp != NULL);
+
+    //
+    //  For the driver that creates an IRP, there is no 'current' stack location.
+    //  Step down one IRP stack location so that the extra top one
+    //  becomes our 'current' one.
+    //
+    IoSetNextIrpStackLocation(irp);
+
+    /*
+     *  Cache our device object in the extra top IRP stack location
+     *  so we have it in our completion routine.
+     */
+    irpStack = IoGetCurrentIrpStackLocation(irp);
+    irpStack->DeviceObject = DeviceExtension->DeviceObject;
+
+    //
+    // If the irp is sent down when the volume needs to be
+    // verified, CdRomUpdateGeometryCompletion won't complete
+    // it since it's not associated with a thread.  Marking
+    // it to override the verify causes it always be sent
+    // to the port driver
+    //
+    nextIrpStack = IoGetNextIrpStackLocation(irp);
+
+    SET_FLAG(nextIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
+
+    nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+    // pick up this IOCTL code as it's not normaly seen for CD/DVD drive and does not require input.
+    // set other fields to make this IOCTL recognizable by CDROM.SYS
+    nextIrpStack->Parameters.Others.Argument1 = RequestSetupMcnSyncIrp;
+    nextIrpStack->Parameters.Others.Argument2 = RequestSetupMcnSyncIrp;
+    nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MCN_SYNC_FAKE_IOCTL; //Argument3.
+    nextIrpStack->Parameters.Others.Argument4 = RequestSetupMcnSyncIrp;
+
+    IoSetCompletionRoutine(irp,
+                           RequestMcnSyncIrpCompletion,
+                           DeviceExtension,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    return;
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceMainTimerTickHandler(
+    _In_ WDFTIMER  Timer
+    )
+/*++
+
+Routine Description:
+
+    This routine setup a sync irp and send it to the serial queue.
+    Serial queue will process MCN when receive this sync irp.
+
+Arguments:
+
+    Timer - the timer object that fires.
+
+Return Value:
+    None
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION      deviceExtension = NULL;
+    size_t                       dataLength = 0;
+
+    deviceExtension = WdfObjectGetTypedContext(WdfTimerGetParentObject(Timer), CDROM_DEVICE_EXTENSION);
+
+    (void) RequestHandleEventNotification(deviceExtension, NULL, NULL, &dataLength);
+
+    return;
+} // end DeviceMainTimerTickHandler()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceEnableMainTimer(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine will allocate timer related resources on the first time call.
+    Start the timer.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    if ((DeviceExtension->MediaChangeDetectionInfo == NULL) ||
+        (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE))
+    {
+        // Asynchronous Notification is enabled, timer not needed.
+        return status;
+    }
+
+    if (DeviceExtension->MainTimer == NULL)
+    {
+        //create main timer object.
+        WDF_TIMER_CONFIG        timerConfig;
+        WDF_OBJECT_ATTRIBUTES   timerAttributes;
+
+        WDF_TIMER_CONFIG_INIT(&timerConfig, DeviceMainTimerTickHandler);
+
+        // Polling frequently on virtual optical devices created by Hyper-V will
+        // cause a significant perf / power hit. These devices need to be polled
+        // less frequently for device state changes.
+        if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_MSFT_VIRTUAL_ODD))
+        {
+            timerConfig.Period = 2000; // 2 seconds, in milliseconds.
+        }
+        else
+        {
+            timerConfig.Period = 1000; // 1 second, in milliseconds.
+        }
+
+        timerConfig.TolerableDelay = 500; // 0.5 seconds, in milliseconds
+
+        //Set the autoSerialization to FALSE, as the parent device's
+        //execute level is WdfExecutionLevelPassive.
+        timerConfig.AutomaticSerialization = FALSE;
+
+        WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+        timerAttributes.ParentObject = DeviceExtension->Device;
+        timerAttributes.ExecutionLevel = WdfExecutionLevelInheritFromParent;
+
+        status = WdfTimerCreate(&timerConfig,
+                                &timerAttributes,
+                                &DeviceExtension->MainTimer);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        WdfTimerStart(DeviceExtension->MainTimer,WDF_REL_TIMEOUT_IN_MS(100));
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceEnableMainTimer: Once a second timer enabled  for WDFDEVICE %p\n",
+                   DeviceExtension->Device));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceEnableMainTimer: WDFDEVICE %p, Status %lx  initializing timer\n",
+                   DeviceExtension->Device, status));
+    }
+
+    return status;
+} // end DeviceEnableMainTimer()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceDisableMainTimer(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    stop the timer.
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+    None
+
+--*/
+{
+    PAGED_CODE();
+
+    if ((DeviceExtension->MediaChangeDetectionInfo == NULL) ||
+        (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE))
+    {
+        // Asynchronous Notification is enabled, timer not needed.
+        return;
+    }
+
+    if (DeviceExtension->MainTimer != NULL)
+    {
+        //
+        // we are only going to stop the actual timer in remove device routine.
+        // it is the responsibility of the code within the timer routine to
+        // check if the device is removed and not processing io for the final
+        // call.
+        // this keeps the code clean and prevents lots of bugs.
+        //
+        WdfTimerStop(DeviceExtension->MainTimer,TRUE);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+                   "DeviceDisableMainTimer: Once a second timer disabled for device %p\n",
+                   DeviceExtension->Device));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "DeviceDisableMainTimer: Timer never enabled\n"));
+    }
+
+    return;
+} // end DeviceDisableMainTimer()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleMcnControl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    This routine handles the process of IOCTL_STORAGE_MCN_CONTROL
+
+Arguments:
+
+    DeviceExtension - device context
+
+    Request - request object
+
+    RequestParameters - request parameters
+
+    DataLength - data transferred
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    WDFFILEOBJECT           fileObject = NULL;
+    PFILE_OBJECT_CONTEXT    fileObjectContext = NULL;
+    PPREVENT_MEDIA_REMOVAL  mediaRemoval = NULL;
+
+    PAGED_CODE();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           sizeof(PREVENT_MEDIA_REMOVAL),
+                                           &mediaRemoval,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        fileObject = WdfRequestGetFileObject(Request);
+
+        // Check to make sure we have a file object extension to keep track of this
+        // request.  If not we'll fail it before synchronizing.
+        if (fileObject != NULL)
+        {
+            fileObjectContext = FileObjectGetContext(fileObject);
+        }
+
+        if ((fileObjectContext == NULL) &&
+            (WdfRequestGetRequestorMode(Request) == KernelMode))
+        {
+            fileObjectContext = &DeviceExtension->KernelModeMcnContext;
+        }
+
+        if (fileObjectContext == NULL)
+        {
+            // This handle isn't setup correctly.  We can't let the
+            // operation go.
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (mediaRemoval->PreventMediaRemoval)
+        {
+            // This is a lock command.  Reissue the command in case bus or
+            // device was reset and the lock was cleared.
+            DeviceDisableMediaChangeDetection(DeviceExtension, fileObjectContext);
+        }
+        else
+        {
+            if (fileObjectContext->McnDisableCount == 0)
+            {
+                status = STATUS_INVALID_DEVICE_STATE;
+            }
+            else
+            {
+                DeviceEnableMediaChangeDetection(DeviceExtension, fileObjectContext, TRUE);
+            }
+        }
+    }
+
+    return status;
+} // end RequestHandleMcnControl()
+
+#pragma warning(pop) // un-sets any local warning changes
+
diff --git a/drivers/storage/class/cdrom_new/cdrom.c b/drivers/storage/class/cdrom_new/cdrom.c
new file mode 100644 (file)
index 0000000..ac0c9c6
--- /dev/null
@@ -0,0 +1,4242 @@
+/*--
+
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceEvtIoInCallerContext(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    )
+/*++
+Routine Description:
+
+    Responds to EvtIoInCallerContext events from KMDF
+    It calls different functions to process different type of IOCTLs.
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+Return Value:
+
+    VOID.
+
+--*/
+{
+    NTSTATUS                status  = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(Request);
+    WDF_REQUEST_PARAMETERS  requestParameters;
+
+    requestContext->DeviceExtension = deviceExtension;
+
+    // set the received time
+    RequestSetReceivedTime(Request);
+
+    // get the request parameters
+    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+    WdfRequestGetParameters(Request, &requestParameters);
+
+    if (requestParameters.Type == WdfRequestTypeDeviceControl)
+    {
+        BOOLEAN                         processed = FALSE;
+        PCDROM_DATA                     cdData = &(deviceExtension->DeviceAdditionalData);
+        PMEDIA_CHANGE_DETECTION_INFO    info = deviceExtension->MediaChangeDetectionInfo;
+
+        if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "Receiving IOCTL: %lx\n",
+                        requestParameters.Parameters.DeviceIoControl.IoControlCode));
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                        "Receiving IOCTL: %lx\n",
+                        requestParameters.Parameters.DeviceIoControl.IoControlCode));
+        }
+
+        // If the device is in exclusive mode, check whether the request is from
+        // the handle that locked the device.
+        if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
+        {
+            BOOLEAN isBlocked = FALSE;
+
+            status = RequestIsIoctlBlockedByExclusiveAccess(Request, &isBlocked);
+            UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+
+            if (isBlocked)
+            {
+                if ((requestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EVENT_NOTIFICATION) &&
+                    (info != NULL) && (info->AsynchronousNotificationSupported != FALSE))
+                {
+                    // If AN is supported and we receive a signal but we can't send down GESN
+                    // due to exclusive lock, we should save it and fire a GESN when it's unlocked.
+                    // We just need true/false here and don't need count because we will keep sending
+                    // GESN until we deplete all events.
+                    info->ANSignalPendingDueToExclusiveLock = TRUE;
+                }
+
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                            "Access Denied! Device in exclusive mode.Failing Ioctl %lx\n",
+                            requestParameters.Parameters.DeviceIoControl.IoControlCode));
+                RequestCompletion(deviceExtension, Request, STATUS_ACCESS_DENIED, 0);
+
+                return;
+            }
+        }
+
+        NormalizeIoctl(&requestParameters);
+
+        // 1. All requests that don't need to access device can be processed immediately
+        if (!processed)
+        {
+            processed = RequestDispatchProcessDirectly(Device, Request, requestParameters);
+        }
+
+        // 2. Requests that should be put in sequential queue.
+        if (!processed)
+        {
+            processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
+        }
+
+        // 3. Requests that need to be processed sequentially and in caller's context.
+        if (!processed)
+        {
+            processed = RequestDispatchSyncWithSequentialQueue(Device, Request, requestParameters);
+        }
+
+        // 4. Special requests that needs different process in different cases.
+        if (!processed)
+        {
+            processed = RequestDispatchSpecialIoctls(Device, Request, requestParameters);
+        }
+
+        // 5. This is default behavior for unknown IOCTLs. To pass it to lower level.
+        if (!processed)
+        {
+            processed = RequestDispatchUnknownRequests(Device, Request, requestParameters);
+        }
+
+        // All requests should be processed already.
+        NT_ASSERT(processed);
+        UNREFERENCED_PARAMETER(processed); //defensive coding, avoid PREFAST warning.
+    }
+    else if (requestParameters.Type == WdfRequestTypeDeviceControlInternal)
+    {
+        RequestProcessInternalDeviceControl(Request, deviceExtension);
+    }
+    else
+    {
+        // Requests other than IOCTLs will be forwarded to default queue.
+        status = WdfDeviceEnqueueRequest(Device, Request);
+        if (!NT_SUCCESS(status))
+        {
+            RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+        }
+    }
+
+    return;
+}
+
+
+BOOLEAN
+RequestDispatchProcessDirectly(
+    _In_ WDFDEVICE              Device,
+    _In_ WDFREQUEST             Request,
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    )
+/*++
+Routine Description:
+
+    These requests can be processed in a non-serialized manner, most of them don't need to access device.
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+    RequestParameters - request parameters
+
+Return Value:
+
+    BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    BOOLEAN                 processed = FALSE;
+    size_t                  dataLength = 0;
+
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    ULONG                   ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+    switch (ioctlCode)
+    {
+
+    case IOCTL_CDROM_GET_INQUIRY_DATA:
+    {
+        status = RequestHandleGetInquiryData(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:
+    {
+        status = RequestHandleGetMediaTypeEx(deviceExtension, Request, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
+    {
+        status = RequestHandleMountQueryUniqueId(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
+    {
+        status = RequestHandleMountQueryDeviceName(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
+    {
+        status = RequestHandleMountQuerySuggestedLinkName(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_GET_DEVICE_NUMBER:
+    {
+        status = RequestHandleGetDeviceNumber(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_GET_HOTPLUG_INFO:
+    {
+        status = RequestHandleGetHotPlugInfo(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_SET_HOTPLUG_INFO:
+    {
+        status = RequestHandleSetHotPlugInfo(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_EVENT_NOTIFICATION:
+    {
+        status = RequestHandleEventNotification(deviceExtension, Request, &RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+    case IOCTL_VOLUME_ONLINE:
+    {
+        //
+        // Mount manager and volume manager will
+        // follow this online with a post online
+        // but other callers may not. In those
+        // cases, we process this request right
+        // away. We approximate that these other
+        // callers are from user mode
+        //
+
+        if (WdfRequestGetRequestorMode(Request) == KernelMode)
+        {
+            processed = TRUE;
+        }
+        break;
+    }
+#endif
+
+    default:
+    {
+        processed = FALSE;
+        break;
+    }
+
+    } //end of switch (ioctlCode)
+
+    if (processed)
+    {
+        UCHAR currentStackLocationFlags = 0;
+        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+        if ((status == STATUS_VERIFY_REQUIRED) &&
+            (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+        {
+            // If the status is verified required and this request
+            // should bypass verify required then retry the request.
+            status = STATUS_IO_DEVICE_ERROR;
+            UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+            processed = RequestDispatchProcessDirectly(Device, Request, RequestParameters);
+        }
+        else
+        {
+            // Complete the request after processing it.
+            RequestCompletion(deviceExtension, Request, status, dataLength);
+        }
+    }
+
+    return processed;
+}
+
+
+BOOLEAN
+RequestDispatchToSequentialQueue(
+    _In_ WDFDEVICE              Device,
+    _In_ WDFREQUEST             Request,
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    )
+/*++
+Routine Description:
+
+    These requests can be processed in a non-serialized manner, most of them don't need to access device.
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+    RequestParameters - request parameters
+
+Return Value:
+
+    BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    BOOLEAN                 processed = FALSE;
+    size_t                  dataLength = 0;
+    BOOLEAN                 inZeroPowerState = FALSE;
+
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    ULONG                   ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+    PZERO_POWER_ODD_INFO    zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+
+    if ((zpoddInfo != NULL) &&
+        (zpoddInfo->InZeroPowerState != FALSE))
+    {
+        inZeroPowerState = TRUE;
+    }
+
+    switch (ioctlCode)
+    {
+
+    case IOCTL_CDROM_RAW_READ:
+    {
+        status = RequestValidateRawRead(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+    case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get drive geometryEx\n"));
+        if ( RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+             (ULONG)FIELD_OFFSET(DISK_GEOMETRY_EX, Data))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
+        }
+        else if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+    case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get drive geometry\n"));
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(DISK_GEOMETRY))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(DISK_GEOMETRY);
+        }
+        else if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_READ_TOC_EX:
+    {
+        status = RequestValidateReadTocEx(deviceExtension, Request, RequestParameters, &dataLength);
+
+        if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_READ_TOC:
+    {
+        status = RequestValidateReadToc(deviceExtension, RequestParameters, &dataLength);
+
+        if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_GET_LAST_SESSION:
+    {
+        status = RequestValidateGetLastSession(deviceExtension, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_PLAY_AUDIO_MSF:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Play audio MSF\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(CDROM_PLAY_AUDIO_MSF))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_SEEK_AUDIO_MSF:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Seek audio MSF\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(CDROM_SEEK_AUDIO_MSF))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_PAUSE_AUDIO:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Pause audio\n"));
+
+        status = STATUS_SUCCESS;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_RESUME_AUDIO:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Resume audio\n"));
+
+        status = STATUS_SUCCESS;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_READ_Q_CHANNEL:
+    {
+        status = RequestValidateReadQChannel(Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_GET_VOLUME:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get volume control\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(VOLUME_CONTROL))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(VOLUME_CONTROL);
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_SET_VOLUME:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Set volume control\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(VOLUME_CONTROL))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_STOP_AUDIO:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Stop audio\n"));
+
+        status = STATUS_SUCCESS;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_CHECK_VERIFY:
+    case IOCTL_STORAGE_CHECK_VERIFY2:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] Check Verify\n", Request));
+
+        // Following check will let the condition "OutputBufferLength == 0" pass.
+        // Since it's legacy behavior in classpnp, we need to keep it.
+        if ((RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0) &&
+            (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(ULONG);
+        }
+        else if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_GET_REGION:
+    {
+        // validation will be done when process it.
+        status = STATUS_SUCCESS;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_READ_STRUCTURE:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_READ_STRUCTURE\n", Request));
+
+        status = RequestValidateDvdReadStructure(deviceExtension, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_READ_KEY:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_READ_KEY\n", Request));
+
+        status = RequestValidateDvdReadKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_START_SESSION:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_START_SESSION\n", Request));
+
+        status = RequestValidateDvdStartSession(deviceExtension, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_SEND_KEY:
+    case IOCTL_DVD_SEND_KEY2:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_SEND_KEY\n", Request));
+
+        status = RequestValidateDvdSendKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_SET_READ_AHEAD:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] SetReadAhead\n", Request));
+
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(STORAGE_SET_READ_AHEAD))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_IS_WRITABLE:
+    {
+        status = STATUS_SUCCESS;
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_DRIVE_LAYOUT:
+    {
+        ULONG requiredSize = 0;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get drive layout\n"));
+
+        requiredSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            requiredSize)
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = requiredSize;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
+    {
+        ULONG requiredSize = 0;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get drive layoutEx\n"));
+
+        requiredSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            requiredSize)
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = requiredSize;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_PARTITION_INFO:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get Partition Info\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(PARTITION_INFORMATION))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(PARTITION_INFORMATION);
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_PARTITION_INFO_EX:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Get Partition InfoEx\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(PARTITION_INFORMATION_EX))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(PARTITION_INFORMATION_EX);
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_VERIFY:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: IOCTL_DISK_VERIFY to device %p through request %p\n",
+                    Device,
+                    Request));
+
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(VERIFY_INFORMATION))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DISK_GET_LENGTH_INFO:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: Disk Get Length InfoEx\n"));
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(GET_LENGTH_INFORMATION))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            dataLength = sizeof(GET_LENGTH_INFORMATION);
+        }
+        else if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_GET_CONFIGURATION:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_GET_CONFIGURATION\n", Request));
+
+        status = RequestValidateGetConfiguration(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_SET_SPEED:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_SET_SPEED\n", Request));
+
+        status = RequestValidateSetSpeed(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_DVD_END_SESSION:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_DVD_END_SESSION\n", Request));
+
+        status = RequestValidateDvdEndSession(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_END_SESSION:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_AACS_END_SESSION\n", Request));
+
+        status = RequestValidateAacsEndSession(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_READ_MEDIA_KEY_BLOCK:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Querying full MKB with bufferSize of %x bytes\n",
+                    (int)RequestParameters.Parameters.DeviceIoControl.OutputBufferLength
+                    ));
+
+        status = RequestValidateAacsReadMediaKeyBlock(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_START_SESSION:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Requesting AGID\n"
+                    ));
+
+        status = RequestValidateAacsStartSession(deviceExtension, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_SEND_CERTIFICATE:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Sending host certificate to drive\n"
+                    ));
+
+        status = RequestValidateAacsSendCertificate(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_GET_CERTIFICATE:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Querying drive certificate\n"
+                    ));
+
+        status = RequestValidateAacsGetCertificate(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_GET_CHALLENGE_KEY:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Querying drive challenge key\n"
+                    ));
+
+        status = RequestValidateAacsGetChallengeKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_SEND_CHALLENGE_KEY:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Sending drive challenge key\n"
+                    ));
+
+        status = RequestValidateAacsSendChallengeKey(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_READ_VOLUME_ID:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Reading volume ID\n"
+                    ));
+
+        status = RequestValidateAacsReadVolumeId(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_READ_SERIAL_NUMBER:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Reading Serial Number\n"
+                    ));
+
+        status = RequestValidateAacsReadSerialNumber(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_READ_MEDIA_ID:
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "AACS: Reading media ID\n"
+                    ));
+
+        status = RequestValidateAacsReadMediaId(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_AACS_READ_BINDING_NONCE:
+    case IOCTL_AACS_GENERATE_BINDING_NONCE:
+    {
+        if (ioctlCode == IOCTL_AACS_GENERATE_BINDING_NONCE)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "AACS: Generating new binding nonce\n"
+                        ));
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "AACS: Reading existing binding nonce\n"
+                        ));
+        }
+
+        status = RequestValidateAacsBindingNonce(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_ENABLE_STREAMING:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_ENABLE_STREAMING\n", Request));
+
+        status = RequestValidateEnableStreaming(Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_SEND_OPC_INFORMATION:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_SEND_OPC_INFORMATION\n", Request));
+
+        status = RequestValidateSendOpcInformation(Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_CDROM_GET_PERFORMANCE:
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "RequestDispatchToSequentialQueue: [%p] IOCTL_CDROM_GET_PERFORMANCE\n", Request));
+
+        status = RequestValidateGetPerformance(Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_MEDIA_REMOVAL:
+    case IOCTL_STORAGE_EJECTION_CONTROL:
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+           sizeof(PREVENT_MEDIA_REMOVAL))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_MCN_CONTROL:
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(PREVENT_MEDIA_REMOVAL))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_RESERVE:
+    case IOCTL_STORAGE_RELEASE:
+    {
+        // there is no validate check currently.
+        status = STATUS_SUCCESS;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
+    case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT:
+    {
+        status = RequestValidatePersistentReserve(deviceExtension, Request, RequestParameters, &dataLength);
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_EJECT_MEDIA:
+    case IOCTL_STORAGE_LOAD_MEDIA:
+    case IOCTL_STORAGE_LOAD_MEDIA2:
+    {
+        status = STATUS_SUCCESS;
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_FIND_NEW_DEVICES:
+    {
+        // process it.
+        IoInvalidateDeviceRelations(deviceExtension->LowerPdo, BusRelations);
+
+        status = STATUS_SUCCESS;
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_READ_CAPACITY:
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_READ_CAPACITY))
+        {
+            dataLength = sizeof(STORAGE_READ_CAPACITY);
+            status = STATUS_BUFFER_TOO_SMALL;
+        }
+        else if (inZeroPowerState != FALSE)
+        {
+            status = STATUS_NO_MEDIA_IN_DEVICE;
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    case IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT:
+    {
+        // for disk.sys only in original classpnp
+        status = STATUS_NOT_SUPPORTED;
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+    case IOCTL_DISK_ARE_VOLUMES_READY:
+    {
+        // this request doesn't access device at all, so seemingly it can be processed
+        // directly; however, in case volume online is not received, we will need to
+        // park these requests in a queue, and the only way a request can be queued is
+        // if the request came out of another queue.
+        status = STATUS_SUCCESS;
+
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_VOLUME_ONLINE:
+    case IOCTL_VOLUME_POST_ONLINE:
+    {
+        status = STATUS_SUCCESS;
+
+        processed = TRUE;
+        break;
+    }
+#endif
+
+    default:
+    {
+        processed = FALSE;
+        break;
+    }
+    } //end of switch (ioctlCode)
+
+    if (processed)
+    {
+        UCHAR currentStackLocationFlags = 0;
+        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+        if ((status == STATUS_VERIFY_REQUIRED) &&
+            (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+        {
+            // If the status is verified required and this request
+            // should bypass verify required then retry the request.
+            status = STATUS_IO_DEVICE_ERROR;
+            UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+            processed = RequestDispatchToSequentialQueue(Device, Request, RequestParameters);
+        }
+        else
+        {
+            if (NT_SUCCESS(status))
+            {
+                // Forward the request to serialized queue.
+                status = WdfDeviceEnqueueRequest(Device, Request);
+            }
+
+            if (!NT_SUCCESS(status))
+            {
+                // Validation failed / forward failed, complete the request.
+                RequestCompletion(deviceExtension, Request, status, dataLength);
+            }
+        }
+    }
+
+    return processed;
+}
+
+
+BOOLEAN
+RequestDispatchSyncWithSequentialQueue(
+    _In_ WDFDEVICE              Device,
+    _In_ WDFREQUEST             Request,
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    )
+/*++
+Routine Description:
+
+    These requests need to stay in caller's context and be processed in serialized manner.
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+    RequestParameters - request parameters
+
+Return Value:
+
+    BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    BOOLEAN                 processed = FALSE;
+    size_t                  dataLength = 0;
+
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    ULONG                   ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+    switch (ioctlCode)
+    {
+
+    case IOCTL_CDROM_EXCLUSIVE_ACCESS:
+    {
+        //1. Validate
+        status =  RequestValidateExclusiveAccess(Request, RequestParameters, &dataLength);
+
+        //2. keep the request in serialized manner and stay in user's context.
+        if (NT_SUCCESS(status))
+        {
+            PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
+
+            status = WdfRequestRetrieveInputBuffer(Request,
+                                                   RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                                   &exclusiveAccess,
+                                                   NULL);
+
+            if (NT_SUCCESS(status))
+            {
+                // do not need to check "status" as it passed validation and cannot fail in WdfRequestRetrieveInputBuffer()
+                switch (exclusiveAccess->RequestType)
+                {
+
+                    case ExclusiveAccessQueryState:
+                    {
+                        status = RequestSetContextFields(Request, RequestHandleExclusiveAccessQueryLockState);
+                        break;
+                    }
+
+                    case ExclusiveAccessLockDevice:
+                    {
+                        status = RequestSetContextFields(Request, RequestHandleExclusiveAccessLockDevice);
+                        break;
+                    }
+
+                    case ExclusiveAccessUnlockDevice:
+                    {
+                        status = RequestSetContextFields(Request, RequestHandleExclusiveAccessUnlockDevice);
+                        break;
+                    }
+                    default:
+                    {
+                        // already valicated in RequestValidateExclusiveAccess()
+                        NT_ASSERT(FALSE);
+                        break;
+                    }
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // now, put the special synchronization information into the context
+                status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+
+                // "status" is used for debugging in above statement, reset to success to avoid further work in this function.
+                status = STATUS_SUCCESS;
+            }
+        }
+
+        processed = TRUE;
+        break; // complete the irp
+    }
+
+    default:
+    {
+        processed = FALSE;
+        break;
+    }
+    } //end of switch (ioctlCode)
+
+    // Following process is only valid if the request is not really processed. (failed in validation)
+    if (processed && !NT_SUCCESS(status))
+    {
+        UCHAR currentStackLocationFlags = 0;
+        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+        if ((status == STATUS_VERIFY_REQUIRED) &&
+            (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+        {
+            //
+            // If the status is verified required and this request
+            // should bypass verify required then retry the request.
+            //
+            status = STATUS_IO_DEVICE_ERROR;
+            UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+            processed = RequestDispatchSyncWithSequentialQueue(Device, Request, RequestParameters);
+        }
+        else
+        {
+            // Validation failed / forward failed, complete the request.
+            RequestCompletion(deviceExtension, Request, status, dataLength);
+        }
+    }
+
+    return processed;
+}
+
+
+BOOLEAN
+RequestDispatchSpecialIoctls(
+    _In_ WDFDEVICE              Device,
+    _In_ WDFREQUEST             Request,
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    )
+/*++
+Routine Description:
+
+    These requests need to be processed in different manner according to input parameters
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+    RequestParameters - request parameters
+
+Return Value:
+
+    BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    BOOLEAN                 processed = FALSE;
+    size_t                  dataLength = 0;
+    BOOLEAN                 requestCompleted = FALSE;
+
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA             cdData = &(deviceExtension->DeviceAdditionalData);
+    ULONG                   ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+    switch (ioctlCode)
+    {
+    case IOCTL_SCSI_PASS_THROUGH:
+    case IOCTL_SCSI_PASS_THROUGH_DIRECT:
+    case IOCTL_SCSI_PASS_THROUGH_EX:
+    case IOCTL_SCSI_PASS_THROUGH_DIRECT_EX:
+    {
+        // SPTI is considered special case as we need to set the MinorFunction before pass to low level.
+
+#if defined (_WIN64)
+        if (WdfRequestIsFrom32BitProcess(Request))
+        {
+            if ((ioctlCode == IOCTL_SCSI_PASS_THROUGH) || (ioctlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT))
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+            else
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32_EX))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+        }
+        else
+#endif
+        {
+            if ((ioctlCode == IOCTL_SCSI_PASS_THROUGH) || (ioctlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT))
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+            else
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH_EX))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+        }
+
+        if (!NT_SUCCESS(status))
+        {
+            // validation failed.
+            RequestCompletion(deviceExtension, Request, status, dataLength);
+        }
+        else
+        {
+            // keep the request in serialized manner and stay in user's context.
+            status = RequestSetContextFields(Request, RequestHandleScsiPassThrough);
+
+            if (NT_SUCCESS(status))
+            {
+                status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+            }
+            else
+            {
+                RequestCompletion(deviceExtension, Request, status, 0);
+            }
+        }
+
+        requestCompleted = TRUE;
+        processed = TRUE;
+        break;
+    }
+
+    case IOCTL_STORAGE_QUERY_PROPERTY:
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else
+        {
+            PSTORAGE_PROPERTY_QUERY inputBuffer = NULL;
+
+            status = WdfRequestRetrieveInputBuffer(Request,
+                                                   RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                                   &inputBuffer,
+                                                   NULL);
+
+            if (NT_SUCCESS(status))
+            {
+                if (!EXCLUSIVE_MODE(cdData) ||                                     // not locked
+                    EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)) ||   // request is from lock owner
+                    (inputBuffer->QueryType == PropertyExistsQuery))               // request not access device
+                {
+                    if (inputBuffer->PropertyId == StorageDeviceUniqueIdProperty)
+                    {
+                        // previously handled in classpnp
+                        // keep the request in serialized manner and stay in user's context.
+                        status = RequestSetContextFields(Request, RequestHandleQueryPropertyDeviceUniqueId);
+
+                        if (NT_SUCCESS(status))
+                        {
+                            status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+                            // remeber that the request has been completed.
+                            requestCompleted = TRUE;
+                        }
+                    }
+                    else if (inputBuffer->PropertyId == StorageDeviceWriteCacheProperty)
+                    {
+                        // previously handled in classpnp
+                        // keep the request in serialized manner and stay in user's context.
+                        status = RequestSetContextFields(Request, RequestHandleQueryPropertyWriteCache);
+
+                        if (NT_SUCCESS(status))
+                        {
+                            status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+                            // remeber that the request has been completed.
+                            requestCompleted = TRUE;
+                        }
+                    }
+                    else
+                    {
+                        // Pass to port driver for handling
+                        RequestDispatchUnknownRequests(Device, Request, RequestParameters);
+
+                        // remeber that the request has been completed.
+                        requestCompleted = TRUE;
+                    }
+                }
+                else
+                {
+                    // If cached data exists, return cached data. Otherwise, fail the request.
+                    if ((inputBuffer->QueryType == PropertyStandardQuery) &&
+                        ((inputBuffer->PropertyId == StorageDeviceProperty) || (inputBuffer->PropertyId == StorageAdapterProperty)) )
+                    {
+                        status = RequestHandleQueryPropertyRetrieveCachedData(deviceExtension, Request, RequestParameters, &dataLength);
+                    }
+                    else
+                    {
+                        status = STATUS_ACCESS_DENIED;
+                    }
+                }
+            }
+        }
+
+        processed = TRUE;
+        break;
+    }
+
+    // this IOCTL is a fake one, used for MCN process sync-ed with serial queue.
+    case IOCTL_MCN_SYNC_FAKE_IOCTL:
+    {
+        PIRP irp = WdfRequestWdmGetIrp(Request);
+
+        if ((deviceExtension->MediaChangeDetectionInfo != NULL) &&
+            (irp == deviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp) &&
+            (WdfRequestGetRequestorMode(Request) == KernelMode) &&
+            (RequestParameters.Parameters.Others.Arg1 == RequestSetupMcnSyncIrp) &&
+            (RequestParameters.Parameters.Others.Arg2 == RequestSetupMcnSyncIrp) &&
+            (RequestParameters.Parameters.Others.Arg4 == RequestSetupMcnSyncIrp))
+        {
+            // This is the requset we use to sync Media Change Detection with sequential queue.
+            status = WdfDeviceEnqueueRequest(Device, Request);
+
+            if (!NT_SUCCESS(status))
+            {
+                RequestCompletion(deviceExtension, Request, status, dataLength);
+            }
+
+            requestCompleted = TRUE;
+            processed = TRUE;
+        }
+        else
+        {
+            // process as an unknown request.
+            processed = FALSE;
+        }
+        break;
+    }
+
+    default:
+    {
+        processed = FALSE;
+        break;
+    }
+    } //end of switch (ioctlCode)
+
+    if (processed && !requestCompleted)
+    {
+        UCHAR currentStackLocationFlags = 0;
+        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+        if ((status == STATUS_VERIFY_REQUIRED) &&
+            (currentStackLocationFlags & SL_OVERRIDE_VERIFY_VOLUME))
+        {
+            // If the status is verified required and this request
+            // should bypass verify required then retry the request.
+            status = STATUS_IO_DEVICE_ERROR;
+            UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+            processed = RequestDispatchSpecialIoctls(Device, Request, RequestParameters);
+        }
+        else
+        {
+            RequestCompletion(deviceExtension, Request, status, dataLength);
+        }
+    }
+
+    return processed;
+}
+
+
+BOOLEAN
+RequestDispatchUnknownRequests(
+    _In_ WDFDEVICE              Device,
+    _In_ WDFREQUEST             Request,
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    )
+/*++
+Routine Description:
+
+    All unknown requests will be pass to lower level driver.
+    If IRQL is PASSIVE_LEVEL, the request will be serialized;
+    Otherwise, it'll be sent and forget.
+
+Arguments:
+
+    Device - handle to a WDF Device object
+
+    Request - handle to the incoming WDF Request object
+
+    RequestParameters - request parameters
+
+Return Value:
+
+    BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).
+
+--*/
+{
+    NTSTATUS                status = STATUS_UNSUCCESSFUL;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+
+    ULONG       baseCode = DEVICE_TYPE_FROM_CTL_CODE(RequestParameters.Parameters.DeviceIoControl.IoControlCode);
+
+    if ((KeGetCurrentIrql() != PASSIVE_LEVEL) ||
+        (baseCode == FILE_DEVICE_ACPI))
+    {
+        // 1. When IRQL is higher than PASSIVE_LEVEL,
+        // 2. ataport sends IOCTL_ACPI_ASYNC_EVAL_METHOD before queue starts,
+        // send request directly to lower driver.
+        status = RequestHandleUnknownIoctl(Device, Request);
+    }
+    else
+    {
+        // keep the request in serialized manner and stay in user's context.
+        status = RequestSetContextFields(Request, RequestHandleUnknownIoctl);
+
+        if (NT_SUCCESS(status))
+        {
+            status = RequestSynchronizeProcessWithSerialQueue(Device, Request);
+        }
+        else
+        {
+            RequestCompletion(deviceExtension, Request, status, 0);
+        }
+    }
+
+    UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+
+    // All unknown IOCTLs are processed in this function.
+    return TRUE; //processed
+}
+
+VOID
+RequestProcessInternalDeviceControl(
+    _In_ WDFREQUEST              Request,
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+Routine Description:
+
+    all internal IOCTL will be send to lower driver asynchronously.
+
+Arguments:
+
+    Request - handle to the incoming WDF Request object
+    DeviceExtension - device extension structure
+
+Return Value:
+
+    None
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PIRP                     irp = NULL;
+    PIO_STACK_LOCATION       irpStack = NULL;
+    PIO_STACK_LOCATION       nextStack = NULL;
+    BOOLEAN                  requestSent = FALSE;
+
+    irp = WdfRequestWdmGetIrp(Request);
+    irpStack = IoGetCurrentIrpStackLocation(irp);
+    nextStack = IoGetNextIrpStackLocation(irp);
+
+    // Set the parameters in the next stack location.
+    nextStack->Parameters.Scsi.Srb = irpStack->Parameters.Scsi.Srb;
+    nextStack->MajorFunction = IRP_MJ_SCSI;
+    nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
+
+    WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL);
+
+    status = RequestSend(DeviceExtension,
+                         Request,
+                         DeviceExtension->IoTarget,
+                         0,
+                         &requestSent);
+
+    // send the request straight down (asynchronously)
+    if (!requestSent)
+    {
+        // fail the request
+        RequestCompletion(DeviceExtension, Request, status, WdfRequestGetInformation(Request));
+    }
+
+    return;
+}
+
+
+
+//
+// Serial I/O Queue Event callbacks
+//
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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);
+    }
+}
+
diff --git a/drivers/storage/class/cdrom_new/cdrom.h b/drivers/storage/class/cdrom_new/cdrom.h
new file mode 100644 (file)
index 0000000..f9c0f43
--- /dev/null
@@ -0,0 +1,1624 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    cdrom.h
+
+Abstract:
+
+    Main header file for cdrom.sys.
+    This contains structure and function declarations as well as constant values.
+
+Author:
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __CDROM_H__
+#define __CDROM_H__
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+
+#include "wdf.h"
+#include "ntddmmc.h"
+#include "ntddcdvd.h"
+#include "ntddcdrm.h"
+#include "ntdddisk.h"
+#include "ntddtape.h"
+#include "ntddscsi.h"
+#include "ntddvol.h"
+#include "specstrings.h"
+#include "cdromp.h"
+
+// Set component ID for DbgPrintEx calls
+#ifndef DEBUG_COMP_ID
+    #define DEBUG_COMP_ID   DPFLTR_CDROM_ID
+#endif
+
+// Include initguid.h so GUID_CONSOLE_DISPLAY_STATE is declared
+#include <initguid.h>
+
+// Include header file and setup GUID for tracing
+#include <storswtr.h>
+#define WPP_GUID_CDROM      (A4196372, C3C4, 42d5, 87BF, 7EDB2E9BCC27)
+#ifndef WPP_CONTROL_GUIDS
+    #define WPP_CONTROL_GUIDS   WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_CDROM)
+#endif
+
+#ifdef __REACTOS__
+#include <pseh/pseh2.h>
+#endif
+
+#ifdef __REACTOS__
+#undef MdlMappingNoExecute
+#define MdlMappingNoExecute 0
+#define NonPagedPoolNx NonPagedPool
+#define NonPagedPoolNxCacheAligned NonPagedPoolCacheAligned
+#undef POOL_NX_ALLOCATION
+#define POOL_NX_ALLOCATION 0
+#endif
+
+// This prototype is needed because, although NTIFS.H is now shipping with
+// the WDK, can't include both it and the other headers we already use.
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTKERNELAPI
+BOOLEAN
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+PsIsThreadTerminating(
+    _In_ PETHREAD Thread
+    );
+
+//
+//
+extern CDROM_SCAN_FOR_SPECIAL_INFO CdromHackItems[];
+
+#define CDROM_HACK_DEC_RRD                 (0x00000001)
+#define CDROM_HACK_FUJITSU_FMCD_10x        (0x00000002)
+//#define CDROM_HACK_HITACHI_1750            (0x00000004) -- obsolete
+#define CDROM_HACK_HITACHI_GD_2000         (0x00000008)
+#define CDROM_HACK_TOSHIBA_SD_W1101        (0x00000010)
+//#define CDROM_HACK_TOSHIBA_XM_3xx          (0x00000020) -- obsolete
+//#define CDROM_HACK_NEC_CDDA                (0x00000040) -- obsolete
+//#define CDROM_HACK_PLEXTOR_CDDA            (0x00000080) -- obsolete
+#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT  (0x00000100)
+//#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200) -- obsolete
+//#define CDROM_HACK_READ_CD_SUPPORTED       (0x00000400) -- obsolete
+#define CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG (0x00000800)
+#define CDROM_HACK_BAD_VENDOR_PROFILES     (0x00001000)
+#define CDROM_HACK_MSFT_VIRTUAL_ODD        (0x00002000)
+#define CDROM_HACK_LOCKED_PAGES            (0x80000000) // not a valid flag to save
+
+#define CDROM_HACK_VALID_FLAGS             (0x00003fff)
+#define CDROM_HACK_INVALID_FLAGS           (~CDROM_HACK_VALID_FLAGS)
+
+
+// A 64k buffer to be written takes the following amount of time:
+//   1x    CD ==     75 sectors/sec == 0.4266667 seconds == 4266667 100ns units
+//   4x    CD ==    300 sectors/sec == 0.1066667 seconds == 1066667 100ns units
+//  10x    CD ==    300 sectors/sec == 0.0426667 seconds ==  426667 100ns units
+//   1x   DVD ==    676 sectors/sec == 0.0473373 seconds ==  473373 100ns units
+//  16x   DVD == 10,816 sectors/sec == 0.0029586 seconds ==   29586 100ns units
+//   1x HDDVD ==  2,230 sectors/sec == 0.0143498 seconds ==  143498 100ns units
+#define WRITE_RETRY_DELAY_CD_1x    ((LONGLONG)4266667)
+#define WRITE_RETRY_DELAY_CD_4x    ((LONGLONG)1066667)
+#define WRITE_RETRY_DELAY_CD_10x   ((LONGLONG) 426667)
+#define WRITE_RETRY_DELAY_DVD_1x   ((LONGLONG) 473373)
+#define WRITE_RETRY_DELAY_DVD_4x   ((LONGLONG) 118343)
+#define WRITE_RETRY_DELAY_DVD_16x  ((LONGLONG)  29586)
+#define WRITE_RETRY_DELAY_HDDVD_1x ((LONGLONG) 143498)
+
+//
+#define MAXIMUM_RETRIES 4
+
+#define CDROM_GET_CONFIGURATION_TIMEOUT     (0x4)
+#define CDROM_READ_DISC_INFORMATION_TIMEOUT (0x4)
+#define CDROM_TEST_UNIT_READY_TIMEOUT       (0x14)
+#define CDROM_GET_PERFORMANCE_TIMEOUT       (0x14)
+#define CDROM_READ_CAPACITY_TIMEOUT         (0x14)
+
+#define START_UNIT_TIMEOUT  (60 * 4)
+
+// Used to detect the loss of the autorun irp.
+#define MEDIA_CHANGE_TIMEOUT_TIME  300
+
+// Indicates whether is is safe to send StartUnit commands
+// to this device. It will only be off for some removeable devices.
+#define DEV_SAFE_START_UNIT 0x00000004
+
+// Indicates that the device is connected to a backup power supply
+// and hence write-through and synch cache requests may be ignored
+#define DEV_POWER_PROTECTED 0x00000010
+
+// The following CDROM_SPECIAL_ flags are set in ScanForSpecialFlags
+// in the Device Extension
+
+// Never Spin Up/Down the drive (may not handle properly)
+#define CDROM_SPECIAL_DISABLE_SPIN_DOWN                 0x00000001
+//#define CDROM_SPECIAL_DISABLE_SPIN_UP                   0x00000002
+
+// Don't bother to lock the queue when powering down
+// (used mostly to send a quick stop to a cdrom to abort audio playback)
+//#define CDROM_SPECIAL_NO_QUEUE_LOCK                     0x00000008
+
+// Disable write cache due to known bugs
+#define CDROM_SPECIAL_DISABLE_WRITE_CACHE               0x00000010
+
+// Used to indicate that this request shouldn't invoke any power type operations
+// like spinning up the drive.
+
+#define SRB_CLASS_FLAGS_LOW_PRIORITY      0x10000000
+
+// Used to indicate that an SRB is the result of a paging operation.
+#define SRB_CLASS_FLAGS_PAGING            0x40000000
+
+typedef struct _ERROR_RECOVERY_DATA {
+    MODE_PARAMETER_HEADER   Header;
+    MODE_PARAMETER_BLOCK    BlockDescriptor;
+    MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
+} ERROR_RECOVERY_DATA, *PERROR_RECOVERY_DATA;
+
+// A compile-time check of the 30,000 limit not overflowing ULONG size...
+// Note that it is not expected that a release (FRE) driver will normally
+// have such a large history, instead using the compression function.
+#define CDROM_INTERPRET_SENSE_INFO2_MAXIMUM_HISTORY_COUNT   30000
+C_ASSERT( (MAXULONG - sizeof(SRB_HISTORY)) / 30000 >= sizeof(SRB_HISTORY_ITEM) );
+
+// Intended to reuse a defined IOCTL code that not seen in Optical stack and does not require input parameter.
+// This fake IOCTL is used used for MCN process sync-ed with serial queue.
+#define IOCTL_MCN_SYNC_FAKE_IOCTL    IOCTL_DISK_UPDATE_DRIVE_SIZE
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+PCDROM_ERROR_HANDLER()
+
+Routine Description:
+
+    This routine is a callback into the driver to handle errors.  The queue
+    shall not be unfrozen when this error handler is called, even though the
+    SRB flags may mark the queue as having been frozen due to this SRB.
+
+Irql:
+
+    This routine will be called at KIRQL <= DISPATCH_LEVEL
+
+Arguments:
+
+    DeviceObject is the device object the error occurred on.
+
+    Srb is the Srb that was being processed when the error occurred.
+
+    Status may be overwritten by the routine if it decides that the error
+        was benign, or otherwise wishes to change the returned status code
+        for this command
+
+    Retry may be overwritten to specify that this command should or should
+        not be retried (if the callee supports retrying commands)
+
+Return Value:
+
+    status
+
+--*/
+struct _CDROM_DEVICE_EXTENSION;     // *PCDROM_DEVICE_EXTENSION;
+typedef struct _CDROM_DEVICE_EXTENSION
+                CDROM_DEVICE_EXTENSION,
+                *PCDROM_DEVICE_EXTENSION;
+
+typedef
+VOID
+(*PCDROM_ERROR_HANDLER) (
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb,
+    _Inout_ PNTSTATUS             Status,
+    _Inout_ PBOOLEAN              Retry
+    );
+
+// CdRom driver extension
+typedef struct _CDROM_DRIVER_EXTENSION {
+    ULONG               Version;
+    PDRIVER_OBJECT      DriverObject;
+    ULONG               Flags;
+
+} CDROM_DRIVER_EXTENSION, *PCDROM_DRIVER_EXTENSION;
+
+#define CDROM_FLAG_WINPE_MODE   0x00000001
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CDROM_DRIVER_EXTENSION, DriverGetExtension)
+
+#define CdromMmcUpdateComplete  0
+#define CdromMmcUpdateRequired  1
+#define CdromMmcUpdateStarted   2
+
+typedef struct _CDROM_MMC_EXTENSION {
+
+    BOOLEAN         IsMmc;          // mmc device
+    BOOLEAN         IsAACS;         // aacs compatible device
+    BOOLEAN         IsWriter;       // the drive is a writer or not
+    BOOLEAN         WriteAllowed;   // currently allow write request or not
+
+    BOOLEAN         IsCssDvd;       // A CSS protected DVD or CPPM-protected DVDAudio media in Drive.
+    BOOLEAN         StreamingReadSupported;   // the drive supports streaming for reads
+    BOOLEAN         StreamingWriteSupported;  // the drive supports streaming for writes
+
+    LONG            UpdateState;
+
+    // The feature number defines the level and form of
+    // validation that needs to be performed on Io requests
+    FEATURE_NUMBER  ValidationSchema;
+    ULONG           Blocking;
+
+    SCSI_REQUEST_BLOCK          CapabilitiesSrb;
+    PIRP                        CapabilitiesIrp;
+    WDFREQUEST                  CapabilitiesRequest;
+    SENSE_DATA                  CapabilitiesSenseData;
+    _Field_size_bytes_(CapabilitiesBufferSize)
+    PGET_CONFIGURATION_HEADER   CapabilitiesBuffer;
+    ULONG                       CapabilitiesBufferSize;
+    PMDL                        CapabilitiesMdl;
+
+    BOOLEAN                     ReadCdC2Pointers;
+    BOOLEAN                     ReadCdSubCode;
+
+} CDROM_MMC_EXTENSION, *PCDROM_MMC_EXTENSION;
+
+typedef struct _CDROM_SCRATCH_READ_WRITE_CONTEXT {
+
+    // Information about the data that we need to read/write
+    ULONG           PacketsCount;
+    ULONG           TransferedBytes;
+    ULONG           EntireXferLen;
+    ULONG           MaxLength;
+    PUCHAR          DataBuffer;
+    LARGE_INTEGER   StartingOffset;
+    BOOLEAN         IsRead;
+
+    // A pointer to the SRB history item to be filled upon completion
+    PSRB_HISTORY_ITEM   SrbHistoryItem;
+
+} CDROM_SCRATCH_READ_WRITE_CONTEXT, *PCDROM_SCRATCH_READ_WRITE_CONTEXT;
+
+// Many commands get double-buffered.  Since the max
+// transfer size is typically 64k, most of these requests
+// can be handled with a single pre-allocated buffer.
+typedef struct _CDROM_SCRATCH_CONTEXT {
+
+    _Field_range_(4*1024, 64*1024)       ULONG   ScratchBufferSize; // 0x1000..0x10000 (4k..64k)
+    _Field_size_bytes_(ScratchBufferSize)    PVOID   ScratchBuffer;     // used to get data for clients
+
+    PMDL                         ScratchBufferMdl;  // used to get data for clients
+
+    WDFREQUEST                   ScratchRequest;
+    PSCSI_REQUEST_BLOCK          ScratchSrb;
+    PSENSE_DATA                  ScratchSense;
+    PSRB_HISTORY                 ScratchHistory;
+
+    // This MDL is used to performed the request whose required transfer size is bigger than adaptor's max.
+    PMDL                         PartialMdl;
+    BOOLEAN                      PartialMdlIsBuilt;
+
+    // For debugging, set/clear this field when using the scratch buffer/request
+    PVOID                        ScratchInUse;
+    PCSTR                        ScratchInUseFileName;
+    ULONG                        ScratchInUseLineNumber;
+
+    // Stuff for asynchronous retrying of the transfer.
+    ULONG                        NumRetries;
+
+    // Read Write context
+    CDROM_SCRATCH_READ_WRITE_CONTEXT ScratchReadWriteContext;
+
+} CDROM_SCRATCH_CONTEXT, *PCDROM_SCRATCH_CONTEXT;
+
+// Context structure for the IOCTL work item
+typedef struct _CDROM_IOCTL_CONTEXT {
+
+    WDFREQUEST OriginalRequest;
+
+} CDROM_IOCTL_CONTEXT, *PCDROM_IOCTL_CONTEXT;
+
+// Context structure for the read/write work item
+typedef struct _CDROM_READ_WRITE_CONTEXT {
+
+    WDFREQUEST OriginalRequest;
+
+} CDROM_READ_WRITE_CONTEXT, *PCDROM_READ_WRITE_CONTEXT;
+
+typedef struct _CDROM_DATA {
+
+    CDROM_MMC_EXTENSION     Mmc;
+
+    // hack flags for ScanForSpecial routines
+    ULONG_PTR               HackFlags;
+
+    // the error handling routines need to be per-device, not per-driver....
+    PCDROM_ERROR_HANDLER    ErrorHandler;
+
+    // Indicates whether an audio play operation is currently being performed.
+    // Only thing this does is prevent reads and toc requests while playing audio.
+    BOOLEAN                 PlayActive;
+
+    // indicate we need to pick a default dvd region for the user if we can
+    ULONG                   PickDvdRegion;
+
+    // The well known name link for this device.
+    UNICODE_STRING          WellKnownName;
+
+    // We need to distinguish between the two...
+    ULONG                   MaxPageAlignedTransferBytes;
+    ULONG                   MaxUnalignedTransferBytes;
+
+    // Indicates that this is a DEC RRD cdrom.
+    // This drive requires software to fix responses from the faulty firmware
+    BOOLEAN                 IsDecRrd;
+
+    // Storage for the error recovery page. This is used as the method
+    // to switch block sizes for some drive-specific error recovery routines.
+    // ERROR_RECOVERY_DATA     recoveryData;    //obsolete along with error process for TOSHIBA_XM_3xx
+
+    // Indicates that the device is in exclusive mode and only
+    // the requests from the exclusive owner will be processed.
+    WDFFILEOBJECT           ExclusiveOwner;
+
+    // Caller name of the owner, if the device is in exclusive mode.
+    UCHAR                   CallerName[CDROM_EXCLUSIVE_CALLER_LENGTH];
+
+    // Indicates that the device speed should be set to
+    // default value on the next media change.
+    BOOLEAN                 RestoreDefaults;
+
+    // How long to wait between retries if a READ/WRITE irp
+    // gets a LWIP (2/4/7, 2/4/8)?
+    LONGLONG                ReadWriteRetryDelay100nsUnits;
+
+    // Cached Device Type information. Maybe FILE_DEVICE_CD_ROM or FILE_DEVICE_DVD
+    // CommonExtension.DevInfo->DeviceType maybe FILE_DEVICE_CD_ROM when this field is FILE_DEVICE_DVD
+    DEVICE_TYPE             DriveDeviceType;
+
+    _Field_size_bytes_(CachedInquiryDataByteCount)
+        PINQUIRYDATA        CachedInquiryData;
+    ULONG                   CachedInquiryDataByteCount;
+
+} CDROM_DATA, *PCDROM_DATA;
+
+
+typedef struct _CDROM_POWER_OPTIONS {
+    ULONG   PowerDown              :  1;
+    ULONG   LockQueue              :  1;
+    ULONG   HandleSpinDown         :  1;
+    ULONG   HandleSpinUp           :  1;
+    ULONG   Reserved               : 27;
+} CDROM_POWER_OPTIONS, *PCDROM_POWER_OPTIONS;
+
+// this is a private enum, but must be kept here
+// to properly compile size of CDROM_DEVICE_EXTENSION
+typedef enum {
+    PowerDownDeviceInitial,
+    PowerDownDeviceLocked,
+    PowerDownDeviceQuiesced,
+    PowerDownDeviceFlushed,
+    PowerDownDeviceStopped,
+    PowerDownDeviceOff,
+    PowerDownDeviceUnlocked
+} CDROM_POWER_DOWN_STATE;
+
+// this is a private enum, but must be kept here
+// to properly compile size of CDROM_DEVICE_EXTENSION
+typedef enum {
+    PowerUpDeviceInitial,
+    PowerUpDeviceLocked,
+    PowerUpDeviceOn,
+    PowerUpDeviceStarted,
+    PowerUpDeviceUnlocked
+} CDROM_POWER_UP_STATE;
+
+// this is a private structure, but must be kept here
+// to properly compile size of CDROM_DEVICE_EXTENSION
+typedef struct _CDROM_POWER_CONTEXT {
+
+    BOOLEAN                 InUse;
+
+    LARGE_INTEGER           StartTime;
+    LARGE_INTEGER           Step1CompleteTime;  // for SYNC CACHE in power down case
+    LARGE_INTEGER           CompleteTime;
+
+    union {
+        CDROM_POWER_DOWN_STATE  PowerDown;
+        CDROM_POWER_UP_STATE    PowerUp;    // currently not used.
+    } PowerChangeState;
+
+    CDROM_POWER_OPTIONS     Options;
+
+    WDFREQUEST              PowerRequest;
+
+    SCSI_REQUEST_BLOCK      Srb;
+    SENSE_DATA              SenseData;
+
+    ULONG                   RetryCount;
+    LONGLONG                RetryIntervalIn100ns;
+
+} CDROM_POWER_CONTEXT, *PCDROM_POWER_CONTEXT;
+
+// device extension structure
+typedef struct _CDROM_DEVICE_EXTENSION {
+
+    // Version control field
+    ULONG           Version;
+
+    // structure size
+    ULONG           Size;
+
+    // the structure is fully ready to use
+    BOOLEAN         IsInitialized;
+
+    // the device is active
+    BOOLEAN         IsActive;
+
+    // the device is surprise removed.
+    BOOLEAN         SurpriseRemoved;
+
+    // Back pointer to device object
+    WDFDEVICE       Device;
+
+    // Save IoTarget here, do not retrieve anytime.
+    WDFIOTARGET     IoTarget;
+
+    // Additional WDF queue for serial I/O processing
+    WDFQUEUE        SerialIOQueue;
+
+    // A separate queue for all the create file requests in sync with device
+    // removal
+    WDFQUEUE        CreateQueue;
+
+    //Main timer of driver, will do Mcn work. (once per second)
+    WDFTIMER        MainTimer;
+
+    // Pointer to the initialization data for this driver.  This is more
+    // efficient than constantly getting the driver extension.
+    PCDROM_DRIVER_EXTENSION DriverExtension;
+
+    // WDM device information
+    PDEVICE_OBJECT  DeviceObject;
+
+    // Pointer to the physical device object we attached to
+    PDEVICE_OBJECT  LowerPdo;
+
+    // FILE_DEVICE_CD_ROM -- 2
+    DEVICE_TYPE     DeviceType;
+
+    // The name of the object
+    UNICODE_STRING  DeviceName;
+
+    // System device number
+    ULONG           DeviceNumber;
+
+    // Values for the flags are below.
+    USHORT          DeviceFlags;
+
+    // Flags for special behaviour required by different hardware,
+    // such as never spinning down or disabling advanced features such as write cache
+    ULONG           ScanForSpecialFlags;
+
+    // Add default Srb Flags.
+    ULONG           SrbFlags;
+
+    // Request timeout in seconds;
+    ULONG           TimeOutValue;
+
+    //The SCSI address of the device.
+    SCSI_ADDRESS    ScsiAddress;
+
+    // Buffer for drive parameters returned in IO device control.
+    DISK_GEOMETRY   DiskGeometry;
+
+    // Log2 of sector size
+    UCHAR           SectorShift;
+
+    // Length of partition in bytes
+    LARGE_INTEGER   PartitionLength;
+
+    // Number of bytes before start of partition
+    LARGE_INTEGER   StartingOffset;
+
+    // Interface name string returned by IoRegisterDeviceInterface.
+    UNICODE_STRING  MountedDeviceInterfaceName;
+
+    // Device capabilities
+    PSTORAGE_DEVICE_DESCRIPTOR  DeviceDescriptor;
+
+    // SCSI port driver capabilities
+    PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
+
+    // Device power properties
+    PDEVICE_POWER_DESCRIPTOR PowerDescriptor;
+
+    // Request Sense Buffer
+    PSENSE_DATA     SenseData;
+
+    // Total number of SCSI protocol errors on the device.
+    ULONG           ErrorCount;
+
+    // Lock count for removable media.
+    LONG            LockCount;
+    LONG            ProtectedLockCount;
+    LONG            InternalLockCount;
+
+    KEVENT          EjectSynchronizationEvent;
+    WDFWAITLOCK     EjectSynchronizationLock;
+
+    // Indicates that the necessary data structures for media change
+    // detection have been initialized.
+    PMEDIA_CHANGE_DETECTION_INFO MediaChangeDetectionInfo;
+
+    // Contains necessary data structures for ZPODD support.
+    PZERO_POWER_ODD_INFO         ZeroPowerODDInfo;
+
+    // File system context. Used for kernel-mode requests to disable autorun.
+    FILE_OBJECT_CONTEXT   KernelModeMcnContext;
+
+    // Count of media changes.  This field is only valid for the root partition
+    // (ie. if PhysicalDevice == NULL).
+    ULONG               MediaChangeCount;
+
+    // Storage for a release queue request.
+    WDFSPINLOCK         ReleaseQueueSpinLock;
+    WDFREQUEST          ReleaseQueueRequest;
+    SCSI_REQUEST_BLOCK  ReleaseQueueSrb;
+    WDFMEMORY           ReleaseQueueInputMemory;    //This is a wrapper of ReleaseQueueSrb
+    BOOLEAN             ReleaseQueueNeeded;
+    BOOLEAN             ReleaseQueueInProgress;
+
+    // Context structure for power operations.  Since we can only have
+    // one D irp at any time in the stack we don't need to worry about
+    // allocating multiple of these structures.
+    CDROM_POWER_CONTEXT     PowerContext;
+    BOOLEAN                 PowerDownInProgress;
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+    BOOLEAN                 IsVolumeOnlinePending;
+    WDFQUEUE                ManualVolumeReadyQueue;
+#endif
+
+    // Lock for Shutdown/Flush operations that need to stop/start the queue
+    WDFWAITLOCK             ShutdownFlushWaitLock;
+
+    // device specific data area
+    CDROM_DATA              DeviceAdditionalData;
+
+    // scratch buffer related fields.
+    CDROM_SCRATCH_CONTEXT   ScratchContext;
+
+    // Hold new private data that only classpnp should modify
+    // in this structure.
+    PCDROM_PRIVATE_FDO_DATA PrivateFdoData;
+
+    // Work item for async reads and writes and its context
+    WDFWORKITEM                 ReadWriteWorkItem;
+    CDROM_READ_WRITE_CONTEXT    ReadWriteWorkItemContext;
+
+    // Auxiliary WDF object for processing ioctl requests that need to go down
+    // to the port driver.
+    WDFWORKITEM             IoctlWorkItem;
+    CDROM_IOCTL_CONTEXT     IoctlWorkItemContext;
+
+} CDROM_DEVICE_EXTENSION, *PCDROM_DEVICE_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CDROM_DEVICE_EXTENSION, DeviceGetExtension)
+
+// a type definition for functions to be called after synchronization
+typedef
+NTSTATUS
+SYNC_HANDLER (
+    _In_ WDFDEVICE  Device,
+    _In_ WDFREQUEST Request
+    );
+
+typedef SYNC_HANDLER *PSYNC_HANDLER ;
+
+typedef struct _CDROM_REQUEST_CONTEXT {
+    PCDROM_DEVICE_EXTENSION     DeviceExtension;
+
+    WDFREQUEST      OriginalRequest;
+
+    LARGE_INTEGER   TimeReceived;
+    LARGE_INTEGER   TimeSentDownFirstTime;
+    LARGE_INTEGER   TimeSentDownLasttTime;
+
+
+    ULONG           RetriedCount;
+
+    // Used to send down an incoming IOCTL in the original context
+    BOOLEAN         SyncRequired;
+    PKEVENT         SyncEvent;
+    PSYNC_HANDLER   SyncCallback;
+
+    //
+    // Used for READ/WRITE requests.
+    // The reason why kernel primitives are used for the spinlock and
+    // the timer instead of WDF object is there is a race condition
+    // between the cancel callback and the timer routine.
+    // Because of this, the timer and the spin lock need to be associated
+    // per request rather than using shared memory in the device extension
+    // and it is possible for the WDF object initialization to fail whereas
+    // the kernel primitives initialize provided memory.  Initializing the
+    // kernel primitives will never fail.  Since READ/WRITE is a critical
+    // code path, it is not desired for READs or WRITEs to fail due to
+    // an allocation failure that can be avoided and because it is not
+    // uncommon to see a failure during a READ or WRITE that may not
+    // occur upon a retry.
+    //
+    KSPIN_LOCK      ReadWriteCancelSpinLock;
+    KTIMER          ReadWriteTimer;
+    KDPC            ReadWriteDpc;
+    BOOLEAN         ReadWriteIsCompleted;
+    BOOLEAN         ReadWriteRetryInitialized;
+
+} CDROM_REQUEST_CONTEXT, *PCDROM_REQUEST_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CDROM_REQUEST_CONTEXT, RequestGetContext)
+
+// Define context structure for asynchronous completions.
+typedef struct _COMPLETION_CONTEXT {
+    WDFDEVICE           Device;
+    SCSI_REQUEST_BLOCK  Srb;
+} COMPLETION_CONTEXT, *PCOMPLETION_CONTEXT;
+
+
+//
+#define SCSI_CDROM_TIMEOUT          10
+#define SCSI_CHANGER_BONUS_TIMEOUT  10
+
+//
+// This value is used as the upper limit for all commands CDROM sends down
+// to device, unless the default timeout value is overriden by registry value
+// "TimeOutValue" and it is larger than this value.
+//
+#define SCSI_CDROM_OPC_TIMEOUT      260
+
+#define HITACHI_MODE_DATA_SIZE      12
+#define MODE_DATA_SIZE              64
+
+#define RAW_SECTOR_SIZE           2352
+#define COOKED_SECTOR_SIZE        2048
+
+#define CDROM_SRB_LIST_SIZE          4
+
+#define PLAY_ACTIVE(x) (x->DeviceAdditionalData.PlayActive)
+
+#define MSF_TO_LBA(Minutes,Seconds,Frames) \
+                (ULONG)((60 * 75 * (Minutes)) + (75 * (Seconds)) + ((Frames) - 150))
+
+// Sector types for READ_CD
+
+#define ANY_SECTOR                0
+#define CD_DA_SECTOR              1
+#define YELLOW_MODE1_SECTOR       2
+#define YELLOW_MODE2_SECTOR       3
+#define FORM2_MODE1_SECTOR        4
+#define FORM2_MODE2_SECTOR        5
+
+#define MAX_COPY_PROTECT_AGID     4
+
+#ifdef ExAllocatePool
+    #undef ExAllocatePool
+    #define ExAllocatePool #assert(FALSE)
+#endif
+
+// memory allocation tags
+// Sc?? - Mass storage driver tags
+// ScC<number> - Class driver misc allocations
+// ScC? - CdRom
+
+#define CDROM_TAG_AUTORUN_DISABLE        'ACcS' // "ScCA" - Autorun disable functionality
+#define CDROM_TAG_MEDIA_CHANGE_DETECTION 'aCcS' // "ScCa" - Media change detection
+
+#define CDROM_TAG_SCRATCH               'BCcS'  // "ScSB" - Scratch buffer (usually 64k)
+#define CDROM_TAG_GET_CONFIG            'CCcS'  // "ScCC" - Ioctl GET_CONFIGURATION
+#define CDROM_TAG_COMPLETION_CONTEXT    'cCcS'  // "ScCc" - Context of completion routine
+#define CDROM_TAG_DESCRIPTOR            'DCcS'  // "ScCD" - Adaptor & Device descriptor buffer
+#define CDROM_TAG_DISC_INFO             'dCcS'  // "ScCd" - Disc information
+#define CDROM_TAG_SYNC_EVENT            'eCcS'  // "ScCe" - Request sync event
+#define CDROM_TAG_FEATURE               'FCcS'  // "ScCF" - Feature descriptor
+#define CDROM_TAG_GESN                  'GCcS'  // "ScCG" - GESN buffer
+#define CDROM_TAG_SENSE_INFO            'ICcS'  // "ScCI" - Sense info buffers
+#define CDROM_TAG_INQUIRY               'iCcS'  // "ScCi" - Cached inquiry buffer
+#define CDROM_TAG_MODE_DATA             'MCcS'  // "ScCM" - Mode data buffer
+#define CDROM_TAG_STREAM                'OCCS'  // "SCCO" - Set stream buffer
+#define CDROM_TAG_NOTIFICATION          'oCcS'  // "ScCo" - Device Notification buffer
+#define CDROM_TAG_PLAY_ACTIVE           'pCcS'  // "ScCp" - Play active checks
+#define CDROM_TAG_REGISTRY              'rCcS'  // "ScCr" - Registry string
+#define CDROM_TAG_SRB                   'SCcS'  // "ScCS" - Srb allocation
+#define CDROM_TAG_STRINGS               'sCcS'  // "ScCs" - Assorted string data
+#define CDROM_TAG_UPDATE_CAP            'UCcS'  // "ScCU" - Update capacity path
+#define CDROM_TAG_ZERO_POWER_ODD        'ZCcS'  // "ScCZ" - Zero Power ODD
+
+#define DVD_TAG_READ_KEY                'uCcS'  // "ScCu" - Read buffer for dvd key
+#define DVD_TAG_RPC2_CHECK              'VCcS'  // "ScCV" - Read buffer for dvd/rpc2 check
+#define DVD_TAG_DVD_REGION              'vCcS'  // "ScCv" - Read buffer for rpc2 check
+#define DVD_TAG_SECURITY                'XCcS'  // "ScCX" - Security descriptor
+
+
+// registry keys and data entry names.
+#define CDROM_SUBKEY_NAME                       (L"CdRom")  // store new settings here
+#define CDROM_READ_CD_NAME                      (L"ReadCD") // READ_CD support previously detected
+#define CDROM_NON_MMC_DRIVE_NAME                (L"NonMmc") // MMC commands hang
+#define CDROM_TYPE_ONE_GET_CONFIG_NAME          (L"NoTypeOneGetConfig") // Type One Get Config commands not supported
+#define CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE   (L"NonMmcVendorSpecificProfile") // GET_CONFIG returns vendor specific header
+                                                                                 // profiles that are not per spec (length divisible by 4)
+#define DVD_DEFAULT_REGION          (L"DefaultDvdRegion")   // this is init. by the dvd class installer
+#define DVD_MAX_REGION              8
+
+// AACS defines
+#define AACS_MKB_PACK_SIZE  0x8000 // does not include header
+
+//enumeration of device interfaces need to be registered.
+typedef enum {
+    CdRomDeviceInterface = 0,     // CdRomClassGuid
+    MountedDeviceInterface        // MOUNTDEV_MOUNTED_DEVICE_GUID
+} CDROM_DEVICE_INTERFACES, *PCDROM_DEVICE_INTERFACES;
+
+
+typedef
+VOID
+(*PCDROM_SCAN_FOR_SPECIAL_HANDLER) (
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ ULONG_PTR                Data
+    );
+
+// Rountines Definition
+
+#define FREE_POOL(_PoolPtr)     \
+    if (_PoolPtr != NULL) {     \
+        ExFreePool(_PoolPtr);   \
+        _PoolPtr = NULL;        \
+    }
+
+#define EXCLUSIVE_MODE(_CdData)                 (_CdData->ExclusiveOwner != NULL)
+#define EXCLUSIVE_OWNER(_CdData, _FileObject)   (_CdData->ExclusiveOwner == _FileObject)
+
+#define IS_SCSIOP_READ(opCode)         \
+      ((opCode == SCSIOP_READ6)   ||   \
+       (opCode == SCSIOP_READ)    ||   \
+       (opCode == SCSIOP_READ12)  ||   \
+       (opCode == SCSIOP_READ16))
+
+#define IS_SCSIOP_WRITE(opCode)         \
+      ((opCode == SCSIOP_WRITE6)   ||   \
+       (opCode == SCSIOP_WRITE)    ||   \
+       (opCode == SCSIOP_WRITE12)  ||   \
+       (opCode == SCSIOP_WRITE16))
+
+#define IS_SCSIOP_READWRITE(opCode)  (IS_SCSIOP_READ(opCode) || IS_SCSIOP_WRITE(opCode))
+
+// Bit Flag Macros
+#define SET_FLAG(Flags, Bit)    ((Flags) |= (Bit))
+#define CLEAR_FLAG(Flags, Bit)  ((Flags) &= ~(Bit))
+#define TEST_FLAG(Flags, Bit)   (((Flags) & (Bit)) != 0)
+
+__inline
+BOOLEAN
+ValidChar(UCHAR Ch)
+{
+    if (((Ch >= '0') && (Ch <= '9')) ||
+        (((Ch|0x20) >= 'a') && ((Ch|0x20) <= 'z')) ||
+        (strchr(" .,:;_-", Ch) != NULL))
+    {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+// could be #define, but this allows typechecking
+__inline
+BOOLEAN
+PORT_ALLOCATED_SENSE(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceExtension);
+    return (BOOLEAN)((TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE) &&
+             TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER))
+            );
+}
+
+__inline
+VOID
+FREE_PORT_ALLOCATED_SENSE_BUFFER(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb
+    )
+{
+#ifndef DEBUG
+    UNREFERENCED_PARAMETER(DeviceExtension);
+#endif
+    NT_ASSERT(TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+    NT_ASSERT(TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+    NT_ASSERT(Srb->SenseInfoBuffer != DeviceExtension->SenseData);
+
+    ExFreePool(Srb->SenseInfoBuffer);
+    Srb->SenseInfoBuffer = NULL;
+    Srb->SenseInfoBufferLength = 0;
+    CLEAR_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER);
+    return;
+}
+
+// Standard driver entry function
+DRIVER_INITIALIZE DriverEntry;
+
+// Driver Event callbacks
+EVT_WDF_DRIVER_DEVICE_ADD DriverEvtDeviceAdd;
+
+EVT_WDF_OBJECT_CONTEXT_CLEANUP DriverEvtCleanup;
+
+// Device Event callbacks
+
+EVT_WDF_OBJECT_CONTEXT_CLEANUP DeviceEvtCleanup;
+
+EVT_WDF_FILE_CLOSE DeviceEvtFileClose;
+
+EVT_WDF_IO_IN_CALLER_CONTEXT DeviceEvtIoInCallerContext;
+
+EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT DeviceEvtSelfManagedIoInit;
+
+EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP DeviceEvtSelfManagedIoCleanup;
+
+EVT_WDF_DEVICE_D0_ENTRY DeviceEvtD0Entry;
+
+EVT_WDF_DEVICE_D0_EXIT DeviceEvtD0Exit;
+
+EVT_WDF_DEVICE_SURPRISE_REMOVAL DeviceEvtSurpriseRemoval;
+
+// Create Queue Event callbacks
+
+EVT_WDF_IO_QUEUE_IO_DEFAULT CreateQueueEvtIoDefault;
+
+// Sequential Queue Event callbacks
+
+// We do not use KMDF annotation for the following function, because it handles
+// both read and write requests and there is no single annotation for that.
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+SequentialQueueEvtIoReadWrite(
+    _In_ WDFQUEUE     Queue,
+    _In_ WDFREQUEST   Request,
+    _In_ size_t       Length
+    );
+
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SequentialQueueEvtIoDeviceControl;
+
+EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE SequentialQueueEvtCanceledOnQueue;
+
+// Miscellaneous request callbacks
+
+EVT_WDF_OBJECT_CONTEXT_CLEANUP RequestEvtCleanup;
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS RequestProcessShutdownFlush;
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS RequestProcessSetPower;
+
+
+// helper functions
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceClaimRelease(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN      Release
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceReleaseMcnResources(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveDescriptor(
+    _In_ WDFDEVICE                              Device,
+    _In_ PSTORAGE_PROPERTY_ID                   PropertyId,
+    _Outptr_ PSTORAGE_DESCRIPTOR_HEADER*        Descriptor
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceRetrieveHackFlagsFromRegistry(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceHackFlagsScan(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ ULONG_PTR                Data
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+StringsAreMatched(
+    _In_opt_z_ PCHAR StringToMatch,
+    _In_z_     PCHAR TargetString
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceGetParameter(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_opt_ PWSTR                  SubkeyName,
+    _In_ PWSTR                      ParameterName,
+    _Inout_ PULONG                  ParameterValue  // also default value
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSetParameter(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_opt_z_ PWSTR                SubkeyName,
+    _In_ PWSTR                      ParameterName,
+    _In_ ULONG                      ParameterValue
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetTimeOutValueFromRegistry();
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScanForSpecialHandler(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ ULONG_PTR               HackFlags
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendSrbSynchronously(
+    _In_ WDFDEVICE           Device,
+    _In_ PSCSI_REQUEST_BLOCK Srb,
+    _In_opt_ PVOID           BufferAddress,
+    _In_ ULONG               BufferLength,
+    _In_ BOOLEAN             WriteToDevice,
+    _In_opt_ WDFREQUEST      OriginalRequest
+    );
+
+BOOLEAN
+RequestSenseInfoInterpret(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      WDFREQUEST                Request, // get IRP MJ code, IoControlCode from it (or attached original request)
+    _In_      PSCSI_REQUEST_BLOCK       Srb,
+    _In_      ULONG                     RetriedCount,
+    _Out_     NTSTATUS*                 Status,
+    _Out_opt_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+              LONGLONG*                 RetryIntervalIn100ns
+    );
+
+VOID
+RequestSetReceivedTime(
+    _In_ WDFREQUEST Request
+    );
+
+VOID
+RequestSetSentTime(
+    _In_ WDFREQUEST Request
+    );
+
+VOID
+RequestClearSendTime(
+    _In_ WDFREQUEST Request
+    );
+
+BOOLEAN
+RequestSenseInfoInterpretForScratchBuffer(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_    ULONG                   RetriedCount,
+    _Out_   NTSTATUS*               Status,
+    _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+            LONGLONG*               RetryIntervalIn100ns
+    );
+
+
+VOID
+DeviceSendNotification(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ const GUID*              Guid,
+    _In_ ULONG                    ExtraDataSize,
+    _In_opt_ PVOID                ExtraData
+    );
+
+VOID
+DeviceSendStartUnit(
+    _In_ WDFDEVICE Device
+    );
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE DeviceAsynchronousCompletion;
+
+VOID
+DeviceReleaseQueue(
+    _In_ WDFDEVICE    Device
+    );
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE DeviceReleaseQueueCompletion;
+
+VOID
+DevicePerfIncrementErrorCount(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceCacheDeviceInquiryData(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceCacheGetConfigurationData(
+    _In_  WDFDEVICE     Device
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+DeviceFindFeaturePage(
+    _In_reads_bytes_(Length) PGET_CONFIGURATION_HEADER   FeatureBuffer,
+    _In_ ULONG const                                Length,
+    _In_ FEATURE_NUMBER const                       Feature
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DevicePrintAllFeaturePages(
+    _In_reads_bytes_(Usable) PGET_CONFIGURATION_HEADER   Buffer,
+    _In_ ULONG const                                Usable
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceSetRawReadInfo(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+MediaReadCapacity(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+MediaReadCapacityDataInterpret(
+    _In_ WDFDEVICE            Device,
+    _In_ PREAD_CAPACITY_DATA  ReadCapacityBuffer
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitReleaseQueueContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitPowerContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceCreateWellKnownName(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitializeDvd(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DevicePickDvdRegion(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRegisterInterface(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ CDROM_DEVICE_INTERFACES InterfaceType
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+DeviceSendPowerDownProcessRequest(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
+    _In_opt_ WDFCONTEXT Context
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceScanForSpecial(
+    _In_ PCDROM_DEVICE_EXTENSION          DeviceExtension,
+    _In_ CDROM_SCAN_FOR_SPECIAL_INFO      DeviceList[],
+    _In_ PCDROM_SCAN_FOR_SPECIAL_HANDLER  Function
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeHotplugInfo(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+NTSTATUS
+DeviceErrorHandlerForMmc(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb,
+    _Inout_ PNTSTATUS             Status,
+    _Inout_ PBOOLEAN              Retry
+    );
+
+NTSTATUS
+DeviceErrorHandlerForHitachiGD2000(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb,
+    _Inout_ PNTSTATUS             Status,
+    _Inout_ PBOOLEAN              Retry
+    );
+
+EVT_WDF_WORKITEM DeviceRestoreDefaultSpeed;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeMediaChangeDetection(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    );
+
+VOID
+DeviceSetMediaChangeStateEx(
+    _In_        PCDROM_DEVICE_EXTENSION       DeviceExtension,
+    _In_        MEDIA_CHANGE_DETECTION_STATE  NewState,
+    _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceSendDelayedMediaChangeNotifications(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+NTSTATUS
+RequestSynchronizeProcessWithSerialQueue(
+    _In_ WDFDEVICE Device,
+    _In_ WDFREQUEST Request
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceCleanupProtectedLocks(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PFILE_OBJECT_CONTEXT     FileObjectContext
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceCleanupDisableMcn(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PFILE_OBJECT_CONTEXT     FileObjectContext
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceUnlockExclusive(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ WDFFILEOBJECT            FileObject,
+    _In_ BOOLEAN                  IgnorePreviousMediaChanges
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestProcessSerializedIoctl(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ WDFREQUEST               Request
+    );
+
+NTSTATUS
+RequestIsIoctlBlockedByExclusiveAccess(
+    _In_  WDFREQUEST  Request,
+    _Out_ PBOOLEAN    IsBlocked
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceSendRequestSynchronously(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request,
+    _In_ BOOLEAN      RequestFormated
+    );
+
+VOID
+DeviceSendIoctlAsynchronously(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_ ULONG                      IoControlCode,
+    _In_ PDEVICE_OBJECT             TargetDeviceObject
+    );
+
+IO_COMPLETION_ROUTINE RequestAsynchronousIrpCompletion;
+
+//  MMC Update functions
+
+BOOLEAN
+DeviceIsMmcUpdateRequired(
+    _In_ WDFDEVICE    Device
+    );
+
+// Helper functions
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceEnableMediaChangeDetection(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _Inout_ PFILE_OBJECT_CONTEXT    FileObjectContext,
+    _In_    BOOLEAN                 IgnorePreviousMediaChanges
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceDisableMediaChangeDetection(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _Inout_ PFILE_OBJECT_CONTEXT    FileObjectContext
+    );
+
+NTSTATUS
+RequestSetContextFields(
+    _In_ WDFREQUEST    Request,
+    _In_ PSYNC_HANDLER Handler
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+DeviceIsPlayActive(
+    _In_ WDFDEVICE Device
+    );
+
+NTSTATUS
+RequestDuidGetDeviceIdProperty(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_ size_t *               DataLength
+    );
+
+NTSTATUS
+RequestDuidGetDeviceProperty(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_ size_t *               DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+ULONG
+DeviceRetrieveModeSenseUsingScratch(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_reads_bytes_(Length) PCHAR     ModeSenseBuffer,
+    _In_ ULONG                    Length,
+    _In_ UCHAR                    PageCode,
+    _In_ UCHAR                    PageControl
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+ModeSenseFindSpecificPage(
+    _In_reads_bytes_(Length) PCHAR   ModeSenseBuffer,
+    _In_ size_t                 Length,
+    _In_ UCHAR                  PageMode,
+    _In_ BOOLEAN                Use6Byte
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PerformEjectionControl(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ WDFREQUEST               Request,
+    _In_ MEDIA_LOCK_TYPE          LockType,
+    _In_ BOOLEAN                  Lock
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceDisableMainTimer(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceEnableMainTimer(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+EVT_WDF_TIMER DeviceMainTimerTickHandler;
+
+VOID
+RequestSetupMcnSyncIrp(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestSetupMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ BOOLEAN                  UseGesn
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+RequestSendMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+RequestPostWorkMcnRequest(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextReuseRequest(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextBeginUse(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextEndUse(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+POWER_SETTING_CALLBACK DevicePowerSettingCallback;
+
+//  Zero Power ODD functions
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeZPODD(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceReleaseZPODDResources(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+NTSTATUS
+DeviceZPODDGetPowerupReason(
+    _In_    PCDROM_DEVICE_EXTENSION         DeviceExtension,
+    _Out_   PSTORAGE_IDLE_POWERUP_REASON    PowerupReason
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceZPODDIsInHomePosition(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceMarkActive(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 IsActive,
+    _In_ BOOLEAN                 SetIdleTimeout
+    );
+
+// common routines for specific IOCTL process
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DvdStartSessionReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  ULONG                    IoControlCode,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_opt_  PVOID                InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ReadDvdStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ReadQChannel(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _Out_ size_t *                 DataLength
+    );
+
+
+#ifndef SIZEOF_ARRAY
+    #define SIZEOF_ARRAY(ar)        (sizeof(ar)/sizeof((ar)[0]))
+#endif // !defined(SIZEOF_ARRAY)
+
+
+// 100us to seconds
+#define UNIT_100NS_PER_SECOND (10*1000*1000)
+#define SECONDS_TO_100NS_UNITS(x) (((LONGLONG)x) * UNIT_100NS_PER_SECOND)
+
+//
+// Bit Flag Macros
+//
+#define SET_FLAG(Flags, Bit)    ((Flags) |= (Bit))
+#define CLEAR_FLAG(Flags, Bit)  ((Flags) &= ~(Bit))
+#define TEST_FLAG(Flags, Bit)   (((Flags) & (Bit)) != 0)
+
+//
+// neat little hacks to count number of bits set efficiently
+//
+__inline ULONG CountOfSetBitsUChar(UCHAR _X)
+                    { ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i; }
+__inline ULONG CountOfSetBitsULong(ULONG _X)
+                    { ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i; }
+__inline ULONG CountOfSetBitsULong32(ULONG32 _X)
+                    { ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i; }
+__inline ULONG CountOfSetBitsULong64(ULONG64 _X)
+                    { ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i; }
+__inline ULONG CountOfSetBitsUlongPtr(ULONG_PTR _X)
+                    { ULONG i = 0; while (_X) { _X &= _X - 1; i++; } return i; }
+
+
+__inline
+BOOLEAN
+IsVolumeMounted(
+    _In_ PDEVICE_OBJECT DeviceObject
+    )
+{
+#pragma prefast(push)
+#pragma prefast(disable: 28175, "there is no other way to check if there is volume mounted")
+    return (DeviceObject->Vpb != NULL) &&
+           ((DeviceObject->Vpb->Flags & VPB_MOUNTED) != 0);
+#pragma prefast(pop)
+}
+
+
+__inline _Ret_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(
+    _In_range_(1,0xFFFFFFFF) ULONG SectorsPerSecond // zero would cause divide-by-zero
+    )
+{
+    //     64k write
+    // ---------------- == time per 64k write
+    //  sectors/second
+    //
+    //  (32 / N) == seconds
+    //  (32 / N) * (1,000,000,000) == nanoseconds
+    //  (32 / N) * (1,000,000,000) / 100 == 100ns increments
+    //  (32 * 10,000,000) / N == 100ns increments
+    //
+    //  320,000,000 / N == 100ns increments
+    //  And this is safe to run in kernel-mode (no floats)
+    //
+
+    // this assert ensures that we _never_ can return a value
+    // larger than the maximum allowed.
+    C_ASSERT(320000000 < MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS);
+
+    return 320000000 / SectorsPerSecond;
+}
+
+__inline
+UCHAR
+RequestGetCurrentStackLocationFlags(
+    _In_ WDFREQUEST Request
+    )
+{
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  currentStack = NULL;
+
+    irp = WdfRequestWdmGetIrp(Request);
+    currentStack = IoGetCurrentIrpStackLocation(irp);
+
+    return currentStack->Flags;
+}
+
+__inline
+ULONG
+TimeOutValueGetCapValue(
+    _In_ ULONG  TimeOutValue,
+    _In_ ULONG  Times
+    )
+{
+    ULONG   value = 0;
+
+    if (TimeOutValue > SCSI_CDROM_OPC_TIMEOUT)
+    {
+        // if time out value is specified by user in registry, and is
+        // bigger than OPC time out, it should be big enough
+        value = TimeOutValue;
+    }
+    else
+    {
+        // otherwise, OPC time out value should be the upper limit
+        value = min(TimeOutValue * Times, SCSI_CDROM_OPC_TIMEOUT);
+    }
+
+    return value;
+}
+
+VOID
+RequestCompletion(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ NTSTATUS                Status,
+    _In_ ULONG_PTR               Information
+    );
+
+NTSTATUS
+RequestSend(
+    _In_        PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_        WDFREQUEST              Request,
+    _In_        WDFIOTARGET             IoTarget,
+    _In_        ULONG                   Flags,
+    _Out_opt_   PBOOLEAN                RequestSent
+    );
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE RequestDummyCompletionRoutine;
+
+VOID
+RequestProcessInternalDeviceControl(
+    _In_ WDFREQUEST              Request,
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+EVT_WDF_WORKITEM IoctlWorkItemRoutine;
+
+EVT_WDF_WORKITEM ReadWriteWorkItemRoutine;
+
+#pragma warning(pop) // un-sets any local warning changes
+
+#endif // __CDROMP_H__
+
+
diff --git a/drivers/storage/class/cdrom_new/cdrom.inf b/drivers/storage/class/cdrom_new/cdrom.inf
new file mode 100644 (file)
index 0000000..8922097
--- /dev/null
@@ -0,0 +1,159 @@
+;
+; Copyright (c) Microsoft Corporation.  All rights reserved.
+;
+
+[Version]
+Signature = "$WINDOWS NT$"
+LayoutFile = layout.inf
+Class     = CDROM
+ClassGuid = {4D36E965-E325-11CE-BFC1-08002BE10318}
+Provider  = %MSFT%
+; Git commit 96eb96d @ https://github.com/microsoft/Windows-driver-samples
+DriverVer = 08/11/2016, 1.0
+
+;[SourceDisksNames]
+;3426=windows cd
+
+;[SourceDisksFiles]
+;cdrom.sys         = 3426
+
+[DestinationDirs]
+cdrom_copyfiles   = 12
+
+[cdrom_copyfiles]
+cdrom.sys,,,0x100
+
+[ControlFlags]
+BasicDriverOk=*
+
+[Manufacturer]
+%MITSUMI%         = mitsumi_cdrom,NT$ARCH$
+%GenManufacturer% = cdrom_device,NT$ARCH$
+
+;[mitsumi_cdrom.NT$ARCH$]
+[mitsumi_cdrom.NT]
+%Mitsumi_cdrom_devdesc%    = mitsumi_install,IDE\CdRomMITSUMI_CD-ROM________!A________________
+
+;[cdrom_device.NT$ARCH$]
+[cdrom_device.NT]
+;;
+;; if none of the above matched, then only cdrom.sys is required for this drive
+;;
+%gencdrom_devdesc%         = cdrom_install,SCSI\WormPIONEER_CD-WO_DR-R504X__
+%gencdrom_devdesc%         = cdrom_install,SCSI\WormSONY____CD-R___CDU920S__
+%gencdrom_devdesc%         = cdrom_install,SCSI\WormSONY____CD-R___CDU948S__
+%gencdrom_devdesc%         = cdrom_install,GenCdRom
+%gencdrom_devdesc%         = cdrom_install_ISO_drive,SCSI\CdRomMsft____Virtual_DVD-ROM_
+
+;;
+;; Use to add filter drivers for the device
+;;
+
+[mitsumi_addreg]
+HKR,,"FriendlyName",,%Mitsumi_Generic_FriendlyName%
+
+;;
+;; Give ISO drives a more friendly name
+;;
+
+[ISO_addreg]
+HKR,,"FriendlyName",,%ISO_Generic_FriendlyName%
+
+;;
+;; The AutoRunAlwaysDisable key is only for use when the hardware cannot
+;; accepts  TEST_UNIT_READY  commands.  Disabling 'AutoRun' or including
+;; devices in this list will prevent removable media services from being
+;; able to properly handle these devices.
+;;
+
+[autorun_addreg]
+HKR,,"AutoRun",0x00010003,1
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "NEC     MBR-7   "
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "NEC     MBR-7.4 "
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "PIONEER CHANGR DRM-1804X"
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "PIONEER CD-ROM DRM-6324X"
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "PIONEER CD-ROM DRM-624X "
+HKR,,"AutoRunAlwaysDisable", 0x00010008, "TORiSAN CD-ROM CDR_C36"
+
+;;
+;; Use to disable synchronous transfers to this device.  Sync transfers will
+;; always be turned off by default in this INF for any cdrom-type device
+;;
+
+[nosync_addreg]
+HKR,,"DefaultRequestFlags",0x00010001,8
+HKR,"Storport","MinimumIdleTimeoutInMS",0x00010001,0
+
+;;
+;; Installation section for mitsumi.
+;;
+
+[mitsumi_install]
+CopyFiles=cdrom_copyfiles
+
+[mitsumi_install.HW]
+AddReg=nosync_addreg,mitsumi_addreg
+
+[mitsumi_install.Services]
+Needs=cdrom_install.Services
+
+;;
+;; Installation section for generic cdrom.
+;;
+
+[cdrom_install]
+CopyFiles=cdrom_copyfiles
+
+[cdrom_install.HW]
+AddReg=nosync_addreg
+
+[cdrom_install.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection,cdrom_EventLog_InstallSection
+
+[cdrom_install_ISO_drive]
+CopyFiles=cdrom_copyfiles
+
+[cdrom_install_ISO_drive.HW]
+AddReg=nosync_addreg,ISO_addreg
+
+[cdrom_install_ISO_drive.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection,cdrom_EventLog_InstallSection
+
+;;
+;; Service install sections
+;;
+
+[cdrom_ServiceInstallSection]
+DisplayName   = %cdrom_ServiceDesc%
+ServiceType   = 1
+StartType     = 1
+ErrorControl  = 1
+ServiceBinary = %12%\cdrom.sys
+LoadOrderGroup = "SCSI CDROM Class"
+AddReg=autorun_addreg
+
+[cdrom_EventLog_InstallSection]
+;AddReg=cdrom_EventLog_AddReg
+
+;[cdrom_EventLog_AddReg]
+;HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+;HKR,,TypesSupported,0x00010001,7
+
+
+[Strings]
+MSFT        = "Microsoft"
+
+;; Manufacturer specific strings
+MITSUMI         = "Mitsumi"
+GenManufacturer = "(Standard CD-ROM drives)"
+
+;; Descriptions for enumerated brands and models
+Mitsumi_cdrom_devdesc    = "Mitsumi CD-ROM Drive"
+gencdrom_devdesc         = "CD-ROM Drive"
+
+;; Friendly names
+Mitsumi_Generic_FriendlyName = "Mitsumi CD-ROM Drive"
+ISO_Generic_FriendlyName = "Microsoft Virtual DVD-ROM"
+
+;; Service descriptions
+cdrom_ServiceDesc   = "CD-ROM Driver"
diff --git a/drivers/storage/class/cdrom_new/cdrom.rc b/drivers/storage/class/cdrom_new/cdrom.rc
new file mode 100644 (file)
index 0000000..688f914
--- /dev/null
@@ -0,0 +1,23 @@
+//+-------------------------------------------------------------------------
+//
+//  Microsoft Windows
+//
+//  Copyright (C) Microsoft Corporation, 1996 - 1999
+//
+//  File:       scsicdrm.rc
+//
+//--------------------------------------------------------------------------
+
+#include <verrsrc.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE                VFT_DRV
+#define VER_FILESUBTYPE             VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR     "SCSI CD-ROM Driver"
+#define VER_INTERNALNAME_STR        "cdrom.sys"
+#define VER_ORIGINALFILENAME_STR    "cdrom.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
+
diff --git a/drivers/storage/class/cdrom_new/cdromp.h b/drivers/storage/class/cdrom_new/cdromp.h
new file mode 100644 (file)
index 0000000..7719213
--- /dev/null
@@ -0,0 +1,383 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    cdromp.h
+
+Abstract:
+
+    Private header file for cdrom.sys modules.  This contains private
+    structure and function declarations as well as constant values which do
+    not need to be exported.
+
+Author:
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __CDROMP_H__
+#define __CDROMP_H__
+
+
+#include <scsi.h>
+#include <storduid.h>
+#include <mountdev.h>
+#include <ioevent.h>
+#include <ntintsafe.h>
+
+/*
+ *  IA64 requires 8-byte alignment for pointers, but the IA64 NT kernel expects 16-byte alignment
+ */
+#ifdef _WIN64
+    #define PTRALIGN                DECLSPEC_ALIGN(16)
+#else
+    #define PTRALIGN
+#endif
+
+// NOTE: Start with a smaller 100 second maximum, due to current assert in CLASSPNP
+//       0x0000 00C9'2A69 C000 (864,000,000,000) is 24 hours in 100ns units
+//       0x0000 0000'3B9A CA00 (  1,000,000,000) is 100 seconds in 100ns units
+#define MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS (0x3B9ACA00)
+
+// structures to simplify matching devices, ids, and hacks required for
+// these ids.
+typedef struct _CDROM_SCAN_FOR_SPECIAL_INFO {
+    //
+    // * NULL pointers indicates that no match is required.
+    // * empty string will only match an empty string.  non-existant strings
+    //   in the device descriptor are considered empty strings for this match.
+    //   (ie. "" will only match "")
+    // * all other strings will do partial matches, based upon
+    //   string provided (ie. "hi" will match "hitazen" and "higazui")
+    // * array must end with all three PCHARs being set to NULL.
+    //
+
+    PCHAR      VendorId;
+    PCHAR      ProductId;
+    PCHAR      ProductRevision;
+
+    //
+    // marked as a ULONG_PTR to allow use as either a ptr to a data block
+    // or 32 bits worth of flags. (64 bits on 64 bit systems)  no longer a
+    // const so that it may be dynamically built.
+    //
+
+    ULONG_PTR  Data;
+
+} CDROM_SCAN_FOR_SPECIAL_INFO, *PCDROM_SCAN_FOR_SPECIAL_INFO;
+
+// Define the various states that media can be in for autorun.
+typedef enum _MEDIA_CHANGE_DETECTION_STATE {
+    MediaUnknown,
+    MediaPresent,
+    MediaNotPresent,
+    MediaUnavailable   // e.g. cd-r media undergoing burn
+} MEDIA_CHANGE_DETECTION_STATE, *PMEDIA_CHANGE_DETECTION_STATE;
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+    This structure defines the history kept for a given transfer packet.
+    It includes a srb status/sense data structure that is always either valid
+    or zero-filled for the full 18 bytes, time sent/completed, and how long
+    the retry delay was requested to be.
+
+--*/
+typedef struct _SRB_HISTORY_ITEM {
+    LARGE_INTEGER TickCountSent;             //  0x00..0x07
+    LARGE_INTEGER TickCountCompleted;        //  0x08..0x0F
+    ULONG         MillisecondsDelayOnRetry;  //  0x10..0x13
+    SENSE_DATA    NormalizedSenseData;       //  0x14..0x25 (0x12 bytes)
+    UCHAR         SrbStatus;                 //  0x26
+    UCHAR         ClassDriverUse;            //  0x27 -- one byte free (alignment)
+} SRB_HISTORY_ITEM, *PSRB_HISTORY_ITEM;
+
+typedef struct _SRB_HISTORY {
+    ULONG_PTR        ClassDriverUse[4]; // for the class driver to use as they please
+    _Field_range_(1,30000)
+    ULONG            TotalHistoryCount;
+    _Field_range_(0,TotalHistoryCount)
+    ULONG            UsedHistoryCount;
+    _Field_size_part_(TotalHistoryCount, UsedHistoryCount)
+    SRB_HISTORY_ITEM History[1];
+} SRB_HISTORY, *PSRB_HISTORY;
+
+extern CDROM_SCAN_FOR_SPECIAL_INFO CdRomBadItems[];
+
+/*++////////////////////////////////////////////////////////////////////////////*/
+
+// legacy registry key and values.
+#define CLASSP_REG_SUBKEY_NAME                  (L"Classpnp")
+
+#define CLASSP_REG_HACK_VALUE_NAME              (L"HackMask")
+#define CLASSP_REG_MMC_DETECTION_VALUE_NAME     (L"MMCDetectionState")
+#define CLASSP_REG_WRITE_CACHE_VALUE_NAME       (L"WriteCacheEnableOverride")
+#define CLASSP_REG_PERF_RESTORE_VALUE_NAME      (L"RestorePerfAtCount")
+#define CLASSP_REG_REMOVAL_POLICY_VALUE_NAME    (L"UserRemovalPolicy")
+#define WINPE_REG_KEY_NAME                      (L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\MiniNT")
+
+#define CLASS_PERF_RESTORE_MINIMUM              (0x10)
+#define CLASS_ERROR_LEVEL_1                     (0x4)
+#define CLASS_ERROR_LEVEL_2                     (0x8)
+
+#define FDO_HACK_CANNOT_LOCK_MEDIA              (0x00000001)
+#define FDO_HACK_GESN_IS_BAD                    (0x00000002)
+#define FDO_HACK_NO_RESERVE6                    (0x00000008)
+#define FDO_HACK_GESN_IGNORE_OPCHANGE           (0x00000010)
+#define FDO_HACK_NO_STREAMING                   (0x00000020)
+#define FDO_HACK_NO_ASYNCHRONOUS_NOTIFICATION   (0x00000040)
+
+#define FDO_HACK_VALID_FLAGS                    (0x0000007F)
+#define FDO_HACK_INVALID_FLAGS                  (~FDO_HACK_VALID_FLAGS)
+
+/*
+ *  Lots of retries of synchronized SCSI commands that devices may not
+ *  even support really slows down the system (especially while booting).
+ *  (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
+ *  If a disk cannot return a small initialization buffer at startup
+ *  in two attempts (with delay interval) then we cannot expect it to return
+ *  data consistently with four retries.
+ *  So don't set the retry counts as high here as for data SRBs.
+ *
+ *  If we find that these requests are failing consecutively,
+ *  despite the retry interval, on otherwise reliable media,
+ *  then we should either increase the retry interval for
+ *  that failure or (by all means) increase these retry counts as appropriate.
+ */
+//#define NUM_LOCKMEDIAREMOVAL_RETRIES    1
+//#define NUM_MODESENSE_RETRIES           1
+//#define NUM_DRIVECAPACITY_RETRIES       1
+
+/*
+ *  We retry failed I/O requests at 1-second intervals.
+ *  In the case of a failure due to bus reset, we want to make sure that we retry after the allowable
+ *  reset time.  For SCSI, the allowable reset time is 5 seconds.  ScsiPort queues requests during
+ *  a bus reset, which should cause us to retry after the reset is over; but the requests queued in
+ *  the miniport are failed all the way back to us immediately.  In any event, in order to make
+ *  extra sure that our retries span the allowable reset time, we should retry more than 5 times.
+ */
+//#define NUM_IO_RETRIES      8
+
+#define CDROM_VOLUME_VERIFY_CHECKED        0x34
+
+#define CDROM_TAG_PRIVATE_DATA             'CPcS'
+
+typedef struct _MEDIA_CHANGE_DETECTION_INFO {
+
+    // Use AN if supported so that we don't need to poll for media change notifications.
+    BOOLEAN     AsynchronousNotificationSupported;
+
+    // If AN is turned on and we received an AN signal when the device is exclusively locked,
+    // we should take a note and send down a GESN when the lock is lifted, since otherwise
+    // we will lose the signal. Even worse, some ODD models will signal the event again next
+    // time a SYNC CACHE is sent down, and processing the event in that arbitrary timing may
+    // cause unexpected behaviors.
+    BOOLEAN     ANSignalPendingDueToExclusiveLock;
+
+    // Mutex to synchronize enable/disable requests and media state changes
+    KMUTEX      MediaChangeMutex;
+
+    // For request erialization use.
+    // This irp is used in timer callback routine, will be sent to the device itself.
+    // so that the sequentail queue will get request wrapped on this irp. After doing
+    // the real work, this irp will be completed to indicate it's ok to send the next
+    // MCN request.
+    PIRP        MediaChangeSyncIrp;
+
+    // The last known state of the media (present, not present, unknown).
+    // Protected by MediaChangeMutex.
+    MEDIA_CHANGE_DETECTION_STATE LastKnownMediaDetectionState;
+
+    // The last state of the media (present, not present, unknown) reported to apps
+    // via notifications. Protected by MediaChangeMutex
+    MEDIA_CHANGE_DETECTION_STATE LastReportedMediaDetectionState;
+
+    // This is a count of how many time MCD has been disabled.  if it is
+    // set to zero, then we'll poll the device for MCN events with the
+    // then-current method (ie. TEST UNIT READY or GESN).  this is
+    // protected by MediaChangeMutex
+    LONG        MediaChangeDetectionDisableCount;
+
+    // recent changes allowed instant retries of the MCN REQUEST.  Since this
+    // could cause an infinite loop, keep a count of how many times we've
+    // retried immediately so that we can catch if the count exceeds an
+    // arbitrary limit.
+    LONG        MediaChangeRetryCount;
+
+    // use GESN if it's available
+    struct {
+        BOOLEAN Supported;
+        BOOLEAN HackEventMask;
+        UCHAR   EventMask;
+        UCHAR   NoChangeEventMask;
+        PUCHAR  Buffer;
+        PMDL    Mdl;
+        ULONG   BufferSize;
+    } Gesn;
+
+    // If this value is one, then the REQUEST is currently in use.
+    // If this value is zero, then the REQUEST is available.
+    // Use InterlockedCompareExchange() to set from "available" to "in use".
+    // ASSERT that InterlockedCompareExchange() showed previous value of
+    //    "in use" when changing back to "available" state.
+    // This also implicitly protects the MediaChangeSrb and SenseBuffer
+    //
+    // This protect is needed in the polling case since the timer is fired asynchronous
+    // to our sequential I/O queues, and when the timer is fired, our Sync Irp could be
+    // still using the MediaChangeRequest. However, if AN is used, then each interrupt
+    // will cause us to queue a request, so interrupts are serialized, and within the
+    // handling of each request, we already sequentially use the MediaChangeRequest if
+    // we need retries. We still use it in case of AN to be consistent with polling, but
+    // if in the future we remove polling altogether, then we don't need this field anymore.
+    LONG                MediaChangeRequestInUse;
+
+    // Pointer to the REQUEST to be used for media change detection.
+    // protected by Interlocked MediaChangeRequestInUse
+    WDFREQUEST          MediaChangeRequest;
+
+    // The srb for the media change detection.
+    // protected by Interlocked MediaChangeIrpInUse
+    SCSI_REQUEST_BLOCK  MediaChangeSrb;
+    PUCHAR              SenseBuffer;
+    ULONG               SrbFlags;
+
+    // Handle to the display state notification registration.
+    PVOID               DisplayStateCallbackHandle;
+
+} MEDIA_CHANGE_DETECTION_INFO, *PMEDIA_CHANGE_DETECTION_INFO;
+
+#define DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS        (60 * 1000)
+#define DELAY_TIME_TO_ENTER_AOAC_IDLE_POWER_IN_MS   (10 * 1000)
+#define BECOMING_READY_RETRY_COUNT                  (15)
+#define BECOMING_READY_RETRY_INTERNVAL_IN_100NS     (2 * 1000 * 1000)
+
+typedef struct _ZERO_POWER_ODD_INFO {
+
+    UCHAR                       LoadingMechanism;               // From Removable Medium Feature Descriptor
+    UCHAR                       Load;                           // From Removable Medium Feature Descriptor
+
+    D3COLD_SUPPORT_INTERFACE    D3ColdInterface;                // D3Cold interface from ACPI
+
+    BOOLEAN                     Reserved;                       // Reserved
+    BOOLEAN                     InZeroPowerState;               // Is device currently in Zero Power State
+    BOOLEAN                     RetryFirstCommand;              // First command after power resume should be retried
+    BOOLEAN                     MonitorStartStopUnit;           // If 1. drawer 2. soft eject 3. no media 4. Immed=0,
+                                                                // we will not receive any GESN events, and so we should
+                                                                // monitor the command to get notified
+    ULONG                       BecomingReadyRetryCount;        // How many times we should retry for becoming ready
+
+    UCHAR                       SenseKey;                       // sense code from TUR to check media & tray status
+    UCHAR                       AdditionalSenseCode;            // sense code from TUR to check media & tray status
+    UCHAR                       AdditionalSenseCodeQualifier;   // sense code from TUR to check media & tray status
+
+    PGET_CONFIGURATION_HEADER   GetConfigurationBuffer;         // Cached Get Configuration response from device
+    ULONG                       GetConfigurationBufferSize;     // Size of the above buffer
+
+} ZERO_POWER_ODD_INFO, *PZERO_POWER_ODD_INFO;
+
+typedef enum {
+    SimpleMediaLock,
+    SecureMediaLock,
+    InternalMediaLock
+} MEDIA_LOCK_TYPE, *PMEDIA_LOCK_TYPE;
+
+
+typedef enum _CDROM_DETECTION_STATE {
+    CdromDetectionUnknown = 0,
+    CdromDetectionUnsupported = 1,
+    CdromDetectionSupported = 2
+} CDROM_DETECTION_STATE, *PCDROM_DETECTION_STATE;
+
+
+typedef struct _CDROM_ERROR_LOG_DATA {
+    LARGE_INTEGER       TickCount;          // Offset 0x00
+    ULONG               PortNumber;         // Offset 0x08
+
+    UCHAR               ErrorPaging    : 1; // Offset 0x0c
+    UCHAR               ErrorRetried   : 1;
+    UCHAR               ErrorUnhandled : 1;
+    UCHAR               ErrorReserved  : 5;
+
+    UCHAR               Reserved[3];
+
+    SCSI_REQUEST_BLOCK  Srb;                // Offset 0x10
+
+    //  We define the SenseData as the default length.
+    //  Since the sense data returned by the port driver may be longer,
+    //  SenseData must be at the end of this structure.
+    //  For our internal error log, we only log the default length.
+    SENSE_DATA          SenseData;     // Offset 0x50 for x86 (or 0x68 for ia64) (ULONG32 Alignment required!)
+} CDROM_ERROR_LOG_DATA, *PCDROM_ERROR_LOG_DATA;
+
+
+#define NUM_ERROR_LOG_ENTRIES       16
+
+//
+// add to the front of this structure to help prevent illegal
+// snooping by other utilities.
+//
+typedef struct _CDROM_PRIVATE_FDO_DATA {
+
+    // this private structure allows us to
+    // dynamically re-enable the perf benefits
+    // lost due to transient error conditions.
+    // in w2k, a reboot was required. :(
+    struct {
+        ULONG      OriginalSrbFlags;
+        ULONG      SuccessfulIO;
+        ULONG      ReEnableThreshhold; // 0 means never
+    } Perf;
+
+    ULONG_PTR            HackFlags;
+
+    STORAGE_HOTPLUG_INFO HotplugInfo;
+
+    BOOLEAN              TimerStarted;
+    BOOLEAN              LoggedTURFailureSinceLastIO;
+    BOOLEAN              LoggedSYNCFailure;
+
+    // not use WDFSPINLOCK to avoid exposing private object creation
+    // in initialization code. (cdrom.sys was in WDK example)
+    KSPIN_LOCK           SpinLock;
+
+    // Circular array of timestamped logs of errors that occurred on this device.
+    ULONG                ErrorLogNextIndex;
+    CDROM_ERROR_LOG_DATA ErrorLogs[NUM_ERROR_LOG_ENTRIES];
+
+}CDROM_PRIVATE_FDO_DATA, *PCDROM_PRIVATE_FDO_DATA;
+
+//
+// this is a private structure, but must be kept here
+// to properly compile size of FUNCTIONAL_DEVICE_EXTENSION
+//
+typedef struct _FILE_OBJECT_CONTEXT {
+    WDFFILEOBJECT   FileObject;
+    WDFDEVICE       DeviceObject;
+    ULONG           LockCount;
+    ULONG           McnDisableCount;
+    BOOLEAN         EnforceStreamingRead;
+    BOOLEAN         EnforceStreamingWrite;
+} FILE_OBJECT_CONTEXT, *PFILE_OBJECT_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILE_OBJECT_CONTEXT, FileObjectGetContext)
+
+
+#define NOT_READY_RETRY_INTERVAL    10
+#define MODE_PAGE_DATA_SIZE         192
+
+//
+// per session device
+//
+#define INVALID_SESSION         ((ULONG)-1)
+
+#endif // __CDROMP_H__
diff --git a/drivers/storage/class/cdrom_new/common.c b/drivers/storage/class/cdrom_new/common.c
new file mode 100644 (file)
index 0000000..05fb635
--- /dev/null
@@ -0,0 +1,3878 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    common.c
+
+Abstract:
+
+    shared private routines for cdrom.sys
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "ntstrsafe.h"
+
+#include "cdrom.h"
+#include "scratch.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "common.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceGetParameter)
+#pragma alloc_text(PAGE, DeviceSetParameter)
+#pragma alloc_text(PAGE, DeviceSendSrbSynchronously)
+#pragma alloc_text(PAGE, DevicePickDvdRegion)
+#pragma alloc_text(PAGE, StringsAreMatched)
+#pragma alloc_text(PAGE, PerformEjectionControl)
+#pragma alloc_text(PAGE, DeviceFindFeaturePage)
+#pragma alloc_text(PAGE, DevicePrintAllFeaturePages)
+#pragma alloc_text(PAGE, DeviceRegisterInterface)
+#pragma alloc_text(PAGE, DeviceRestoreDefaultSpeed)
+#pragma alloc_text(PAGE, DeviceSendRequestSynchronously)
+#pragma alloc_text(PAGE, MediaReadCapacity)
+#pragma alloc_text(PAGE, MediaReadCapacityDataInterpret)
+#pragma alloc_text(PAGE, DeviceRetrieveModeSenseUsingScratch)
+#pragma alloc_text(PAGE, ModeSenseFindSpecificPage)
+#pragma alloc_text(PAGE, DeviceUnlockExclusive)
+
+#endif
+
+LPCSTR LockTypeStrings[] = {"Simple",
+                            "Secure",
+                            "Internal"
+                            };
+
+VOID
+RequestSetReceivedTime(
+    _In_ WDFREQUEST Request
+    )
+{
+    PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+    LARGE_INTEGER          temp;
+
+    KeQueryTickCount(&temp);
+
+    requestContext->TimeReceived = temp;
+
+    return;
+}
+
+VOID
+RequestSetSentTime(
+    _In_ WDFREQUEST Request
+    )
+{
+    PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+    LARGE_INTEGER          temp;
+
+    KeQueryTickCount(&temp);
+
+    if (requestContext->TimeSentDownFirstTime.QuadPart == 0)
+    {
+        requestContext->TimeSentDownFirstTime = temp;
+    }
+
+    requestContext->TimeSentDownLasttTime = temp;
+
+    if (requestContext->OriginalRequest != NULL)
+    {
+        PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(requestContext->OriginalRequest);
+
+        if (originalRequestContext->TimeSentDownFirstTime.QuadPart == 0)
+        {
+            originalRequestContext->TimeSentDownFirstTime = temp;
+        }
+
+        originalRequestContext->TimeSentDownLasttTime = temp;
+    }
+
+    return;
+}
+
+VOID
+RequestClearSendTime(
+    _In_ WDFREQUEST Request
+    )
+/*
+Routine Description:
+
+    This function is used to clean SentTime fields in reusable request context.
+
+Arguments:
+    Request -
+
+Return Value:
+    N/A
+
+*/
+{
+    PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+
+    requestContext->TimeSentDownFirstTime.QuadPart = 0;
+    requestContext->TimeSentDownLasttTime.QuadPart = 0;
+
+    return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceGetParameter(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_opt_ PWSTR                  SubkeyName,
+    _In_ PWSTR                      ParameterName,
+    _Inout_ PULONG                  ParameterValue  // also default value
+    )
+/*++
+Routine Description:
+
+    retrieve device parameter from registry.
+
+Arguments:
+
+    DeviceExtension - device context.
+
+    SubkeyName - name of subkey
+
+    ParameterName - the registry parameter to be retrieved
+
+Return Value:
+
+    ParameterValue - registry value retrieved
+
+--*/
+{
+    NTSTATUS        status;
+    WDFKEY          rootKey = NULL;
+    WDFKEY          subKey = NULL;
+    UNICODE_STRING  registrySubKeyName;
+    UNICODE_STRING  registryValueName;
+    ULONG           defaultParameterValue;
+
+    PAGED_CODE();
+
+    RtlInitUnicodeString(&registryValueName, ParameterName);
+
+    if (SubkeyName != NULL)
+    {
+        RtlInitUnicodeString(&registrySubKeyName, SubkeyName);
+    }
+
+    // open the hardware key
+    status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
+                                      PLUGPLAY_REGKEY_DEVICE,
+                                      KEY_READ,
+                                      WDF_NO_OBJECT_ATTRIBUTES,
+                                      &rootKey);
+
+    // open the sub key
+    if (NT_SUCCESS(status) && (SubkeyName != NULL))
+    {
+        status = WdfRegistryOpenKey(rootKey,
+                                    &registrySubKeyName,
+                                    KEY_READ,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    &subKey);
+
+        if (!NT_SUCCESS(status))
+        {
+            WdfRegistryClose(rootKey);
+            rootKey = NULL;
+        }
+    }
+
+    if (NT_SUCCESS(status) && (rootKey != NULL))
+    {
+        defaultParameterValue = *ParameterValue;
+
+        status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
+                                       &registryValueName,
+                                       ParameterValue);
+
+        if (!NT_SUCCESS(status))
+        {
+            *ParameterValue = defaultParameterValue; // use default value
+        }
+    }
+
+    // close what we open
+    if (subKey != NULL)
+    {
+        WdfRegistryClose(subKey);
+        subKey = NULL;
+    }
+
+    if (rootKey != NULL)
+    {
+        WdfRegistryClose(rootKey);
+        rootKey = NULL;
+    }
+
+    // Windows 2000 SP3 uses the driver-specific key, so look in there
+    if (!NT_SUCCESS(status))
+    {
+        // open the software key
+        status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
+                                          PLUGPLAY_REGKEY_DRIVER,
+                                          KEY_READ,
+                                          WDF_NO_OBJECT_ATTRIBUTES,
+                                          &rootKey);
+
+        // open the sub key
+        if (NT_SUCCESS(status) && (SubkeyName != NULL))
+        {
+            status = WdfRegistryOpenKey(rootKey,
+                                        &registrySubKeyName,
+                                        KEY_READ,
+                                        WDF_NO_OBJECT_ATTRIBUTES,
+                                        &subKey);
+
+            if (!NT_SUCCESS(status))
+            {
+                WdfRegistryClose(rootKey);
+                rootKey = NULL;
+            }
+        }
+
+        if (NT_SUCCESS(status) && (rootKey != NULL))
+        {
+            defaultParameterValue = *ParameterValue;
+
+            status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
+                                           &registryValueName,
+                                           ParameterValue);
+
+            if (!NT_SUCCESS(status))
+            {
+                *ParameterValue = defaultParameterValue; // use default value
+            }
+            else
+            {
+                // Migrate the value over to the device-specific key
+                DeviceSetParameter(DeviceExtension, SubkeyName, ParameterName, *ParameterValue);
+            }
+        }
+
+        // close what we open
+        if (subKey != NULL)
+        {
+            WdfRegistryClose(subKey);
+            subKey = NULL;
+        }
+
+        if (rootKey != NULL)
+        {
+            WdfRegistryClose(rootKey);
+            rootKey = NULL;
+        }
+    }
+
+    return;
+
+} // end DeviceetParameter()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSetParameter(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_opt_z_ PWSTR                SubkeyName,
+    _In_ PWSTR                      ParameterName,
+    _In_ ULONG                      ParameterValue
+    )
+/*++
+Routine Description:
+
+    set parameter to registry.
+
+Arguments:
+
+    DeviceExtension - device context.
+
+    SubkeyName - name of subkey
+
+    ParameterName - the registry parameter to be retrieved
+
+    ParameterValue - registry value to be set
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status;
+    WDFKEY          rootKey = NULL;
+    WDFKEY          subKey = NULL;
+    UNICODE_STRING  registrySubKeyName;
+    UNICODE_STRING  registryValueName;
+
+    PAGED_CODE();
+
+    RtlInitUnicodeString(&registryValueName, ParameterName);
+
+    if (SubkeyName != NULL)
+    {
+        RtlInitUnicodeString(&registrySubKeyName, SubkeyName);
+    }
+
+    // open the hardware key
+    status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
+                                      PLUGPLAY_REGKEY_DEVICE,
+                                      KEY_READ | KEY_WRITE,
+                                      WDF_NO_OBJECT_ATTRIBUTES,
+                                      &rootKey);
+
+    // open the sub key
+    if (NT_SUCCESS(status) && (SubkeyName != NULL))
+    {
+        status = WdfRegistryOpenKey(rootKey,
+                                    &registrySubKeyName,
+                                    KEY_READ | KEY_WRITE,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    &subKey);
+
+        if (!NT_SUCCESS(status))
+        {
+            WdfRegistryClose(rootKey);
+            rootKey = NULL;
+        }
+    }
+
+    if (NT_SUCCESS(status) && (rootKey != NULL))
+    {
+        status = WdfRegistryAssignULong((subKey != NULL) ? subKey : rootKey,
+                                        &registryValueName,
+                                        ParameterValue);
+    }
+
+    // close what we open
+    if (subKey != NULL)
+    {
+        WdfRegistryClose(subKey);
+        subKey = NULL;
+    }
+
+    if (rootKey != NULL)
+    {
+        WdfRegistryClose(rootKey);
+        rootKey = NULL;
+    }
+
+    return status;
+
+} // end DeviceSetParameter()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceSendRequestSynchronously(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request,
+    _In_ BOOLEAN      RequestFormated
+    )
+/*++
+Routine Description:
+
+    send a request to lower driver synchronously.
+
+Arguments:
+
+    Device - device object.
+
+    Request - request object
+
+    RequestFormated - if the request is already formatted, will no do it in this function
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    BOOLEAN                     requestCancelled = FALSE;
+    PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
+
+    PAGED_CODE();
+
+    if (!RequestFormated)
+    {
+        // set request up for sending down
+        WdfRequestFormatRequestUsingCurrentType(Request);
+    }
+
+    // get cancellation status for the original request
+    if (requestContext->OriginalRequest != NULL)
+    {
+        requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
+    }
+
+    if (!requestCancelled)
+    {
+        status = RequestSend(deviceExtension,
+                             Request,
+                             deviceExtension->IoTarget,
+                             WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+                             NULL);
+    }
+    else
+    {
+        status = STATUS_CANCELLED;
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendSrbSynchronously(
+    _In_ WDFDEVICE           Device,
+    _In_ PSCSI_REQUEST_BLOCK Srb,
+    _In_opt_ PVOID           BufferAddress,
+    _In_ ULONG               BufferLength,
+    _In_ BOOLEAN             WriteToDevice,
+    _In_opt_ WDFREQUEST      OriginalRequest
+    )
+/*++
+Routine Description:
+
+    Send a SRB structure to lower driver synchronously.
+
+    Process of this function:
+    1. Allocate SenseBuffer; Create Request; Allocate MDL
+    2. Do following loop if necessary
+       2.1 Reuse Request
+       2.2 Format Srb, Irp
+       2.3 Send Request
+       2.4 Error Intepret and retry decision making.
+    3. Release all allocated resosurces.
+
+Arguments:
+
+    Device - device object.
+
+    Request - request object
+
+    RequestFormated - if the request is already formatted, will no do it in this function
+
+Return Value:
+    NTSTATUS
+
+NOTE:
+The caller needs to setup following fields before calling this routine.
+        srb.CdbLength
+        srb.TimeOutValue
+        cdb
+
+BufferLength and WriteToDevice to control the data direction of the device
+    BufferLength = 0: No data transfer
+    BufferLenth != 0 && !WriteToDevice: get data from device
+    BufferLenth != 0 && WriteToDevice: send data to device
+--*/
+{
+    NTSTATUS                status;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_PRIVATE_FDO_DATA fdoData = deviceExtension->PrivateFdoData;
+    PUCHAR                  senseInfoBuffer = NULL;
+    ULONG                   retryCount = 0;
+    BOOLEAN                 retry = FALSE;
+    ULONG                   ioctlCode = 0;
+    WDFREQUEST              request = NULL;
+    PIRP                    irp = NULL;
+    PIO_STACK_LOCATION      nextStack = NULL;
+    PMDL                    mdlAddress = NULL;
+    BOOLEAN                 memoryLocked = FALSE;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+    PZERO_POWER_ODD_INFO    zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+
+    PAGED_CODE();
+
+    // NOTE: This code is only pagable because we are not freezing
+    //       the queue.  Allowing the queue to be frozen from a pagable
+    //       routine could leave the queue frozen as we try to page in
+    //       the code to unfreeze the queue.  The result would be a nice
+    //       case of deadlock.  Therefore, since we are unfreezing the
+    //       queue regardless of the result, just set the NO_FREEZE_QUEUE
+    //       flag in the SRB.
+    NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+    //1. allocate SenseBuffer and initiate Srb common fields
+    //   these fields will not be changed by lower driver.
+    {
+        // Write length to SRB.
+        Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+        Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+        // Sense buffer is in aligned nonpaged pool.
+        senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                                SENSE_BUFFER_SIZE,
+                                                CDROM_TAG_SENSE_INFO);
+
+        if (senseInfoBuffer == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "DeviceSendSrbSynchronously: Can't allocate MDL\n"));
+
+            goto Exit;
+        }
+
+        Srb->SenseInfoBuffer = senseInfoBuffer;
+        Srb->DataBuffer = BufferAddress;
+
+        // set timeout value to default value if it's not specifically set by caller.
+        if (Srb->TimeOutValue == 0)
+        {
+            Srb->TimeOutValue = deviceExtension->TimeOutValue;
+        }
+    }
+
+    //2. Create Request object
+    {
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                                CDROM_REQUEST_CONTEXT);
+
+        status = WdfRequestCreate(&attributes,
+                                  deviceExtension->IoTarget,
+                                  &request);
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "DeviceSendSrbSynchronously: Can't create request: %lx\n",
+                       status));
+
+            goto Exit;
+        }
+
+        irp = WdfRequestWdmGetIrp(request);
+    }
+
+    // 3. Build an MDL for the data buffer and stick it into the irp.
+    if (BufferAddress != NULL)
+    {
+        mdlAddress = IoAllocateMdl( BufferAddress,
+                                    BufferLength,
+                                    FALSE,
+                                    FALSE,
+                                    irp );
+        if (mdlAddress == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "DeviceSendSrbSynchronously: Can't allocate MDL\n"));
+
+            goto Exit;
+        }
+
+        _SEH2_TRY
+        {
+            MmProbeAndLockPages(mdlAddress,
+                                KernelMode,
+                                (WriteToDevice ? IoReadAccess : IoWriteAccess));
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            status = _SEH2_GetExceptionCode();
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "DeviceSendSrbSynchronously: Exception %lx locking buffer\n", status));
+
+            _SEH2_YIELD(goto Exit);
+        }
+        _SEH2_END;
+
+        memoryLocked = TRUE;
+    }
+
+    // 4. Format Srb, Irp; Send request and retry when necessary
+    do
+    {
+        // clear the control variable.
+        retry = FALSE;
+
+        // 4.1 reuse the request object; set originalRequest field.
+        {
+            WDF_REQUEST_REUSE_PARAMS    params;
+            PCDROM_REQUEST_CONTEXT      requestContext = NULL;
+
+            // deassign the MdlAddress, this is the value we assign explicitly.
+            // doing this can prevent WdfRequestReuse to release the Mdl unexpectly.
+            if (irp->MdlAddress)
+            {
+                irp->MdlAddress = NULL;
+            }
+
+            WDF_REQUEST_REUSE_PARAMS_INIT(&params,
+                                          WDF_REQUEST_REUSE_NO_FLAGS,
+                                          STATUS_SUCCESS);
+
+            status = WdfRequestReuse(request, &params);
+
+            if (!NT_SUCCESS(status))
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                           "DeviceSendSrbSynchronously: WdfRequestReuse failed, %!STATUS!\n",
+                           status));
+                // exit the loop.
+                break;
+            }
+
+            // WDF requests to format the request befor sending it
+            status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
+                                                                    request,
+                                                                    ioctlCode,
+                                                                    NULL, NULL,
+                                                                    NULL, NULL,
+                                                                    NULL, NULL);
+
+            if (!NT_SUCCESS(status))
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                           "DeviceSendSrbSynchronously: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
+                           status));
+                // exit the loop.
+                break;
+            }
+
+            requestContext = RequestGetContext(request);
+            requestContext->OriginalRequest = OriginalRequest;
+        }
+
+        // 4.2 Format Srb and Irp
+        {
+            Srb->OriginalRequest = irp;
+            Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+            Srb->DataTransferLength = BufferLength;
+            Srb->SrbFlags = deviceExtension->SrbFlags;
+
+            // Disable synchronous transfer for these requests.
+            SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+            SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+            if (BufferAddress != NULL)
+            {
+                if (WriteToDevice)
+                {
+                    SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+                    ioctlCode = IOCTL_SCSI_EXECUTE_OUT;
+                }
+                else
+                {
+                    SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
+                    ioctlCode = IOCTL_SCSI_EXECUTE_IN;
+                }
+            }
+            else
+            {
+                ioctlCode = IOCTL_SCSI_EXECUTE_NONE;
+            }
+
+
+            // Zero out status.
+            Srb->ScsiStatus = 0;
+            Srb->SrbStatus = 0;
+            Srb->NextSrb = NULL;
+
+            // irp related fields
+            irp->MdlAddress = mdlAddress;
+
+            nextStack = IoGetNextIrpStackLocation(irp);
+
+            nextStack->MajorFunction = IRP_MJ_SCSI;
+            nextStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
+            nextStack->Parameters.Scsi.Srb = Srb;
+        }
+
+        // 4.3 send Request to lower driver.
+        status = DeviceSendRequestSynchronously(Device, request, TRUE);
+
+        if (status != STATUS_CANCELLED)
+        {
+            NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
+            NT_ASSERT(status != STATUS_PENDING);
+            NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
+
+            // 4.4 error process.
+            if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
+            {
+                LONGLONG    retryIntervalIn100ns = 0;
+
+                // Update status and determine if request should be retried.
+                retry = RequestSenseInfoInterpret(deviceExtension,
+                                                  request,
+                                                  Srb,
+                                                  retryCount,
+                                                  &status,
+                                                  &retryIntervalIn100ns);
+
+                if (retry)
+                {
+                    LARGE_INTEGER t;
+                    t.QuadPart = -retryIntervalIn100ns;
+                    retryCount++;
+                    KeDelayExecutionThread(KernelMode, FALSE, &t);
+                }
+            }
+            else
+            {
+                // Request succeeded.
+                fdoData->LoggedTURFailureSinceLastIO = FALSE;
+                status = STATUS_SUCCESS;
+                retry = FALSE;
+            }
+        }
+    } while(retry);
+
+    if ((zpoddInfo != NULL) &&
+        (zpoddInfo->MonitorStartStopUnit != FALSE) &&
+        (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceSendSrbSynchronously: soft eject detected, device marked as active\n"));
+
+        DeviceMarkActive(deviceExtension, TRUE, FALSE);
+    }
+
+    // 5. Release all allocated resources.
+
+    // required even though we allocated our own, since the port driver may
+    // have allocated one also
+    if (PORT_ALLOCATED_SENSE(deviceExtension, Srb))
+    {
+        FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, Srb);
+    }
+
+Exit:
+
+    if (senseInfoBuffer != NULL)
+    {
+        FREE_POOL(senseInfoBuffer);
+    }
+
+    Srb->SenseInfoBuffer = NULL;
+    Srb->SenseInfoBufferLength = 0;
+
+    if (mdlAddress)
+    {
+        if (memoryLocked)
+        {
+            MmUnlockPages(mdlAddress);
+            memoryLocked = FALSE;
+        }
+
+        IoFreeMdl(mdlAddress);
+        irp->MdlAddress = NULL;
+    }
+
+    if (request)
+    {
+        WdfObjectDelete(request);
+    }
+
+    return status;
+}
+
+
+VOID
+DeviceSendNotification(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ const GUID*              Guid,
+    _In_ ULONG                    ExtraDataSize,
+    _In_opt_ PVOID                ExtraData
+    )
+/*++
+Routine Description:
+
+    send notification to other components
+
+Arguments:
+
+    DeviceExtension - device context.
+
+    Guid - GUID for the notification
+
+    ExtraDataSize - data size along with notification
+
+    ExtraData - data buffer send with notification
+
+Return Value:
+    None
+
+--*/
+{
+    PTARGET_DEVICE_CUSTOM_NOTIFICATION  notification;
+    ULONG                               requiredSize;
+    NTSTATUS                            status;
+
+    status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
+                         ExtraDataSize,
+                         &requiredSize);
+
+    if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff))
+    {
+        // MAX_USHORT, max total size for these events!
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+                   "Error sending event: size too large! (%x)\n",
+                   requiredSize));
+        return;
+    }
+
+    notification = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                         requiredSize,
+                                         CDROM_TAG_NOTIFICATION);
+
+    // if none allocated, exit
+    if (notification == NULL)
+    {
+        return;
+    }
+
+    // Prepare and send the request!
+    RtlZeroMemory(notification, requiredSize);
+    notification->Version = 1;
+    notification->Size = (USHORT)(requiredSize);
+    notification->FileObject = NULL;
+    notification->NameBufferOffset = -1;
+    notification->Event = *Guid;
+
+    if (ExtraData != NULL)
+    {
+        RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
+    }
+
+    IoReportTargetDeviceChangeAsynchronous(DeviceExtension->LowerPdo,
+                                           notification,
+                                           NULL,
+                                           NULL);
+
+    FREE_POOL(notification);
+
+    return;
+}
+
+
+VOID
+DeviceSendStartUnit(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+    Send command to SCSI unit to start or power up.
+    Because this command is issued asynchronounsly, that is, without
+    waiting on it to complete, the IMMEDIATE flag is not set. This
+    means that the CDB will not return until the drive has powered up.
+    This should keep subsequent requests from being submitted to the
+    device before it has completely spun up.
+
+    This routine is called from the InterpretSense routine, when a
+    request sense returns data indicating that a drive must be
+    powered up.
+
+    This routine may also be called from a class driver's error handler,
+    or anytime a non-critical start device should be sent to the device.
+
+Arguments:
+
+    Device - The device object.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+    WDFREQUEST              startUnitRequest = NULL;
+    WDFMEMORY               inputMemory = NULL;
+
+    PCOMPLETION_CONTEXT     context = NULL;
+    PSCSI_REQUEST_BLOCK     srb = NULL;
+    PCDB                    cdb = NULL;
+
+    deviceExtension = DeviceGetExtension(Device);
+
+    if (NT_SUCCESS(status))
+    {
+        // Allocate Srb from nonpaged pool.
+        context = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                        sizeof(COMPLETION_CONTEXT),
+                                        CDROM_TAG_COMPLETION_CONTEXT);
+
+        if (context == NULL)
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "DeviceSendStartUnit: Failed to allocate completion context\n"));
+
+            status = STATUS_INTERNAL_ERROR;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Save the device object in the context for use by the completion
+        // routine.
+        context->Device = Device;
+        srb = &context->Srb;
+
+        // Zero out srb.
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        // setup SRB structure.
+        srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->TimeOutValue = START_UNIT_TIMEOUT;
+
+        srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+                        SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+        // setup CDB
+        srb->CdbLength = 6;
+        cdb = (PCDB)srb->Cdb;
+
+        cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+        cdb->START_STOP.Start = 1;
+        cdb->START_STOP.Immediate = 0;
+        cdb->START_STOP.LogicalUnitNumber = srb->Lun;
+
+        //Create Request for sending down to port driver
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                                CDROM_REQUEST_CONTEXT);
+        attributes.ParentObject = deviceExtension->IoTarget;
+
+        status = WdfRequestCreate(&attributes,
+                                  deviceExtension->IoTarget,
+                                  &startUnitRequest);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        srb->OriginalRequest = WdfRequestWdmGetIrp(startUnitRequest);
+        NT_ASSERT(srb->OriginalRequest != NULL);
+
+        //Prepare the request
+        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+        attributes.ParentObject = startUnitRequest;
+
+        status = WdfMemoryCreatePreallocated(&attributes,
+                                             (PVOID)srb,
+                                             sizeof(SCSI_REQUEST_BLOCK),
+                                             &inputMemory);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
+                                                                startUnitRequest,
+                                                                IOCTL_SCSI_EXECUTE_NONE,
+                                                                inputMemory,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Set a CompletionRoutine callback function.
+        WdfRequestSetCompletionRoutine(startUnitRequest,
+                                       DeviceAsynchronousCompletion,
+                                       context);
+
+        status = RequestSend(deviceExtension,
+                             startUnitRequest,
+                             deviceExtension->IoTarget,
+                             0,
+                             NULL);
+    }
+
+    // release resources when failed.
+    if (!NT_SUCCESS(status))
+    {
+        FREE_POOL(context);
+        if (startUnitRequest != NULL)
+        {
+            WdfObjectDelete(startUnitRequest);
+        }
+    }
+
+    return;
+} // end StartUnit()
+
+
+VOID
+DeviceSendIoctlAsynchronously(
+    _In_ PCDROM_DEVICE_EXTENSION    DeviceExtension,
+    _In_ ULONG                      IoControlCode,
+    _In_ PDEVICE_OBJECT             TargetDeviceObject
+    )
+/*++
+
+Routine Description:
+
+    Send an IOCTL asynchronously
+
+Arguments:
+
+    DeviceExtension - device context.
+    IoControlCode - IOCTL code.
+    TargetDeviceObject - target device object.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  nextIrpStack = NULL;
+
+    irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE);
+
+    if (irp != NULL)
+    {
+        nextIrpStack = IoGetNextIrpStackLocation(irp);
+
+        nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+
+        nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
+        nextIrpStack->Parameters.DeviceIoControl.InputBufferLength = 0;
+        nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
+        nextIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
+
+        IoSetCompletionRoutine(irp,
+                               RequestAsynchronousIrpCompletion,
+                               DeviceExtension,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        (VOID) IoCallDriver(TargetDeviceObject, irp);
+    }
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestAsynchronousIrpCompletion(
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PIRP Irp,
+    _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
+    )
+/*++
+
+Routine Description:
+
+    Free the Irp.
+
+Arguments:
+
+    DeviceObject - device that the completion routine fires on.
+
+    Irp - The irp to be completed.
+
+    Context - IRP context
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Context);
+
+    IoFreeIrp(Irp);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceAsynchronousCompletion(
+    _In_ WDFREQUEST                       Request,
+    _In_ WDFIOTARGET                      Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS   Params,
+    _In_ WDFCONTEXT                       Context
+    )
+/*++
+
+Routine Description:
+
+    This routine is called when an asynchronous I/O request
+    which was issused by the class driver completes.  Examples of such requests
+    are release queue or START UNIT. This routine releases the queue if
+    necessary.  It then frees the context and the IRP.
+
+Arguments:
+
+    DeviceObject - The device object for the logical unit; however since this
+        is the top stack location the value is NULL.
+
+    Irp - Supplies a pointer to the Irp to be processed.
+
+    Context - Supplies the context to be used to process this request.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PCOMPLETION_CONTEXT     context = (PCOMPLETION_CONTEXT)Context;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(context->Device);
+
+    UNREFERENCED_PARAMETER(Target);
+    UNREFERENCED_PARAMETER(Params);
+
+    // If this is an execute srb, then check the return status and make sure.
+    // the queue is not frozen.
+    if (context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI)
+    {
+        // Check for a frozen queue.
+        if (context->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
+        {
+            // Unfreeze the queue getting the device object from the context.
+            DeviceReleaseQueue(context->Device);
+        }
+    }
+
+    // free port-allocated sense buffer if we can detect
+    //
+    if (PORT_ALLOCATED_SENSE(deviceExtension, &context->Srb))
+    {
+        FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, &context->Srb);
+    }
+
+    FREE_POOL(context);
+
+    WdfObjectDelete(Request);
+
+} // end DeviceAsynchronousCompletion()
+
+
+VOID
+DeviceReleaseQueue(
+    _In_ WDFDEVICE    Device
+    )
+/*++
+
+Routine Description:
+
+    This routine issues an internal device control command
+    to the port driver to release a frozen queue. The call
+    is issued asynchronously as DeviceReleaseQueue will be invoked
+    from the IO completion DPC (and will have no context to
+    wait for a synchronous call to complete).
+
+    This routine must be called with the remove lock held.
+
+Arguments:
+
+    Device - The functional device object for the device with the frozen queue.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    PSCSI_REQUEST_BLOCK         srb = NULL;
+    KIRQL                       currentIrql;
+
+    // we raise irql seperately so we're not swapped out or suspended
+    // while holding the release queue irp in this routine.  this lets
+    // us release the spin lock before lowering irql.
+    KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
+
+    WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
+
+    if (deviceExtension->ReleaseQueueInProgress)
+    {
+        // Someone is already doing this work - just set the flag to indicate that
+        // we need to release the queue again.
+        deviceExtension->ReleaseQueueNeeded = TRUE;
+        WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
+        KeLowerIrql(currentIrql);
+
+        return;
+    }
+
+    // Mark that there is a release queue in progress and drop the spinlock.
+    deviceExtension->ReleaseQueueInProgress = TRUE;
+
+    WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
+
+    srb = &(deviceExtension->ReleaseQueueSrb);
+
+    // Optical media are removable, so we just flush the queue.  This will also release it.
+    srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
+
+    srb->OriginalRequest = WdfRequestWdmGetIrp(deviceExtension->ReleaseQueueRequest);
+
+    // Set a CompletionRoutine callback function.
+    WdfRequestSetCompletionRoutine(deviceExtension->ReleaseQueueRequest,
+                                   DeviceReleaseQueueCompletion,
+                                   Device);
+    // Send the request. If an error occurs, complete the request.
+    RequestSend(deviceExtension,
+                deviceExtension->ReleaseQueueRequest,
+                deviceExtension->IoTarget,
+                WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE,
+                NULL);
+
+    KeLowerIrql(currentIrql);
+
+    return;
+
+} // end DeviceReleaseQueue()
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceReleaseQueueCompletion(
+    _In_ WDFREQUEST                       Request,
+    _In_ WDFIOTARGET                      Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS   Params,
+    _In_ WDFCONTEXT                       Context
+    )
+/*++
+
+Routine Description:
+
+    This routine is called when an asynchronous release queue request which
+    was issused in DeviceReleaseQueue completes. This routine prepares for
+    the next release queue request and resends it if necessary.
+
+Arguments:
+
+    Request - The completed request.
+
+    Target - IoTarget object
+
+    Params - Completion parameters
+
+    Context - WDFDEVICE object handle.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    NTSTATUS                    status;
+    WDFDEVICE                   device = Context;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(device);
+
+    BOOLEAN                     releaseQueueNeeded = FALSE;
+    WDF_REQUEST_REUSE_PARAMS    params = {0};
+
+    UNREFERENCED_PARAMETER(Target);
+    UNREFERENCED_PARAMETER(Params);
+
+    WDF_REQUEST_REUSE_PARAMS_INIT(&params,
+                                  WDF_REQUEST_REUSE_NO_FLAGS,
+                                  STATUS_SUCCESS);
+
+    // Grab the spinlock and clear the release queue in progress flag so others
+    // can run. Save (and clear) the state of the release queue needed flag
+    // so that we can issue a new release queue outside the spinlock.
+    WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
+
+    releaseQueueNeeded = deviceExtension->ReleaseQueueNeeded;
+
+    deviceExtension->ReleaseQueueNeeded = FALSE;
+    deviceExtension->ReleaseQueueInProgress = FALSE;
+
+    // Reuse the ReleaseQueueRequest for the next time.
+    status = WdfRequestReuse(Request,&params);
+
+    if (NT_SUCCESS(status))
+    {
+        // Preformat the ReleaseQueueRequest for the next time.
+        // This should always succeed because it was already preformatted once during device initialization
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
+                                                                Request,
+                                                                IOCTL_SCSI_EXECUTE_NONE,
+                                                                deviceExtension->ReleaseQueueInputMemory,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                   "DeviceReleaseQueueCompletion: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
+                   status));
+    }
+
+    RequestClearSendTime(Request);
+
+    WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
+
+    // If we need a release queue then issue one now.  Another processor may
+    // have already started one in which case we'll try to issue this one after
+    // it is done - but we should never recurse more than one deep.
+    if (releaseQueueNeeded)
+    {
+        DeviceReleaseQueue(device);
+    }
+
+    return;
+
+} // DeviceReleaseQueueCompletion()
+
+
+//
+// In order to provide better performance without the need to reboot,
+// we need to implement a self-adjusting method to set and clear the
+// srb flags based upon current performance.
+//
+// whenever there is an error, immediately grab the spin lock.  the
+// MP perf hit here is acceptable, since we're in an error path.  this
+// is also neccessary because we are guaranteed to be modifying the
+// SRB flags here, setting SuccessfulIO to zero, and incrementing the
+// actual error count (which is always done within this spinlock).
+//
+// whenever there is no error, increment a counter.  if there have been
+// errors on the device, and we've enabled dynamic perf, *and* we've
+// just crossed the perf threshhold, then grab the spin lock and
+// double check that the threshhold has, indeed been hit(*). then
+// decrement the error count, and if it's dropped sufficiently, undo
+// some of the safety changes made in the SRB flags due to the errors.
+//
+// * this works in all cases.  even if lots of ios occur after the
+//   previous guy went in and cleared the successfulio counter, that
+//   just means that we've hit the threshhold again, and so it's proper
+//   to run the inner loop again.
+//
+
+VOID
+DevicePerfIncrementErrorCount(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+{
+    PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
+    KIRQL                   oldIrql;
+    ULONG                   errors;
+
+    KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+    fdoData->Perf.SuccessfulIO = 0; // implicit interlock
+    errors = InterlockedIncrement((PLONG)&DeviceExtension->ErrorCount);
+
+    if (errors >= CLASS_ERROR_LEVEL_1)
+    {
+        // If the error count has exceeded the error limit, then disable
+        // any tagged queuing, multiple requests per lu queueing
+        // and sychronous data transfers.
+        //
+        // Clearing the no queue freeze flag prevents the port driver
+        // from sending multiple requests per logical unit.
+        CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+        CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+        SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                   "PerfIncrementErrorCount: Too many errors; disabling tagged queuing and "
+                   "synchronous data tranfers.\n"));
+    }
+
+    if (errors >= CLASS_ERROR_LEVEL_2)
+    {
+        // If a second threshold is reached, disable disconnects.
+        SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                   "PerfIncrementErrorCount: Too many errors; disabling disconnects.\n"));
+    }
+
+    KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+    return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+DeviceFindFeaturePage(
+    _In_reads_bytes_(Length) PGET_CONFIGURATION_HEADER   FeatureBuffer,
+    _In_ ULONG const                                Length,
+    _In_ FEATURE_NUMBER const                       Feature
+    )
+/*++
+Routine Description:
+
+    find the specific feature page in the buffer
+
+Arguments:
+
+    FeatureBuffer - buffer contains the device feature set.
+
+    Length - buffer length
+
+    Feature - the feature number looking for.
+
+Return Value:
+
+    PVOID - pointer to the starting location of the specific feature in buffer.
+
+--*/
+{
+    PUCHAR buffer;
+    PUCHAR limit;
+    ULONG  validLength;
+
+    PAGED_CODE();
+
+    if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER))
+    {
+        return NULL;
+    }
+
+    // Calculate the length of valid data available in the
+    // capabilities buffer from the DataLength field
+    REVERSE_BYTES(&validLength, FeatureBuffer->DataLength);
+
+    validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+    // set limit to point to first illegal address
+    limit  = (PUCHAR)FeatureBuffer;
+    limit += min(Length, validLength);
+
+    // set buffer to point to first page
+    buffer = FeatureBuffer->Data;
+
+    // loop through each page until we find the requested one, or
+    // until it's not safe to access the entire feature header
+    // (if equal, have exactly enough for the feature header)
+    while (buffer + sizeof(FEATURE_HEADER) <= limit)
+    {
+        PFEATURE_HEADER header = (PFEATURE_HEADER)buffer;
+        FEATURE_NUMBER  thisFeature;
+
+        thisFeature  = (header->FeatureCode[0] << 8) |
+                       (header->FeatureCode[1]);
+
+        if (thisFeature == Feature)
+        {
+            PUCHAR temp;
+
+            // if don't have enough memory to safely access all the feature
+            // information, return NULL
+            temp = buffer;
+            temp += sizeof(FEATURE_HEADER);
+            temp += header->AdditionalLength;
+
+            if (temp > limit)
+            {
+                // this means the transfer was cut-off, an insufficiently
+                // small buffer was given, or other arbitrary error.  since
+                // it's not safe to view the amount of data (even though
+                // the header is safe) in this feature, pretend it wasn't
+                // transferred at all...
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                           "Feature %x exists, but not safe to access all its data.  returning NULL\n",
+                           Feature));
+                return NULL;
+            }
+            else
+            {
+                return buffer;
+            }
+        }
+
+        if ((header->AdditionalLength % 4) &&
+            !(Feature >= 0xff00 && Feature <= 0xffff))
+        {
+            return NULL;
+        }
+
+        buffer += sizeof(FEATURE_HEADER);
+        buffer += header->AdditionalLength;
+    }
+
+    return NULL;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DevicePrintAllFeaturePages(
+    _In_reads_bytes_(Usable) PGET_CONFIGURATION_HEADER   Buffer,
+    _In_ ULONG const                                Usable
+    )
+/*++
+Routine Description:
+
+    print out all feature pages in the buffer
+
+Arguments:
+
+    Buffer - buffer contains the device feature set.
+
+    Usable -
+
+Return Value:
+
+    none
+
+--*/
+{
+#if DBG
+    PFEATURE_HEADER header;
+
+    PAGED_CODE();
+
+    ////////////////////////////////////////////////////////////////////////////////
+    // items expected to ALWAYS be current if they exist
+    ////////////////////////////////////////////////////////////////////////////////
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureProfileList);
+    if (header != NULL) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: CurrentProfile %x "
+                   "with %x bytes of data at %p\n",
+                   Buffer->CurrentProfile[0] << 8 |
+                   Buffer->CurrentProfile[1],
+                   Usable, Buffer));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCore);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "CORE Features"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureMorphing);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Morphing"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRemovableMedium);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Removable Medium"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeaturePowerManagement);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Power Management"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Embedded Changer"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Microcode Update"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureTimeout);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Timeouts"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "LUN Serial Number"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureFirmwareDate);
+    if (header) {
+
+        ULONG featureSize = header->AdditionalLength;
+        featureSize += RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER, AdditionalLength);
+
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                   "Currently supports" : "Is able to support"),
+                   "Firmware Date"
+                   ));
+
+        if (featureSize >= RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_FIRMWARE_DATE, Minute))
+        {
+            PFEATURE_DATA_FIRMWARE_DATE date = (PFEATURE_DATA_FIRMWARE_DATE)header;
+            // show date as "YYYY/MM/DD hh:mm", which is 18 chars (17+NULL)
+            UCHAR dateString[18] = { 0 };
+            dateString[ 0] = date->Year[0];
+            dateString[ 1] = date->Year[1];
+            dateString[ 2] = date->Year[2];
+            dateString[ 3] = date->Year[3];
+            dateString[ 4] = '/';
+            dateString[ 5] = date->Month[0];
+            dateString[ 6] = date->Month[1];
+            dateString[ 7] = '/';
+            dateString[ 8] = date->Day[0];
+            dateString[ 9] = date->Day[1];
+            dateString[10] = ' ';
+            dateString[11] = ' ';
+            dateString[12] = date->Hour[0];
+            dateString[13] = date->Hour[1];
+            dateString[14] = ':';
+            dateString[15] = date->Minute[0];
+            dateString[16] = date->Minute[1];
+            dateString[17] = 0;
+            // SECONDS IS NOT AVAILABLE ON EARLY IMPLEMENTATIONS -- ignore it
+            //dateString[17] = ':';
+            //dateString[18] = date->Seconds[0];
+            //dateString[19] = date->Seconds[1];
+            //dateString[20] = 0;
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                        "CdromGetConfiguration: Firmware Date/Time %s (UTC)\n",
+                        (PCSTR)dateString
+                        ));
+        }
+    }
+
+////////////////////////////////////////////////////////////////////////////////
+// items expected not to always be current
+////////////////////////////////////////////////////////////////////////////////
+
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteProtect);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Software Write Protect"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomReadable);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Random Reads"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureMultiRead);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Multi-Read"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdRead);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "reading from CD-ROM/R/RW"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRead);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD Structure Reads"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomWritable);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Random Writes"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Incremental Streaming Writing"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureSectorErasable);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Sector Erasable Media"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureFormattable);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Formatting"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDefectManagement);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "defect management"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteOnce);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Write Once Media"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Restricted Overwrites"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdrwCAVWrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "CD-RW CAV recording"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureMrw);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Mount Rainier media"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureEnhancedDefectReporting);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Enhanced Defect Reporting"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdPlusRW);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD+RW media"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRigidRestrictedOverwrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Rigid Restricted Overwrite"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "CD Recording (Track At Once)"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdMastering);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "CD Recording (Mastering)"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD Recording (Mastering)"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRead);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DD CD Reading"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DD CD-R Writing"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWWrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DD CD-RW Writing"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureLayerJumpRecording);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Layer Jump Recording"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDRead);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "HD-DVD Reading"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDWrite);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "HD-DVD Writing"
+                   ));
+    }
+
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureSMART);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "S.M.A.R.T."
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Analogue CD Audio Operations"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCSS);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD CSS"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "Real-time Streaming Reads"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD Disc Control Blocks"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCPRM);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "DVD CPRM"
+                   ));
+    }
+
+    header = DeviceFindFeaturePage(Buffer, Usable, FeatureAACS);
+    if (header) {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
+                   "CdromGetConfiguration: %s %s\n",
+                   (header->Current ?
+                    "Currently supports" : "Is able to support"),
+                   "AACS"
+                   ));
+    }
+
+#else
+    PAGED_CODE();
+
+    UNREFERENCED_PARAMETER(Usable);
+    UNREFERENCED_PARAMETER(Buffer);
+
+#endif // DBG
+    return;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+MediaReadCapacity(
+    _In_ WDFDEVICE Device
+    )
+/*++
+Routine Description:
+
+    Get media capacity
+
+Arguments:
+
+    Device - the device that owns the media
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status;
+    SCSI_REQUEST_BLOCK      srb;
+    PCDB                    cdb = NULL;
+    READ_CAPACITY_DATA      capacityData;
+
+    PAGED_CODE();
+
+    RtlZeroMemory(&srb, sizeof(srb));
+    RtlZeroMemory(&capacityData, sizeof(capacityData));
+
+    cdb = (PCDB)(&srb.Cdb);
+
+    //Prepare SCSI command fields
+    srb.CdbLength = 10;
+    srb.TimeOutValue = CDROM_READ_CAPACITY_TIMEOUT;
+    cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+
+    status = DeviceSendSrbSynchronously(Device,
+                                        &srb,
+                                        &capacityData,
+                                        sizeof(READ_CAPACITY_DATA),
+                                        FALSE,
+                                        NULL);
+
+    //Remember the result
+    if (!NT_SUCCESS(status))
+    {
+        //Set the BytesPerBlock to zero, this is for safe as if error happens this field should stay zero (no change).
+        //it will be treated as error case in MediaReadCapacityDataInterpret()
+        capacityData.BytesPerBlock = 0;
+    }
+
+    MediaReadCapacityDataInterpret(Device, &capacityData);
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+MediaReadCapacityDataInterpret(
+    _In_ WDFDEVICE            Device,
+    _In_ PREAD_CAPACITY_DATA  ReadCapacityBuffer
+    )
+/*++
+Routine Description:
+
+    Interpret media capacity and set corresponding fields in device context
+
+Arguments:
+
+    Device - the device that owns the media
+
+    ReadCapacityBuffer - data buffer of capacity
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    ULONG                   lastSector = 0;
+    ULONG                   bps = 0;
+    ULONG                   lastBit = 0;
+    ULONG                   bytesPerBlock = 0;
+    BOOLEAN                 errorHappened = FALSE;
+
+    PAGED_CODE();
+
+    NT_ASSERT(ReadCapacityBuffer);
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                "MediaReadCapacityDataInterpret: Entering\n"));
+
+    // Swizzle bytes from Read Capacity and translate into
+    // the necessary geometry information in the device extension.
+    bytesPerBlock = ReadCapacityBuffer->BytesPerBlock;
+
+    ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
+    ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
+    ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
+    ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
+
+    // Insure that bps is a power of 2.
+    // This corrects a problem with the HP 4020i CDR where it
+    // returns an incorrect number for bytes per sector.
+    if (!bps)
+    {
+        // Set disk geometry to default values (per ISO 9660).
+        bps = 2048;
+        errorHappened = TRUE;
+    }
+    else
+    {
+        lastBit = (ULONG)(-1);
+        while (bps)
+        {
+            lastBit++;
+            bps = (bps >> 1);
+        }
+        bps = (1 << lastBit);
+    }
+
+    deviceExtension->DiskGeometry.BytesPerSector = bps;
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+               "MediaReadCapacityDataInterpret: Calculated bps %#x\n",
+                deviceExtension->DiskGeometry.BytesPerSector));
+
+    // Copy last sector in reverse byte order.
+    bytesPerBlock = ReadCapacityBuffer->LogicalBlockAddress;
+
+    ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
+    ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
+    ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
+    ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
+
+    // Calculate sector to byte shift.
+    WHICH_BIT(bps, deviceExtension->SectorShift);
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
+               "MediaReadCapacityDataInterpret: Sector size is %d\n",
+               deviceExtension->DiskGeometry.BytesPerSector));
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+               "MediaReadCapacityDataInterpret: Number of Sectors is %d\n",
+               lastSector + 1));
+
+    // Calculate media capacity in bytes.
+    if (errorHappened)
+    {
+        // Set disk geometry to default values (per ISO 9660).
+        deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
+    }
+    else
+    {
+        deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
+        deviceExtension->PartitionLength.QuadPart =
+                    (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
+    }
+
+    // we've defaulted to 32/64 forever.  don't want to change this now...
+    deviceExtension->DiskGeometry.TracksPerCylinder = 0x40;
+    deviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
+
+    // Calculate number of cylinders.
+    deviceExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64));
+
+    deviceExtension->DiskGeometry.MediaType = RemovableMedia;
+
+    return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DevicePickDvdRegion(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+    pick a default dvd region
+
+Arguments:
+
+    Device - Device Object
+
+Return Value:
+
+    none
+
+--*/
+{
+    NTSTATUS                status;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+
+    // these five pointers all point to dvdReadStructure or part of
+    // its data, so don't deallocate them more than once!
+    PDVD_READ_STRUCTURE         dvdReadStructure;
+    PDVD_COPY_PROTECT_KEY       copyProtectKey;
+    PDVD_COPYRIGHT_DESCRIPTOR   dvdCopyRight;
+    PDVD_RPC_KEY                rpcKey;
+    PDVD_SET_RPC_KEY            dvdRpcKey;
+
+    size_t          bytesReturned = 0;
+    ULONG           bufferLen = 0;
+    UCHAR           mediaRegion = 0;
+    ULONG           pickDvdRegion = 0;
+    ULONG           defaultDvdRegion = 0;
+    ULONG           dvdRegion = 0;
+    WDFKEY          registryKey = NULL;
+
+    DECLARE_CONST_UNICODE_STRING(registryValueName, DVD_DEFAULT_REGION);
+
+    PAGED_CODE();
+
+    if ((pickDvdRegion = InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, 0)) == 0)
+    {
+        // it was non-zero, so either another thread will do this, or
+        // we no longer need to pick a region
+        return;
+    }
+
+    bufferLen = max(
+                    max(sizeof(DVD_DESCRIPTOR_HEADER) +
+                            sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+                        sizeof(DVD_READ_STRUCTURE)
+                        ),
+                    max(DVD_RPC_KEY_LENGTH,
+                        DVD_SET_RPC_KEY_LENGTH
+                        )
+                    );
+
+    dvdReadStructure = (PDVD_READ_STRUCTURE)
+                        ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_DVD_REGION);
+
+    if (dvdReadStructure == NULL)
+    {
+        InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
+        return;
+    }
+
+    copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure;
+
+    dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR)
+                        ((PDVD_DESCRIPTOR_HEADER)dvdReadStructure)->Data;
+
+    // get the media region
+    RtlZeroMemory (dvdReadStructure, bufferLen);
+    dvdReadStructure->Format = DvdCopyrightDescriptor;
+
+    // Build and send a request for READ_KEY
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Getting Copyright Descriptor\n",
+                Device));
+
+    status = ReadDvdStructure(deviceExtension,
+                              NULL,
+                              dvdReadStructure,
+                              sizeof(DVD_READ_STRUCTURE),
+                              dvdReadStructure,
+                              sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+                              &bytesReturned);
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Got Copyright Descriptor %x\n",
+                Device, status));
+
+    if ((NT_SUCCESS(status)) &&
+        (dvdCopyRight->CopyrightProtectionType == 0x01))
+    {
+        // keep the media region bitmap around
+        // a 1 means ok to play
+        if (dvdCopyRight->RegionManagementInformation == 0xff)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                      "DevicePickDvdRegion (%p): RegionManagementInformation "
+                      "is set to dis-allow playback for all regions.  This is "
+                      "most likely a poorly authored disc.  defaulting to all "
+                      "region disc for purpose of choosing initial region\n",
+                      Device));
+            dvdCopyRight->RegionManagementInformation = 0;
+        }
+
+        mediaRegion = ~dvdCopyRight->RegionManagementInformation;
+    }
+    else
+    {
+        // can't automatically pick a default region on a drive without media, so just exit
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): failed to auto-choose a region due to status %x getting copyright descriptor\n",
+                    Device, status));
+        goto getout;
+    }
+
+    // get the device region
+    RtlZeroMemory (copyProtectKey, bufferLen);
+    copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
+    copyProtectKey->KeyType = DvdGetRpcKey;
+
+    // Build and send a request for READ_KEY for RPC key
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Getting RpcKey\n",
+                Device));
+    status = DvdStartSessionReadKey(deviceExtension,
+                                    IOCTL_DVD_READ_KEY,
+                                    NULL,
+                                    copyProtectKey,
+                                    DVD_RPC_KEY_LENGTH,
+                                    copyProtectKey,
+                                    DVD_RPC_KEY_LENGTH,
+                                    &bytesReturned);
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Got RpcKey %x\n",
+                Device, status));
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): failed to get RpcKey from "
+                    "a DVD Device\n", Device));
+        goto getout;
+    }
+
+    // so we now have what we can get for the media region and the
+    // drive region.  we will not set a region if the drive has one
+    // set already (mask is not all 1's), nor will we set a region
+    // if there are no more user resets available.
+    rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
+
+    if (rpcKey->RegionMask != 0xff)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): not picking a region since "
+                    "it is already chosen\n", Device));
+        goto getout;
+    }
+
+    if (rpcKey->UserResetsAvailable <= 1)
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): not picking a region since "
+                    "only one change remains\n", Device));
+        goto getout;
+    }
+
+    // OOBE sets this key based upon the system locale
+    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+                                                KEY_READ,
+                                                WDF_NO_OBJECT_ATTRIBUTES,
+                                                &registryKey);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRegistryQueryULong(registryKey,
+                                       &registryValueName,
+                                       &defaultDvdRegion);
+
+        WdfRegistryClose(registryKey);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): failed to read registry value due to status %x\n",
+                    Device, status));
+
+        // by default the default Dvd region is 0
+        defaultDvdRegion = 0;
+        status = STATUS_SUCCESS;
+    }
+
+    if (defaultDvdRegion > DVD_MAX_REGION)
+    {
+        // the registry has a bogus default
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): registry has a bogus default "
+                    "region value of %x\n", Device, defaultDvdRegion));
+
+        defaultDvdRegion = 0;
+    }
+
+    // if defaultDvdRegion == 0, it means no default.
+
+    // we will select the initial dvd region for the user
+
+    if ((defaultDvdRegion != 0) &&
+        (mediaRegion & (1 << (defaultDvdRegion - 1))))
+    {
+        // first choice:
+        // the media has region that matches
+        // the default dvd region.
+        dvdRegion = (1 << (defaultDvdRegion - 1));
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): Choice #1: media matches "
+                    "drive's default, chose region %x\n", Device, dvdRegion));
+    }
+    else if (mediaRegion)
+    {
+        // second choice:
+        // pick the lowest region number from the media
+        UCHAR mask = 1;
+        dvdRegion = 0;
+
+        while (mediaRegion && !dvdRegion)
+        {
+            // pick the lowest bit
+            dvdRegion = mediaRegion & mask;
+            mask <<= 1;
+        }
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): Choice #2: choosing lowest "
+                    "media region %x\n", Device, dvdRegion));
+    }
+    else if (defaultDvdRegion)
+    {
+        // third choice:
+        // default dvd region from the dvd class installer
+        dvdRegion = (1 << (defaultDvdRegion - 1));
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): Choice #3: using default "
+                    "region for this install %x\n", Device, dvdRegion));
+    }
+    else
+    {
+        // unable to pick one for the user -- this should rarely
+        // happen, since the proppage dvd class installer sets
+        // the key based upon the system locale
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                    "DevicePickDvdRegion (%p): Choice #4: failed to choose "
+                    "a media region\n", Device));
+        goto getout;
+    }
+
+    // now that we've chosen a region, set the region by sending the
+    // appropriate request to the drive
+    RtlZeroMemory (copyProtectKey, bufferLen);
+    copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH;
+    copyProtectKey->KeyType = DvdSetRpcKey;
+    dvdRpcKey = (PDVD_SET_RPC_KEY)copyProtectKey->KeyData;
+    dvdRpcKey->PreferredDriveRegionCode = (UCHAR)~dvdRegion;
+
+    // Build and send request for SEND_KEY
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Sending new Rpc Key to region %x\n",
+                Device, dvdRegion));
+
+    status = DvdSendKey(deviceExtension,
+                        NULL,
+                        copyProtectKey,
+                        DVD_SET_RPC_KEY_LENGTH,
+                        &bytesReturned);
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "DevicePickDvdRegion (%p): Sent new Rpc Key %x\n",
+                Device, status));
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): unable to set dvd initial "
+                     " region code (%x)\n", Device, status));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): Successfully set dvd "
+                     "initial region\n", Device));
+
+        pickDvdRegion = 0;
+    }
+
+getout:
+    if (dvdReadStructure)
+    {
+        FREE_POOL(dvdReadStructure);
+    }
+
+    // update the new PickDvdRegion value
+    InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
+
+    return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRegisterInterface(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ CDROM_DEVICE_INTERFACES InterfaceType
+    )
+/*++
+Routine Description:
+
+    used to register device class interface or mount device interface
+
+Arguments:
+
+    DeviceExtension - device context
+
+    InterfaceType - interface type to be registered.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status;
+    WDFSTRING       string = NULL;
+    GUID*           interfaceGuid = NULL;
+    PUNICODE_STRING savingString = NULL;
+    BOOLEAN         setRestricted = FALSE;
+    UNICODE_STRING  localString;
+
+    PAGED_CODE();
+
+    //Get parameters
+    switch(InterfaceType)
+    {
+    case CdRomDeviceInterface:
+        interfaceGuid = (LPGUID)&GUID_DEVINTERFACE_CDROM;
+        setRestricted = TRUE;
+        savingString = &localString;
+        break;
+    case MountedDeviceInterface:
+        interfaceGuid = (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID;
+        savingString = &(DeviceExtension->MountedDeviceInterfaceName);
+        break;
+    default:
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    status = WdfDeviceCreateDeviceInterface(DeviceExtension->Device,
+                                            interfaceGuid,
+                                            NULL);
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                    "DeviceRegisterInterface: Unable to register cdrom "
+                    "DCA for fdo %p type: %s [%lx]\n",
+                    DeviceExtension->Device,
+                    (InterfaceType == CdRomDeviceInterface)? "CdRom Interface" : "Mounted Device Interface",
+                    status));
+    }
+
+    // Retrieve interface string
+    if (NT_SUCCESS(status))
+    {
+        // The string object will be released when its parent object is released.
+        WDF_OBJECT_ATTRIBUTES  attributes;
+
+        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+        attributes.ParentObject = DeviceExtension->Device;
+
+        status = WdfStringCreate(WDF_NO_OBJECT_ATTRIBUTES,
+                                 NULL,
+                                 &string);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfDeviceRetrieveDeviceInterfaceString(DeviceExtension->Device,
+                                                        interfaceGuid,
+                                                        NULL,
+                                                        string);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        WdfStringGetUnicodeString(string, savingString);
+
+        if (setRestricted) {
+
+
+            WdfObjectDelete(string);
+        }
+    }
+
+    return status;
+} // end DeviceRegisterInterface()
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceRestoreDefaultSpeed(
+    _In_ WDFWORKITEM  WorkItem
+    )
+/*++
+
+Routine Description:
+
+    This workitem is called on a media change when the CDROM device
+    speed should be restored to the default value.
+
+Arguments:
+
+    Fdo      - Supplies the device object for the CDROM device.
+    WorkItem - Supplies the pointer to the workitem.
+
+Return Value:
+
+    None
+
+--*/
+{
+    NTSTATUS                status;
+    WDFDEVICE               device = WdfWorkItemGetParentObject(WorkItem);
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+    PPERFORMANCE_DESCRIPTOR perfDescriptor;
+    ULONG                   transferLength = sizeof(PERFORMANCE_DESCRIPTOR);
+    SCSI_REQUEST_BLOCK      srb = {0};
+    PCDB                    cdb = (PCDB)srb.Cdb;
+
+    PAGED_CODE();
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceRestoreDefaultSpeed: Restore device speed for %p\n", device));
+
+    perfDescriptor = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                           transferLength,
+                                           CDROM_TAG_STREAM);
+    if (perfDescriptor == NULL)
+    {
+        return;
+    }
+
+    RtlZeroMemory(perfDescriptor, transferLength);
+
+    perfDescriptor->RestoreDefaults = TRUE;
+
+    srb.TimeOutValue = deviceExtension->TimeOutValue;
+
+    srb.CdbLength = 12;
+    cdb->SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
+    REVERSE_BYTES_SHORT(&cdb->SET_STREAMING.ParameterListLength, &transferLength);
+
+    status = DeviceSendSrbSynchronously(device,
+                                        &srb,
+                                        perfDescriptor,
+                                        transferLength,
+                                        TRUE,
+                                        NULL);
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+               "DeviceRestoreDefaultSpeed: Set Streaming command completed with status: 0x%X\n", status));
+
+    FREE_POOL(perfDescriptor);
+    WdfObjectDelete(WorkItem);
+
+    return;
+}
+
+// custom string match -- careful!
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+StringsAreMatched(
+    _In_opt_z_ PCHAR StringToMatch,
+    _In_z_     PCHAR TargetString
+    )
+/*++
+
+Routine Description:
+
+    compares if two strings are identical
+
+Arguments:
+
+    StringToMatch - source string.
+    TargetString - target string.
+
+Return Value:
+
+    BOOLEAN - TRUE (identical); FALSE (not match)
+
+--*/
+{
+    size_t length;
+
+    PAGED_CODE();
+
+    NT_ASSERT(TargetString);
+
+    // if no match requested, return TRUE
+    if (StringToMatch == NULL)
+    {
+        return TRUE;
+    }
+
+    // cache the string length for efficiency
+    length = strlen(StringToMatch);
+
+    // ZERO-length strings may only match zero-length strings
+    if (length == 0)
+    {
+        return (strlen(TargetString) == 0);
+    }
+
+    // strncmp returns zero if the strings match
+    return (strncmp(StringToMatch, TargetString, length) == 0);
+}
+
+
+NTSTATUS
+RequestSetContextFields(
+    _In_ WDFREQUEST    Request,
+    _In_ PSYNC_HANDLER Handler
+    )
+/*++
+
+Routine Description:
+
+    set the request object context fields
+
+Arguments:
+
+    Request - request object.
+    Handler - the function that finally handles this request.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(Request);
+    PKEVENT                 syncEvent = NULL;
+
+    syncEvent = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                      sizeof(KEVENT),
+                                      CDROM_TAG_SYNC_EVENT);
+
+    if (syncEvent == NULL)
+    {
+        // memory allocation failed.
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        // now, put the special synchronization information into the context
+        requestContext->SyncRequired = TRUE;
+        requestContext->SyncEvent = syncEvent;
+        requestContext->SyncCallback = Handler;
+
+        status = STATUS_SUCCESS;
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestDuidGetDeviceIdProperty(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_ size_t *               DataLength
+    )
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request object.
+    RequestParameters - request parameter
+    DataLength - transferred data length.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PSTORAGE_DEVICE_ID_DESCRIPTOR   deviceIdDescriptor = NULL;
+    PSTORAGE_DESCRIPTOR_HEADER      descHeader = NULL;
+    STORAGE_PROPERTY_ID             propertyId = StorageDeviceIdProperty;
+
+    *DataLength = 0;
+
+    // Get the VPD page 83h data.
+    status = DeviceRetrieveDescriptor(DeviceExtension->Device,
+                                      &propertyId,
+                                      (PSTORAGE_DESCRIPTOR_HEADER*)&deviceIdDescriptor);
+
+    if (NT_SUCCESS(status) && (deviceIdDescriptor == NULL))
+    {
+        status = STATUS_NOT_FOUND;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &descHeader,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        PSTORAGE_DEVICE_UNIQUE_IDENTIFIER   storageDuid = NULL;
+        ULONG                               offset = descHeader->Size;
+        PUCHAR                              dest = (PUCHAR)descHeader + offset;
+        size_t                              outputBufferSize;
+
+        outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+
+        // Adjust required size and potential destination location.
+        status = RtlULongAdd(descHeader->Size, deviceIdDescriptor->Size, &descHeader->Size);
+
+        if (NT_SUCCESS(status) &&
+            (outputBufferSize < descHeader->Size))
+        {
+            // Output buffer is too small.  Return error and make sure
+            // the caller gets info about required buffer size.
+            *DataLength = descHeader->Size;
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
+            storageDuid->StorageDeviceIdOffset = offset;
+
+            RtlCopyMemory(dest,
+                          deviceIdDescriptor,
+                          deviceIdDescriptor->Size);
+
+            *DataLength = storageDuid->Size;
+            status = STATUS_SUCCESS;
+        }
+
+        FREE_POOL(deviceIdDescriptor);
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestDuidGetDeviceProperty(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ WDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_ size_t *               DataLength
+    )
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request object.
+    RequestParameters - request parameter
+    DataLength - transferred data length.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                          status = STATUS_SUCCESS;
+    PSTORAGE_DEVICE_DESCRIPTOR        deviceDescriptor = DeviceExtension->DeviceDescriptor;
+    PSTORAGE_DESCRIPTOR_HEADER        descHeader = NULL;
+    PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
+    PUCHAR                            dest = NULL;
+
+    if (deviceDescriptor == NULL)
+    {
+        status = STATUS_NOT_FOUND;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &descHeader,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status) &&
+        (deviceDescriptor->SerialNumberOffset == 0))
+    {
+        status = STATUS_NOT_FOUND;
+    }
+
+    // Use this info only if serial number is available.
+    if (NT_SUCCESS(status))
+    {
+        ULONG offset = descHeader->Size;
+        size_t outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+
+        // Adjust required size and potential destination location.
+        dest = (PUCHAR)descHeader + offset;
+
+        status = RtlULongAdd(descHeader->Size, deviceDescriptor->Size, &descHeader->Size);
+
+        if (NT_SUCCESS(status) &&
+            (outputBufferSize < descHeader->Size))
+        {
+            // Output buffer is too small.  Return error and make sure
+            // the caller get info about required buffer size.
+            *DataLength = descHeader->Size;
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
+            storageDuid->StorageDeviceOffset = offset;
+
+            RtlCopyMemory(dest,
+                          deviceDescriptor,
+                          deviceDescriptor->Size);
+
+            *DataLength = storageDuid->Size;
+            status = STATUS_SUCCESS;
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+ULONG
+DeviceRetrieveModeSenseUsingScratch(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_reads_bytes_(Length) PCHAR     ModeSenseBuffer,
+    _In_ ULONG                    Length,
+    _In_ UCHAR                    PageCode,
+    _In_ UCHAR                    PageControl
+    )
+/*++
+
+Routine Description:
+
+    retrieve mode sense informaiton of the device
+
+Arguments:
+
+    DeviceExtension - device context
+    ModeSenseBuffer - buffer to savee the mode sense info.
+    Length - buffer length
+    PageCode - .
+    PageControl -
+
+Return Value:
+
+    ULONG - transferred data length
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    ULONG                       transferSize = min(Length, DeviceExtension->ScratchContext.ScratchBufferSize);
+    CDB                         cdb;
+
+    PAGED_CODE();
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+    cdb.MODE_SENSE.PageCode = PageCode;
+    cdb.MODE_SENSE.Pc = PageControl;
+    cdb.MODE_SENSE.AllocationLength = (UCHAR)transferSize;
+
+    status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 6);
+
+    if (NT_SUCCESS(status))
+    {
+        transferSize = min(Length, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
+        RtlCopyMemory(ModeSenseBuffer,
+                      DeviceExtension->ScratchContext.ScratchBuffer,
+                      transferSize);
+    }
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return transferSize;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+ModeSenseFindSpecificPage(
+    _In_reads_bytes_(Length) PCHAR     ModeSenseBuffer,
+    _In_ size_t                   Length,
+    _In_ UCHAR                    PageMode,
+    _In_ BOOLEAN                  Use6BytesCdb
+    )
+/*++
+
+Routine Description:
+
+    This routine scans through the mode sense data and finds the requested
+    mode sense page code.
+
+Arguments:
+    ModeSenseBuffer - Supplies a pointer to the mode sense data.
+
+    Length - Indicates the length of valid data.
+
+    PageMode - Supplies the page mode to be searched for.
+
+    Use6BytesCdb - Indicates whether 6 or 10 byte mode sense was used.
+
+Return Value:
+
+    A pointer to the the requested mode page.  If the mode page was not found
+    then NULL is return.
+
+--*/
+{
+    PCHAR limit;
+    ULONG  parameterHeaderLength;
+    PVOID  result = NULL;
+
+    PAGED_CODE();
+
+    limit = ModeSenseBuffer + Length;
+    parameterHeaderLength = (Use6BytesCdb)
+                            ? sizeof(MODE_PARAMETER_HEADER)
+                            : sizeof(MODE_PARAMETER_HEADER10);
+
+    if (Length >= parameterHeaderLength)
+    {
+        PMODE_PARAMETER_HEADER10 modeParam10;
+        ULONG                    blockDescriptorLength;
+
+        //  Skip the mode select header and block descriptors.
+        if (Use6BytesCdb)
+        {
+            blockDescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength;
+        }
+        else
+        {
+            modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
+            blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
+        }
+
+        ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
+
+        // ModeSenseBuffer now points at pages.  Walk the pages looking for the
+        // requested page until the limit is reached.
+        while (ModeSenseBuffer +
+               RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit)
+        {
+            if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode)
+            {
+                // found the mode page.  make sure it's safe to touch it all
+                // before returning the pointer to caller
+                if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit)
+                {
+                    //  Return NULL since the page is not safe to access in full
+                    result = NULL;
+                }
+                else
+                {
+                    result = ModeSenseBuffer;
+                }
+                break;
+            }
+
+            // Advance to the next page which is 4-byte-aligned offset after this page.
+            ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
+                                RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
+        }
+    }
+
+    return result;
+} // end ModeSenseFindSpecificPage()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PerformEjectionControl(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ WDFREQUEST               Request,
+    _In_ MEDIA_LOCK_TYPE          LockType,
+    _In_ BOOLEAN                  Lock
+    )
+/*++
+
+Routine Description:
+
+    ejection control process
+
+Arguments:
+
+    DeviceExtension - device extension
+    Request - WDF request to be used for communication with the device
+    LockType - the type of lock
+    Lock - if TRUE, lock the device; if FALSE, unlock it
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status;
+    PFILE_OBJECT_CONTEXT    fileObjectContext = NULL;
+    SCSI_REQUEST_BLOCK      srb;
+    PCDB                    cdb = NULL;
+
+    LONG                    newLockCount = 0;
+    LONG                    newProtectedLockCount = 0;
+    LONG                    newInternalLockCount = 0;
+    LONG                    newFileLockCount = 0;
+    BOOLEAN                 countChanged = FALSE;
+    BOOLEAN                 previouslyLocked = FALSE;
+    BOOLEAN                 nowLocked = FALSE;
+
+    PAGED_CODE();
+
+    // Prevent race conditions while working with lock counts
+    status = WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
+    if (!NT_SUCCESS(status))
+    {
+        NT_ASSERT(FALSE);
+    }
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                "PerformEjectionControl: "
+                "Received request for %s lock type\n",
+                LockTypeStrings[LockType]
+                ));
+
+
+    // If this is a "secured" request, retrieve the file object context
+    if (LockType == SecureMediaLock)
+    {
+        WDFFILEOBJECT fileObject = NULL;
+
+        fileObject = WdfRequestGetFileObject(Request);
+
+        if (fileObject == NULL)
+        {
+            status = STATUS_INVALID_HANDLE;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                            "FileObject does not match to the one in IRP_MJ_CREATE, KMDF returns NULL\n"));
+            goto Exit;
+        }
+
+        fileObjectContext = FileObjectGetContext(fileObject);
+        NT_ASSERT(fileObjectContext != NULL);
+    }
+
+    // Lock counts should never fall below 0
+    NT_ASSERT(DeviceExtension->LockCount >= 0);
+    NT_ASSERT(DeviceExtension->ProtectedLockCount >= 0);
+    NT_ASSERT(DeviceExtension->InternalLockCount >= 0);
+
+    // Get the current lock counts
+    newLockCount = DeviceExtension->LockCount;
+    newProtectedLockCount = DeviceExtension->ProtectedLockCount;
+    newInternalLockCount = DeviceExtension->InternalLockCount;
+    if (fileObjectContext)
+    {
+        // fileObjectContext->LockCount is ULONG and should always >= 0
+        newFileLockCount = fileObjectContext->LockCount;
+    }
+
+    // Determine which lock counts need to be changed and how
+    if (Lock && LockType == SimpleMediaLock)
+    {
+        newLockCount++;
+        countChanged = TRUE;
+    }
+    else if (Lock && LockType == SecureMediaLock)
+    {
+        newFileLockCount++;
+        newProtectedLockCount++;
+        countChanged = TRUE;
+    }
+    else if (Lock && LockType == InternalMediaLock)
+    {
+        newInternalLockCount++;
+        countChanged = TRUE;
+    }
+    else if (!Lock && LockType == SimpleMediaLock)
+    {
+        if (newLockCount != 0)
+        {
+            newLockCount--;
+            countChanged = TRUE;
+        }
+    }
+    else if (!Lock && LockType == SecureMediaLock)
+    {
+        if ( (newFileLockCount == 0) || (newProtectedLockCount == 0) )
+        {
+            status = STATUS_INVALID_DEVICE_STATE;
+            goto Exit;
+        }
+        newFileLockCount--;
+        newProtectedLockCount--;
+        countChanged = TRUE;
+    }
+    else if (!Lock && LockType == InternalMediaLock)
+    {
+        NT_ASSERT(newInternalLockCount != 0);
+        newInternalLockCount--;
+        countChanged = TRUE;
+    }
+
+    if ( (DeviceExtension->LockCount != 0) ||
+         (DeviceExtension->ProtectedLockCount != 0) ||
+         (DeviceExtension->InternalLockCount != 0) )
+    {
+        previouslyLocked = TRUE;
+    }
+    if ( (newLockCount != 0) ||
+         (newProtectedLockCount != 0) ||
+         (newInternalLockCount != 0) )
+    {
+        nowLocked = TRUE;
+    }
+
+    // Only send command down to device when necessary
+    if (previouslyLocked != nowLocked)
+    {
+        // Compose and send the PREVENT ALLOW MEDIA REMOVAL command.
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        srb.CdbLength = 6;
+        srb.TimeOutValue = DeviceExtension->TimeOutValue;
+
+        cdb = (PCDB)&srb.Cdb;
+        cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+        cdb->MEDIA_REMOVAL.Prevent = Lock;
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            &srb,
+                                            NULL,
+                                            0,
+                                            FALSE,
+                                            Request);
+    }
+
+Exit:
+
+    // Store the updated lock counts on success
+    if (countChanged && NT_SUCCESS(status))
+    {
+        DeviceExtension->LockCount = newLockCount;
+        DeviceExtension->ProtectedLockCount = newProtectedLockCount;
+        DeviceExtension->InternalLockCount = newInternalLockCount;
+        if (fileObjectContext)
+        {
+            fileObjectContext->LockCount = newFileLockCount;
+        }
+    }
+
+    WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                "PerformEjectionControl: %!STATUS!, "
+                "Count Changed: %d, Command Sent: %d, "
+                "Current Counts: Internal: %x  Secure: %x  Simple: %x\n",
+                status,
+                countChanged,
+                previouslyLocked != nowLocked,
+                DeviceExtension->InternalLockCount,
+                DeviceExtension->ProtectedLockCount,
+                DeviceExtension->LockCount
+                ));
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceUnlockExclusive(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ WDFFILEOBJECT            FileObject,
+    _In_ BOOLEAN                  IgnorePreviousMediaChanges
+    )
+/*++
+
+Routine Description:
+
+    to unlock the exclusive lock
+
+Arguments:
+
+    DeviceExtension - device context
+    FileObject - file object that currently holds the lock
+    IgnorePreviousMediaChanges - if TRUE, ignore previously accumulated media changes
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PCDROM_DATA                     cdData = &DeviceExtension->DeviceAdditionalData;
+    PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
+    BOOLEAN                         ANPending = 0;
+    LONG                            requestInUse = 0;
+
+    PAGED_CODE();
+
+    if (!EXCLUSIVE_MODE(cdData))
+    {
+        // Device is not locked for exclusive access.
+        // Can not process unlock request.
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                    "RequestHandleExclusiveAccessUnlockDevice: Device not locked for exclusive access, can't unlock device.\n"));
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (!EXCLUSIVE_OWNER(cdData, FileObject))
+    {
+        // Request not from the exclusive owner, can't unlock the device.
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                   "RequestHandleExclusiveAccessUnlockDevice: Unable to unlock device, invalid file object\n"));
+
+        status = STATUS_INVALID_HANDLE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Unless we were explicitly requested not to do so, generate a media removal notification
+        // followed by a media arrival notification similar to volume lock/unlock file system events.
+        if (!IgnorePreviousMediaChanges)
+        {
+            MEDIA_CHANGE_DETECTION_STATE previousMediaState = MediaUnknown;
+
+            // Change the media state to "unavailable", which will cause a removal notification if the media
+            // was previously present. At the same time, store the previous state in previousMediaState.
+            DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, &previousMediaState);
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "DeviceUnlockExclusive: Changing the media state to MediaUnavailable\n"));
+
+            // Restore the previous media state, which will cause a media arrival notification if the media
+            // was originally present.
+            DeviceSetMediaChangeStateEx(DeviceExtension, previousMediaState, NULL);
+        }
+
+        // Set DO_VERIFY_VOLUME so that the file system will remount on it.
+        if (IsVolumeMounted(DeviceExtension->DeviceObject))
+        {
+            SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+        }
+
+        // Set MMC state to update required
+        cdData->Mmc.WriteAllowed = FALSE;
+        cdData->Mmc.UpdateState = CdromMmcUpdateRequired;
+
+        // Send unlock notification
+        DeviceSendNotification(DeviceExtension,
+                               &GUID_IO_CDROM_EXCLUSIVE_UNLOCK,
+                               0,
+                               NULL);
+
+        InterlockedExchangePointer((PVOID)&cdData->ExclusiveOwner,  NULL);
+
+        if ((info != NULL) && (info->AsynchronousNotificationSupported != FALSE))
+        {
+            ANPending = info->ANSignalPendingDueToExclusiveLock;
+            info->ANSignalPendingDueToExclusiveLock = FALSE;
+
+            if ((ANPending != FALSE) && (info->MediaChangeDetectionDisableCount == 0))
+            {
+                // if the request is not in use, mark it as such.
+                requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);
+
+                if (requestInUse == 0)
+                {
+                    // The last MCN finished. ok to issue the new one.
+                    RequestSetupMcnSyncIrp(DeviceExtension);
+
+                    // The irp will go into KMDF framework and a request will be created there to represent it.
+                    IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
+                }
+            }
+        }
+    }
+
+    return status;
+}
+
+
+VOID
+RequestCompletion(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ WDFREQUEST              Request,
+    _In_ NTSTATUS                Status,
+    _In_ ULONG_PTR               Information
+    )
+{
+#ifdef DBG
+    ULONG                   ioctlCode = 0;
+    WDF_REQUEST_PARAMETERS  requestParameters;
+
+    // Get the Request parameters
+    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+    WdfRequestGetParameters(Request, &requestParameters);
+
+    if (requestParameters.Type == WdfRequestTypeDeviceControl)
+    {
+        ioctlCode = requestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+        if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                       "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
+                       ioctlCode,
+                       Status,
+                       (ULONG)Information));
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
+                       "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
+                       ioctlCode,
+                       Status,
+                       (ULONG)Information));
+        }
+    }
+    else if (requestParameters.Type == WdfRequestTypeRead)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                   "Request complete - READ - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
+                   (ULONG)requestParameters.Parameters.Read.DeviceOffset,
+                   (ULONG)requestParameters.Parameters.Read.Length,
+                   (ULONG)Information,
+                   Status));
+    }
+    else if (requestParameters.Type == WdfRequestTypeWrite)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                   "Request complete - WRITE - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
+                   (ULONG)requestParameters.Parameters.Write.DeviceOffset,
+                   (ULONG)requestParameters.Parameters.Write.Length,
+                   (ULONG)Information,
+                   Status));
+    }
+#endif
+
+    if (IoIsErrorUserInduced(Status))
+    {
+        PIRP irp = WdfRequestWdmGetIrp(Request);
+        if (irp->Tail.Overlay.Thread)
+        {
+            IoSetHardErrorOrVerifyDevice(irp, DeviceExtension->DeviceObject);
+        }
+    }
+
+    if (!NT_SUCCESS(Status) && DeviceExtension->SurpriseRemoved == TRUE)
+    {
+        // IMAPI expects ERROR_DEV_NOT_EXISTS if recorder has been surprised removed,
+        // or it will retry WRITE commands for up to 3 minutes
+        // CDROM behavior should be consistent for all requests, including SCSI pass-through
+        Status = STATUS_DEVICE_DOES_NOT_EXIST;
+    }
+
+    WdfRequestCompleteWithInformation(Request, Status, Information);
+
+    return;
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestDummyCompletionRoutine(
+    _In_ WDFREQUEST                     Request,
+    _In_ WDFIOTARGET                    Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+    _In_ WDFCONTEXT                     Context
+    )
+/*++
+
+Routine Description:
+
+    This is a dummy competion routine that simply calls WdfRequestComplete. We have to use
+    this dummy competion routine instead of WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET, because
+    the latter causes the framework to not check if the I/O target is closed or not.
+
+Arguments:
+
+    Request - completed request
+    Target - the I/O target that completed the request
+    Params - request parameters
+    Context - not used
+
+Return Value:
+
+    none
+
+--*/
+{
+    UNREFERENCED_PARAMETER(Target);
+    UNREFERENCED_PARAMETER(Params);
+    UNREFERENCED_PARAMETER(Context);
+
+    WdfRequestCompleteWithInformation(Request,
+                                      WdfRequestGetStatus(Request),
+                                      WdfRequestGetInformation(Request));
+}
+
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+DeviceSendPowerDownProcessRequest(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
+    _In_opt_ PVOID Context
+    )
+/*++
+
+Routine Description:
+
+    This function is called during processing power down request.
+    It is used to send either SYNC CACHE command or STOP UNIT command.
+
+    Caller should set proper value in deviceExtension->PowerContext.PowerChangeState.PowerDown
+    to trigger the correct command be sent.
+
+Arguments:
+
+    DeviceExtension -
+
+    CompletionRoutine - Completion routine that needs to be set for the request
+
+    Context - Completion context associated with the completion routine
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status;
+    BOOLEAN                     requestSent = FALSE;
+
+    BOOLEAN                     shouldRetry = TRUE;
+    PCDB                        cdb = (PCDB)DeviceExtension->PowerContext.Srb.Cdb;
+    ULONG                       timeoutValue = DeviceExtension->TimeOutValue;
+    ULONG                       retryCount = 1;
+
+    // reset some fields.
+    DeviceExtension->PowerContext.RetryIntervalIn100ns = 0;
+    status = PowerContextReuseRequest(DeviceExtension);
+    RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest);
+
+    if (!NT_SUCCESS(status))
+    {
+        return status;
+    }
+
+    // set proper timeout value and max retry count.
+    switch(DeviceExtension->PowerContext.PowerChangeState.PowerDown)
+    {
+    case PowerDownDeviceInitial:
+    case PowerDownDeviceQuiesced:
+    case PowerDownDeviceStopped:
+        break;
+
+    case PowerDownDeviceLocked:
+        // Case of issuing SYNC CACHE command. Do not use power irp timeout remaining time in this case
+        // as we want to give best try on SYNC CACHE command.
+        retryCount = MAXIMUM_RETRIES;
+        timeoutValue = DeviceExtension->TimeOutValue;
+        break;
+
+    case PowerDownDeviceFlushed:
+    {
+        // Case of issuing STOP UNIT command
+        // As "Imme" bit is set to '1', this command should be completed in short time.
+        // This command is at low importance, failure of this command has very small impact.
+        ULONG secondsRemaining = 0;
+
+#if (WINVER >= 0x0601)
+        // this API is introduced in Windows7
+        PoQueryWatchdogTime(DeviceExtension->LowerPdo, &secondsRemaining);
+#endif
+
+        if (secondsRemaining == 0)
+        {
+            // not able to retrieve remaining time from PoQueryWatchdogTime API, use default values.
+            retryCount = MAXIMUM_RETRIES;
+            timeoutValue = SCSI_CDROM_TIMEOUT;
+        }
+        else
+        {
+            // plan to leave about 30 seconds to lower level drivers if possible.
+            if (secondsRemaining >= 32)
+            {
+                retryCount = (secondsRemaining - 30)/SCSI_CDROM_TIMEOUT + 1;
+                timeoutValue = SCSI_CDROM_TIMEOUT;
+
+                if (retryCount > MAXIMUM_RETRIES)
+                {
+                    retryCount = MAXIMUM_RETRIES;
+                }
+
+                if (retryCount == 1)
+                {
+                    timeoutValue = secondsRemaining - 30;
+                }
+            }
+            else
+            {
+                // issue the command with minimal timeout value and do not retry on it.
+                retryCount = 1;
+                timeoutValue = 2;
+            }
+        }
+    }
+        break;
+    default:
+        NT_ASSERT( FALSE );
+        status = STATUS_NOT_IMPLEMENTED;
+        return status;
+    }
+
+    DeviceExtension->PowerContext.RetryCount = retryCount;
+
+    // issue command.
+    while (shouldRetry)
+    {
+
+        // set SRB fields.
+        DeviceExtension->PowerContext.Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+                                                     SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+                                                     SRB_FLAGS_NO_QUEUE_FREEZE |
+                                                     SRB_FLAGS_BYPASS_LOCKED_QUEUE |
+                                                     SRB_FLAGS_D3_PROCESSING;
+
+        DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+        DeviceExtension->PowerContext.Srb.TimeOutValue = timeoutValue;
+
+        if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceInitial)
+        {
+            DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
+        }
+        else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceLocked)
+        {
+            DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_QUIESCE_DEVICE;
+        }
+        else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
+        {
+            // Case of issuing SYNC CACHE command.
+            DeviceExtension->PowerContext.Srb.CdbLength = 10;
+            cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+        }
+        else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceFlushed)
+        {
+            // Case of issuing STOP UNIT command.
+            DeviceExtension->PowerContext.Srb.CdbLength = 6;
+            cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+            cdb->START_STOP.Start = 0;
+            cdb->START_STOP.Immediate = 1;
+        }
+        else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceStopped)
+        {
+            DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
+        }
+
+        // Set up completion routine and context if requested
+        if (CompletionRoutine)
+        {
+            WdfRequestSetCompletionRoutine(DeviceExtension->PowerContext.PowerRequest,
+                                           CompletionRoutine,
+                                           Context);
+        }
+
+        status = RequestSend(DeviceExtension,
+                             DeviceExtension->PowerContext.PowerRequest,
+                             DeviceExtension->IoTarget,
+                             CompletionRoutine ? 0 : WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+                             &requestSent);
+
+        if (requestSent)
+        {
+            if ((CompletionRoutine == NULL) &&
+                (SRB_STATUS(DeviceExtension->PowerContext.Srb.SrbStatus) != SRB_STATUS_SUCCESS))
+            {
+                TracePrint((TRACE_LEVEL_ERROR,
+                            TRACE_FLAG_POWER,
+                            "%p\tError occured when issuing %s command to device. Srb %p, Status %x\n",
+                            DeviceExtension->PowerContext.PowerRequest,
+                            (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) ? "SYNC CACHE" : "STOP UNIT",
+                            &DeviceExtension->PowerContext.Srb,
+                            DeviceExtension->PowerContext.Srb.SrbStatus));
+
+                NT_ASSERT(!(TEST_FLAG(DeviceExtension->PowerContext.Srb.SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
+
+                shouldRetry = RequestSenseInfoInterpret(DeviceExtension,
+                                                        DeviceExtension->PowerContext.PowerRequest,
+                                                        &(DeviceExtension->PowerContext.Srb),
+                                                        retryCount - DeviceExtension->PowerContext.RetryCount,
+                                                        &status,
+                                                        &(DeviceExtension->PowerContext.RetryIntervalIn100ns));
+
+                if (shouldRetry && (DeviceExtension->PowerContext.RetryCount-- == 0))
+                {
+                    shouldRetry = FALSE;
+                }
+            }
+            else
+            {
+                // succeeded, do not need to retry.
+                shouldRetry = FALSE;
+            }
+
+        }
+        else
+        {
+            // request failed to be sent
+            shouldRetry = FALSE;
+        }
+
+        if (shouldRetry)
+        {
+            LARGE_INTEGER t;
+            t.QuadPart = -DeviceExtension->PowerContext.RetryIntervalIn100ns;
+            KeDelayExecutionThread(KernelMode, FALSE, &t);
+
+            status = PowerContextReuseRequest(DeviceExtension);
+            if (!NT_SUCCESS(status))
+            {
+                shouldRetry = FALSE;
+            }
+        }
+    }
+
+    if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
+    {
+        // record SYNC CACHE command completion time stamp.
+        KeQueryTickCount(&DeviceExtension->PowerContext.Step1CompleteTime);
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestSend(
+    _In_        PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_        WDFREQUEST              Request,
+    _In_        WDFIOTARGET             IoTarget,
+    _In_        ULONG                   Flags,
+    _Out_opt_   PBOOLEAN                RequestSent
+    )
+/*++
+
+Routine Description:
+
+    Send the request to the target, wake up the device from Zero Power state if necessary.
+
+Arguments:
+
+    DeviceExtension - device extension
+    Request - the request to be sent
+    IoTarget - target of the above request
+    Flags - flags for the operation
+    RequestSent - optional, if the request was sent
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                 status  = STATUS_SUCCESS;
+    BOOLEAN                  requestSent = FALSE;
+    WDF_REQUEST_SEND_OPTIONS options;
+
+    UNREFERENCED_PARAMETER(DeviceExtension);
+
+    if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
+        (DeviceExtension->ZeroPowerODDInfo->InZeroPowerState != FALSE))
+    {
+    }
+
+    // Now send down the request
+    if (NT_SUCCESS(status))
+    {
+        WDF_REQUEST_SEND_OPTIONS_INIT(&options, Flags);
+
+        RequestSetSentTime(Request);
+
+        // send request and check status
+
+        // Disable SDV warning about infinitely waiting in caller's context:
+        //   1. Some requests (such as SCSI_PASS_THROUGH, contains buffer from user space) need to be sent down in caller\92s context.
+        //      Consequently, these requests wait in caller\92s context until they are allowed to be sent down.
+        //   2. Considering the situation that during sleep, a request can be hold by storage port driver. When system resumes, any time out value (if we set using KMDF time out value) might be expires.
+        //      This will cause the waiting request being failed (behavior change). We\92d rather not set time out value.
+
+        _Analysis_assume_(options.Timeout != 0);
+        requestSent = WdfRequestSend(Request, IoTarget, &options);
+        _Analysis_assume_(options.Timeout == 0);
+
+        // If WdfRequestSend fails, or if the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag is set,
+        // the driver can call WdfRequestGetStatus immediately after calling WdfRequestSend.
+        if ((requestSent == FALSE) ||
+            (Flags & WDF_REQUEST_SEND_OPTION_SYNCHRONOUS))
+        {
+            status = WdfRequestGetStatus(Request);
+
+            if (requestSent == FALSE)
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                            "WdfRequestSend failed: %lx\n",
+                            status
+                            ));
+            }
+        }
+        else
+        {
+            status = STATUS_SUCCESS;
+        }
+
+        if (RequestSent != NULL)
+        {
+            *RequestSent = requestSent;
+        }
+    }
+
+    return status;
+}
+
diff --git a/drivers/storage/class/cdrom_new/data.c b/drivers/storage/class/cdrom_new/data.c
new file mode 100644 (file)
index 0000000..72ec53b
--- /dev/null
@@ -0,0 +1,283 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+
+Abstract:
+
+
+Environment:
+
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "ntddk.h"
+#include "cdrom.h"
+
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg("PAGEDATA")
+#endif
+
+/*
+
+#define CDROM_HACK_DEC_RRD                 (0x00000001)
+#define CDROM_HACK_FUJITSU_FMCD_10x        (0x00000002)
+    #define CDROM_HACK_HITACHI_1750            (0x00000004) - obsolete.
+#define CDROM_HACK_HITACHI_GD_2000         (0x00000008)
+#define CDROM_HACK_TOSHIBA_SD_W1101        (0x00000010)
+    #define CDROM_HACK_TOSHIBA_XM_3xx          (0x00000020) - obsolete.
+#define CDROM_HACK_NEC_CDDA                (0x00000040)
+#define CDROM_HACK_PLEXTOR_CDDA            (0x00000080)
+#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT  (0x00000100)
+#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200)
+#define CDROM_HACK_READ_CD_SUPPORTED       (0x00000400)
+
+*/
+
+CDROM_SCAN_FOR_SPECIAL_INFO CdromHackItems[] = {
+    // digital put out drives using 512 byte block sizes,
+    // and needed us to send a mode page to set the sector
+    // size back to 2048.
+    { "DEC"     , "RRD"                            , NULL,   0x0001 },
+    // these fujitsu drives take longer than ten seconds to
+    // timeout commands when audio discs are placed in them
+    { "FUJITSU" , "FMCD-101"                       , NULL,   0x0002 },
+    { "FUJITSU" , "FMCD-102"                       , NULL,   0x0002 },
+    // these hitachi drives don't work properly in PIO mode
+    //{ "HITACHI ", "CDR-1750S"                      , NULL,   0x0004 },
+    //{ "HITACHI ", "CDR-3650/1650S"                 , NULL,   0x0004 },
+    // this particular gem doesn't automatcially spin up
+    // on some media access commands.
+    { ""        , "HITACHI GD-2000"                , NULL,   0x0008 },
+    { ""        , "HITACHI DVD-ROM GD-2000"        , NULL,   0x0008 },
+    // this particular drive doesn't support DVD playback.
+    // just print an error message in CHK builds.
+    { "TOSHIBA ", "SD-W1101 DVD-RAM"               , NULL,   0x0010 },
+    // not sure what this device's issue was.  seems to
+    // require mode selects at various times.
+    //{ "TOSHIBA ", "CD-ROM XM-3"                    , NULL,   0x0020 },
+    // NEC defined a "READ_CD" type command before there was
+    // a standard, so fall back on this as an option.
+    { "NEC"     , NULL                             , NULL,   0x0040 },
+    // plextor defined a "READ_CD" type command before there was
+    // a standard, so fall back on this as an option.
+    { "PLEXTOR ", NULL                             , NULL,   0x0080 },
+    // this drive times out and sometimes disappears from the bus
+    // when send GET_CONFIGURATION commands.  don't send them.
+    { ""        , "LG DVD-ROM DRD-840B"            , NULL,   0x0100 },
+    { ""        , "SAMSUNG DVD-ROM SD-608"         , NULL,   0x0300 },
+    // these drives should have supported READ_CD, but at least
+    // some firmware revisions did not.  force READ_CD detection.
+    { ""        , "SAMSUNG DVD-ROM SD-"            , NULL,   0x0200 },
+    // the mitsumi drive below doesn't follow the block-only spec,
+    // and we end up hanging when sending it commands it doesn't
+    // understand.  this causes complications later, also.
+    { "MITSUMI ", "CR-4802TE       "               , NULL,   0x0100 },
+    // some drives return various funky errors (such as 3/2/0 NO_SEEK_COMPLETE)
+    // during the detection of READ_CD support, resulting in iffy detection.
+    // since they probably don't support mode switching, which is really old
+    // legacy stuff anyways, the ability to read digitally is lost when
+    // these drives return unexpected error codes.  note: MMC compliant drives
+    // are presumed to support READ_CD, as are DVD drives, and anything
+    // connected to a bus type other than IDE or SCSI, and therefore don't
+    // need to be here.
+    { "YAMAHA  ", "CRW8424S        "               , NULL,   0x0400 },
+    // Polling frequently on virtual optical devices created by Hyper-V will
+    // cause a significant perf / power hit. These devices need to be polled
+    // less frequently for device state changes.
+    { "MSFT    ", NULL                             , NULL,   0x2000 },
+    // and finally, a place to finish the list. :)
+    { NULL      , NULL                             , NULL,   0x0000 }
+};
+
+CDROM_SCAN_FOR_SPECIAL_INFO CdRomBadItems[] = {                     // Type (HH, slim) + WHQL Date, if known
+    { ""        , "MITSUMI CD-ROM FX240"           , NULL  ,   0x02 },
+    { ""        , "MITSUMI CD-ROM FX320"           , NULL  ,   0x02 },
+    { ""        , "MITSUMI CD-ROM FX322"           , NULL  ,   0x02 },
+    { ""        , "TEAC DV-28E-A"                  , "2.0A",   0x02 },
+    { ""        , "HP CD-Writer cd16h"             , "Q000",   0x02 },
+    { ""        , "_NEC NR-7800A"                  , "1.33",   0x02 },
+    { ""        , "COMPAQ CRD-8481B"               , NULL  ,   0x04 },
+    // The following is a list of device that report too many OpChange/Add events.
+    // They require ignoring (or not sending) the OpChange flag in the GESN command.
+    // This list contains vendor ID and product ID as separate strings for USB/1394 interface.
+    { "HL-DT-ST", "DVDRAM GMA-4020B"               , NULL  ,   0x10 }, // hh  , 2002/04/22
+    { "HL-DT-ST", "DVD-RW GCA-4020B"               , NULL  ,   0x10 }, // hh  , 2002/05/14
+    { "HL-DT-ST", "DVDRAM GSA-4040B"               , NULL  ,   0x10 }, // hh  , 2003/05/06
+    { "HL-DT-ST", "DVDRAM GMA-4040B"               , NULL  ,   0x10 }, // hh  , 2003/07/27
+    { "HL-DT-ST", "DVD-RW GWA-4040B"               , NULL  ,   0x10 }, // hh  , 2003/11/18
+    { "HL-DT-ST", "DVDRAM GSA-4081B"               , NULL  ,   0x10 }, // hh  , 2003/11/06
+    { "HL-DT-ST", "DVDRAM GSA-4082B"               , NULL  ,   0x10 }, // hh  , 2004/01/27
+    { "HL-DT-ST", "DVD-RW GWA-4082B"               , NULL  ,   0x10 }, // hh  , 2004/03/11
+    { "HL-DT-ST", "DVDRAM GSA-4120B"               , NULL  ,   0x10 }, // hh  , 2004/05/16
+    { "HL-DT-ST", "DVD+RW GRA-4120B"               , NULL  ,   0x10 }, // hh  , 2004/04/28
+    { "HL-DT-ST", "DVDRAM GSA-4160B"               , NULL  ,   0x10 }, // hh  , 2004/08/12
+    { "HL-DT-ST", "DVD-RW GWA-4160B"               , NULL  ,   0x10 }, // hh  , 2004/08/24
+    { "HL-DT-ST", "DVDRAM GSA-4163B"               , NULL  ,   0x10 }, // hh  , 2004/11/09
+    { "HL-DT-ST", "DVD-RW GWA-4163B"               , NULL  ,   0x10 }, // hh  , 2004/12/29
+    { "HL-DT-ST", "DVDRAM GSA-4165B"               , NULL  ,   0x10 }, // hh  , 2005/06/09
+    { "HL-DT-ST", "DVDRAM_GSA-4165B"               , NULL  ,   0x10 }, // hh  , 2005/06/28
+    { "HL-DT-ST", "DVD-RW GWA-4165B"               , NULL  ,   0x10 }, // hh  , 2005/08/23
+    { "HL-DT-ST", "DVDRAM GSA-4167B"               , NULL  ,   0x10 }, // hh  , 2005/07/01
+    { "HL-DT-ST", "DVDRAM GSA-H10N"                , NULL  ,   0x10 }, // hh  , 2006/02/16
+    { "HL-DT-ST", "DVDRAM_GSA-H10N"                , NULL  ,   0x10 }, // hh  , 2006/02/16
+    { "HL-DT-ST", "DVDRAM GSA-H10L"                , NULL  ,   0x10 }, // hh  , 2006/02/27
+    { "HL-DT-ST", "DVDRAM_GSA-H10L"                , NULL  ,   0x10 }, // hh  , 2006/04/21
+    { "HL-DT-ST", "DVDRAM GSA-H10A"                , NULL  ,   0x10 }, // hh  , 2006/01/03
+    { "HL-DT-ST", "DVDRAM_GSA-H10A"                , NULL  ,   0x10 }, // hh  , 2006/05/14
+    { "HL-DT-ST", "DVD-RW GSA-H11N"                , NULL  ,   0x10 }, // hh  , 2006/04/28
+    { "HL-DT-ST", "DVD-RW_GSA-H11N"                , NULL  ,   0x10 }, // hh  , 2006/02/22
+
+    { "HL-DT-ST", "DVDRAM GSA-4080N"               , NULL  ,   0x10 }, // slim, 2004/08/08
+    { "HL-DT-ST", "DVDRAM GMA-4080N"               , NULL  ,   0x10 }, // slim, 2004/11/09
+    { "HL-DT-ST", "DVD-RW GCA-4080N"               , NULL  ,   0x10 }, // slim, 2004/11/22
+    { "HL-DT-ST", "DVD-RW GWA-4080N"               , NULL  ,   0x10 }, // slim, 2004/08/17
+    { "HL-DT-ST", "DVDRAM GSA-4082N"               , NULL  ,   0x10 }, // slim, 2005/07/12
+    { "HL-DT-ST", "DVDRAM_GSA-4082N"               , NULL  ,   0x10 }, // slim, 2005/09/21
+    { "HL-DT-ST", "DVDRAM GMA-4082N"               , NULL  ,   0x10 }, // slim, 2005/10/20
+    { "HL-DT-ST", "DVD-RW GRA-4082N"               , NULL  ,   0x10 }, // slim, 2006/06/07
+    { "HL-DT-ST", "DVD-RW GWA-4082N"               , NULL  ,   0x10 }, // slim, 2005/05/24
+    { "HL-DT-ST", "DVDRAM GMA4082Nf"               , NULL  ,   0x10 }, // slim, 2006/02/28
+    { "HL-DT-ST", "DVDRAM GMA4082Nj"               , NULL  ,   0x10 }, // slim, 2006/01/26
+
+    { "HL-DT-ST", "DVDRAM GSA-4084N"               , NULL  ,   0x10 }, // slim, 2005/12/21
+    { "HL-DT-ST", "DVDRAM GMA-4084N"               , NULL  ,   0x10 }, // slim, 2006/02/15
+    { "HP"      , "DVD Writer 550s"                , NULL  ,   0x10 }, // slim, 2006/05/08
+    { "HL-DT-ST", "DVDRAM GSA-T10N"                , NULL  ,   0x10 }, // slim, 2006/07/26
+    { "HL-DT-ST", "DVDRAM_GSA-T10N"                , NULL  ,   0x10 }, // slim, 2006/07/26
+    { "HL-DT-ST", "DVD+-RW GSA-T11N"               , NULL  ,   0x10 }, // slim, 2006/07/25
+
+    { "HL-DT-ST", "DVD-ROM GDR8160B"               , NULL  ,   0x10 }, // hh  , 2001/10/12
+    { "COMPAQ"  , "DVD-ROM GDR8160B"               , NULL  ,   0x10 }, // hh  , 2001/11/08
+    { "HL-DT-ST", "DVD-ROM GDR8161B"               , NULL  ,   0x10 }, // hh  , 2002/07/19
+    { "HL-DT-ST", "DVD-ROM GDR8162B"               , NULL  ,   0x10 }, // hh  , 2003/04/22
+    { "HL-DT-ST", "DVD-ROM GDR8163B"               , NULL  ,   0x10 }, // hh  , 2004/05/19
+    { "HL-DT-ST", "DVD-ROM GDR8164B"               , NULL  ,   0x10 }, // hh  , 2005/06/29
+    { "HL-DT-ST", "DVD-ROM GDRH10N"                , NULL  ,   0x10 }, // hh  , 2006/03/07
+
+    { "HL-DT-ST", "DVD-ROM GDR8081N"               , NULL  ,   0x10 }, // slim, 2001/08/27
+    { "HL-DT-ST", "DVD-ROM GDR8082N"               , NULL  ,   0x10 }, // slim, 2003/02/02
+    { "HL-DT-ST", "DVD-ROM GDR8083N"               , NULL  ,   0x10 }, // slim, 2003/02/02
+    { "HL-DT-ST", "DVD-ROM GDR8085N"               , NULL  ,   0x10 }, // slim, 2005/11/10
+
+    { "HL-DT-ST", "RW/DVD GCC-4080N"               , NULL  ,   0x10 }, // slim, 2001/08/21
+    { "HL-DT-ST", "RW/DVD_GCC-4080N"               , NULL  ,   0x10 }, // slim,
+    { "HL-DT-ST", "RW/DVD GCC-4160N"               , NULL  ,   0x10 }, // slim, 2002/04/08
+    { "HL-DT-ST", "RW/DVD GCC-4240N"               , NULL  ,   0x10 }, // slim, 2002/04/26
+    { "HL-DT-ST", "RW/DVD GCC-4241N"               , NULL  ,   0x10 }, // slim, 2003/04/07
+    { "HL-DT-ST", "RW/DVD_GCC-4241N"               , NULL  ,   0x10 }, // slim, 2004/03/07
+    { "HL-DT-ST", "RW/DVD GCC-4242N"               , NULL  ,   0x10 }, // slim, 2003/12/21
+    { "HL-DT-ST", "RW/DVD GCC-4246N"               , NULL  ,   0x10 }, // slim, 2005/05/23
+    { "HL-DT-ST", "BD-RE  GBW-H10N"                , NULL  ,   0x10 }, // hh  , 2006/06/27
+
+    { "HL-DT-ST", "DVDRAM GSA-4083N"               , NULL  ,   0x10 }, // hh  , 2006/05/17
+    { "HL-DT-ST", "DVD+-RW GWA4083N"               , NULL  ,   0x10 }, // hh  , 2006/06/05
+
+    { "PIONEER",  "DVD-RW  DVR-106D"               , NULL  ,   0x10 }, // hh  , ?
+    { "ASUS",     "DVD-RW DRW-0402P"               , NULL  ,   0x10 }, // hh  , ?
+
+    //
+    // This list contains devices that claims to support asynchronous notification, but
+    // doesn't handle it well (e.g., some TSST devices will not report media removal if
+    // the GESN command is sent down immediately after the AN interrupt, they need some
+    // time in between to be able to correctly report media removal).
+    //
+
+    { "TSSTcorp", "CDDVDW SN-S083A"                , "SB00",   0x40 }, // slim, ?
+
+    //
+    // This list contains vendor ID and product ID as a single string for ATAPI interface.
+    //
+
+    { "", "HL-DT-ST DVDRAM GMA-4020B"              , NULL  ,   0x10 }, // hh  , 2002/04/22
+    { "", "HL-DT-ST DVD-RW GCA-4020B"              , NULL  ,   0x10 }, // hh  , 2002/05/14
+    { "", "HL-DT-ST DVDRAM GSA-4040B"              , NULL  ,   0x10 }, // hh  , 2003/05/06
+    { "", "HL-DT-ST DVDRAM GMA-4040B"              , NULL  ,   0x10 }, // hh  , 2003/07/27
+    { "", "HL-DT-ST DVD-RW GWA-4040B"              , NULL  ,   0x10 }, // hh  , 2003/11/18
+    { "", "HL-DT-ST DVDRAM GSA-4081B"              , NULL  ,   0x10 }, // hh  , 2003/11/06
+    { "", "HL-DT-ST DVDRAM GSA-4082B"              , NULL  ,   0x10 }, // hh  , 2004/01/27
+    { "", "HL-DT-ST DVD-RW GWA-4082B"              , NULL  ,   0x10 }, // hh  , 2004/03/11
+    { "", "HL-DT-ST DVDRAM GSA-4120B"              , NULL  ,   0x10 }, // hh  , 2004/05/16
+    { "", "HL-DT-ST DVD+RW GRA-4120B"              , NULL  ,   0x10 }, // hh  , 2004/04/28
+    { "", "HL-DT-ST DVDRAM GSA-4160B"              , NULL  ,   0x10 }, // hh  , 2004/08/12
+    { "", "HL-DT-ST DVD-RW GWA-4160B"              , NULL  ,   0x10 }, // hh  , 2004/08/24
+    { "", "HL-DT-ST DVDRAM GSA-4163B"              , NULL  ,   0x10 }, // hh  , 2004/11/09
+    { "", "HL-DT-ST DVD-RW GWA-4163B"              , NULL  ,   0x10 }, // hh  , 2004/12/29
+    { "", "HL-DT-ST DVDRAM GSA-4165B"              , NULL  ,   0x10 }, // hh  , 2005/06/09
+    { "", "HL-DT-ST DVDRAM_GSA-4165B"              , NULL  ,   0x10 }, // hh  , 2005/06/28
+    { "", "HL-DT-ST DVD-RW GWA-4165B"              , NULL  ,   0x10 }, // hh  , 2005/08/23
+    { "", "HL-DT-ST DVDRAM GSA-4167B"              , NULL  ,   0x10 }, // hh  , 2005/07/01
+    { "", "HL-DT-ST DVDRAM GSA-H10N"               , NULL  ,   0x10 }, // hh  , 2006/02/16
+    { "", "HL-DT-ST DVDRAM_GSA-H10N"               , NULL  ,   0x10 }, // hh  , 2006/02/16
+    { "", "HL-DT-ST DVDRAM GSA-H10L"               , NULL  ,   0x10 }, // hh  , 2006/02/27
+    { "", "HL-DT-ST DVDRAM_GSA-H10L"               , NULL  ,   0x10 }, // hh  , 2006/04/21
+    { "", "HL-DT-ST DVDRAM GSA-H10A"               , NULL  ,   0x10 }, // hh  , 2006/01/03
+    { "", "HL-DT-ST DVDRAM_GSA-H10A"               , NULL  ,   0x10 }, // hh  , 2006/05/14
+    { "", "HL-DT-ST DVD-RW GSA-H11N"               , NULL  ,   0x10 }, // hh  , 2006/04/28
+    { "", "HL-DT-ST DVD-RW_GSA-H11N"               , NULL  ,   0x10 }, // hh  , 2006/02/22
+
+    { "", "HL-DT-ST DVDRAM GSA-4080N"              , NULL  ,   0x10 }, // slim, 2004/08/08
+    { "", "HL-DT-ST DVDRAM GMA-4080N"              , NULL  ,   0x10 }, // slim, 2004/11/09
+    { "", "HL-DT-ST DVD-RW GCA-4080N"              , NULL  ,   0x10 }, // slim, 2004/11/22
+    { "", "HL-DT-ST DVD-RW GWA-4080N"              , NULL  ,   0x10 }, // slim, 2004/08/17
+    { "", "HL-DT-ST DVDRAM GSA-4082N"              , NULL  ,   0x10 }, // slim, 2005/07/12
+    { "", "HL-DT-ST DVDRAM_GSA-4082N"              , NULL  ,   0x10 }, // slim, 2005/09/21
+    { "", "HL-DT-ST DVDRAM GMA-4082N"              , NULL  ,   0x10 }, // slim, 2005/10/20
+    { "", "HL-DT-ST DVD-RW GRA-4082N"              , NULL  ,   0x10 }, // slim, 2006/06/07
+    { "", "HL-DT-ST DVD-RW GWA-4082N"              , NULL  ,   0x10 }, // slim, 2005/05/24
+    { "", "HL-DT-ST DVDRAM GMA4082Nf"              , NULL  ,   0x10 }, // slim, 2006/02/28
+    { "", "HL-DT-ST DVDRAM GMA4082Nj"              , NULL  ,   0x10 }, // slim, 2006/01/26
+
+    { "", "HL-DT-ST DVDRAM GSA-4084N"              , NULL  ,   0x10 }, // slim, 2005/12/21
+    { "", "HL-DT-ST DVDRAM GMA-4084N"              , NULL  ,   0x10 }, // slim, 2006/02/15
+    { "", "HP DVD Writer 550s"                     , NULL  ,   0x10 }, // slim, 2006/05/08
+    { "", "HL-DT-ST DVDRAM GSA-T10N"               , NULL  ,   0x10 }, // slim, 2006/07/26
+    { "", "HL-DT-ST DVDRAM_GSA-T10N"               , NULL  ,   0x10 }, // slim, 2006/07/26
+    { "", "HL-DT-ST DVD+-RW GSA-T11N"              , NULL  ,   0x10 }, // slim, 2006/07/25
+
+    { "", "HL-DT-ST DVD-ROM GDR8160B"              , NULL  ,   0x10 }, // hh  , 2001/10/12
+    { "", "COMPAQ DVD-ROM GDR8160B"                , NULL  ,   0x10 }, // hh  , 2001/11/08
+    { "", "HL-DT-ST DVD-ROM GDR8161B"              , NULL  ,   0x10 }, // hh  , 2002/07/19
+    { "", "HL-DT-ST DVD-ROM GDR8162B"              , NULL  ,   0x10 }, // hh  , 2003/04/22
+    { "", "HL-DT-ST DVD-ROM GDR8163B"              , NULL  ,   0x10 }, // hh  , 2004/05/19
+    { "", "HL-DT-ST DVD-ROM GDR8164B"              , NULL  ,   0x10 }, // hh  , 2005/06/29
+    { "", "HL-DT-ST DVD-ROM GDRH10N"               , NULL  ,   0x10 }, // hh  , 2006/03/07
+
+    { "", "HL-DT-ST DVD-ROM GDR8081N"              , NULL  ,   0x10 }, // slim, 2001/08/27
+    { "", "HL-DT-ST DVD-ROM GDR8082N"              , NULL  ,   0x10 }, // slim, 2003/02/02
+    { "", "HL-DT-ST DVD-ROM GDR8083N"              , NULL  ,   0x10 }, // slim, 2003/02/02
+    { "", "HL-DT-ST DVD-ROM GDR8085N"              , NULL  ,   0x10 }, // slim, 2005/11/10
+
+    { "", "HL-DT-ST RW/DVD GCC-4080N"              , NULL  ,   0x10 }, // slim, 2001/08/21
+    { "", "HL-DT-ST RW/DVD_GCC-4080N"              , NULL  ,   0x10 }, // slim,
+    { "", "HL-DT-ST RW/DVD GCC-4160N"              , NULL  ,   0x10 }, // slim, 2002/04/08
+    { "", "HL-DT-ST RW/DVD GCC-4240N"              , NULL  ,   0x10 }, // slim, 2002/04/26
+    { "", "HL-DT-ST RW/DVD GCC-4241N"              , NULL  ,   0x10 }, // slim, 2003/04/07
+    { "", "HL-DT-ST RW/DVD_GCC-4241N"              , NULL  ,   0x10 }, // slim, 2004/03/07
+    { "", "HL-DT-ST RW/DVD GCC-4242N"              , NULL  ,   0x10 }, // slim, 2003/12/21
+    { "", "HL-DT-ST RW/DVD GCC-4246N"              , NULL  ,   0x10 }, // slim, 2005/05/23
+    { "", "HL-DT-ST BD-RE  GBW-H10N"               , NULL  ,   0x10 }, // hh  , 2006/06/27
+
+    { "", "HL-DT-ST DVDRAM GSA-4083N"              , NULL  ,   0x10 }, // hh  , 2006/05/17
+    { "", "HL-DT-ST DVD+-RW GWA4083N"              , NULL  ,   0x10 }, // hh  , 2006/06/05
+
+    { "", "PIONEER DVD-RW  DVR-106D"               , NULL  ,   0x10 }, // hh  , ?
+    { "", "ASUS DVD-RW DRW-0402P"                  , NULL  ,   0x10 }, // hh  , ?
+
+
+    // Sony sourced some drives from LG also....
+
+    { NULL      , NULL                             , NULL  ,   0x00 },
+};
+
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg()
+#endif
diff --git a/drivers/storage/class/cdrom_new/guid.c b/drivers/storage/class/cdrom_new/guid.c
new file mode 100644 (file)
index 0000000..8af1f87
--- /dev/null
@@ -0,0 +1,16 @@
+/*--
+Copyright (C) Microsoft Corporation. All rights reserved.
+--*/
+
+#include "initguid.h"
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "mountmgr.h"
+#include "ioevent.h"
+#include "devpkey.h"
+#include "wdmguid.h"
+
+// no code, just GUIDs being defined
+
+
+
diff --git a/drivers/storage/class/cdrom_new/init.c b/drivers/storage/class/cdrom_new/init.c
new file mode 100644 (file)
index 0000000..dfa8e96
--- /dev/null
@@ -0,0 +1,2742 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    init.c
+
+Abstract:
+
+    Initialization routines for CDROM
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "ntstrsafe.h"
+#include "devpkey.h"
+
+#include "cdrom.h"
+#include "scratch.h"
+#include "mmc.h"
+
+#ifdef DEBUG_USE_WPP
+#include "init.tmh"
+#endif
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitAllocateBuffers(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveScsiAddress(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ PSCSI_ADDRESS           ScsiAddress
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveDescriptorsAndTransferLength(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceScanSpecialDevices(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitMmcContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetMmcSupportInfo(
+    _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _Out_ PBOOLEAN                  IsMmcDevice
+    );
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceIsPortable(
+    _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _Out_ PBOOLEAN                  IsPortable
+    );
+#endif
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceClaimRelease)
+#pragma alloc_text(PAGE, DeviceEvtSelfManagedIoInit)
+
+#pragma alloc_text(PAGE, DeviceInitReleaseQueueContext)
+#pragma alloc_text(PAGE, DeviceInitAllocateBuffers)
+#pragma alloc_text(PAGE, DeviceInitPowerContext)
+#pragma alloc_text(PAGE, DeviceCreateWellKnownName)
+#pragma alloc_text(PAGE, DeviceRetrieveScsiAddress)
+#pragma alloc_text(PAGE, DeviceRetrieveDescriptorsAndTransferLength)
+#pragma alloc_text(PAGE, DeviceInitializeHotplugInfo)
+#pragma alloc_text(PAGE, DeviceScanSpecialDevices)
+#pragma alloc_text(PAGE, DeviceGetTimeOutValueFromRegistry)
+#pragma alloc_text(PAGE, DeviceGetMmcSupportInfo)
+#pragma alloc_text(PAGE, DeviceRetrieveDescriptor)
+#pragma alloc_text(PAGE, DeviceRetrieveHackFlagsFromRegistry)
+#pragma alloc_text(PAGE, DeviceScanForSpecial)
+#pragma alloc_text(PAGE, DeviceHackFlagsScan)
+#pragma alloc_text(PAGE, DeviceInitMmcContext)
+#pragma alloc_text(PAGE, ScanForSpecialHandler)
+#pragma alloc_text(PAGE, DeviceSetRawReadInfo)
+#pragma alloc_text(PAGE, DeviceInitializeDvd)
+#pragma alloc_text(PAGE, DeviceCacheDeviceInquiryData)
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+#pragma alloc_text(PAGE, DeviceIsPortable)
+#endif
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+#pragma warning(disable:26000) // read overflow reported because of pointer type conversion
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceClaimRelease(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 Release
+    )
+/*++
+
+Routine Description:
+
+    This function claims a device in the port driver.  The port driver object
+    is updated with the correct driver object if the device is successfully
+    claimed.
+
+Arguments:
+
+    Device - The WDFDEVICE that needs to be claimed or released.
+
+    Release - Indicates the logical unit should be released rather than claimed.
+
+Return Value:
+
+    Returns a status indicating success or failure of the operation.
+
+--*/
+{
+    NTSTATUS                status;
+    SCSI_REQUEST_BLOCK      srb = {0};
+    WDF_MEMORY_DESCRIPTOR   descriptor;
+    WDFREQUEST              request;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+
+    PAGED_CODE();
+
+    //Create a request
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                            CDROM_REQUEST_CONTEXT);
+
+    status = WdfRequestCreate(&attributes,
+                              DeviceExtension->IoTarget,
+                              &request);
+
+    if (NT_SUCCESS(status))
+    {
+        //fill up srb structure
+        srb.OriginalRequest = WdfRequestWdmGetIrp(request);
+        NT_ASSERT(srb.OriginalRequest != NULL);
+
+        srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+        srb.Function = Release
+                        ? SRB_FUNCTION_RELEASE_DEVICE
+                        : SRB_FUNCTION_CLAIM_DEVICE;
+
+
+        WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&descriptor,
+                                          &srb,
+                                          sizeof(srb));
+
+        status = WdfIoTargetSendInternalIoctlOthersSynchronously(DeviceExtension->IoTarget,
+                                                                 request,
+                                                                 IOCTL_SCSI_EXECUTE_NONE,
+                                                                 &descriptor,
+                                                                 NULL,
+                                                                 NULL,
+                                                                 NULL,
+                                                                 NULL);
+
+        NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+        // The request should be deleted.
+        WdfObjectDelete(request);
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+                        "DeviceClaimRelease: Failed to %s device, status: 0x%X\n",
+                        Release ? "Release" : "Claim",
+                        status));
+        }
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
+                    "DeviceClaimRelease: Failed to create request, status: 0x%X\n",
+                    status));
+    }
+
+    if (Release)
+    {
+        // We only release the device when we don't want to manage it.
+        // The failure status does not matter.
+        status = STATUS_SUCCESS;
+    }
+
+    return status;
+} // end DeviceClaimRelease()
+
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DeviceEvtSelfManagedIoInit(
+    _In_ WDFDEVICE      Device
+    )
+/*++
+
+Routine Description:
+
+    This routine is called only once after the device is added in system, so it's used to do
+    hardware-dependent device initialization work and resource allocation.
+    If this routine fails, DeviceEvtSelfManagedIoCleanup will be invoked by the framework.
+
+Arguments:
+
+    Device - Handle to device object
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
+
+    PAGED_CODE();
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
+                "DeviceEvtSelfManagedIoInit: WDFDEVICE %p is being started.\n",
+                Device));
+
+    deviceExtension = DeviceGetExtension(Device);
+
+    // 1. Set/retrieve basic information, some of the following operations may depend on it
+    if (NT_SUCCESS(status))
+    {
+        // We do not care if this function fails, SCSI address is mainly for debugging/tracing purposes.
+        (VOID) DeviceRetrieveScsiAddress(deviceExtension, &deviceExtension->ScsiAddress);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceRetrieveDescriptorsAndTransferLength(deviceExtension);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // This function should be called after DeviceRetrieveDescriptorsAndTransferLength()
+        // It depends on MaxTransferLenth fields.
+        status = DeviceInitAllocateBuffers(deviceExtension);
+    }
+
+    // 2. The following functions depend on the allocated buffers.
+
+    // perf re-enable after failing. Q: Is this one used by cdrom.sys?
+    if (NT_SUCCESS(status))
+    {
+        // allow perf to be re-enabled after a given number of failed IOs
+        // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
+        ULONG t = CLASS_PERF_RESTORE_MINIMUM;
+
+        DeviceGetParameter(deviceExtension,
+                           CLASSP_REG_SUBKEY_NAME,
+                           CLASSP_REG_PERF_RESTORE_VALUE_NAME,
+                           &t);
+        if (t >= CLASS_PERF_RESTORE_MINIMUM)
+        {
+            deviceExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
+        }
+    }
+
+    // 3. Retrieve information about special devices and hack flags.
+    if (NT_SUCCESS(status))
+    {
+        DeviceRetrieveHackFlagsFromRegistry(deviceExtension);
+        // scan for bad items.
+        DeviceScanForSpecial(deviceExtension, CdRomBadItems, DeviceHackFlagsScan);
+        // Check to see if it's a special device that needs special error process.
+        DeviceScanSpecialDevices(deviceExtension);  // may send command to device
+    }
+
+    // 4. Initialize the hotplug information only after the ScanForSpecial routines,
+    // as it relies upon the hack flags - deviceExtension->PrivateFdoData->HackFlags.
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceInitializeHotplugInfo(deviceExtension);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // cache the device's inquiry data
+        status = DeviceCacheDeviceInquiryData(deviceExtension);
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to cache the device's inquiry data, failng %!STATUS!\n",
+                        status
+                        ));
+        }
+    }
+
+    // 5. Initialize MMC context, media change notification stuff and read media capacity
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceInitializeMediaChangeDetection(deviceExtension);
+    }
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceInitMmcContext(deviceExtension);
+    }
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceInitializeZPODD(deviceExtension);
+    }
+    if (NT_SUCCESS(status))
+    {
+        // Do READ CAPACITY. This SCSI command returns the last sector address
+        // on the device and the bytes per sector. These are used to calculate
+        // the drive capacity in bytes.
+        status = MediaReadCapacity(Device);
+
+        // If READ CAPACITY succeeded, we can safely conclude that there is a media present
+        if (NT_SUCCESS(status))
+        {
+            DeviceSetMediaChangeStateEx(deviceExtension,
+                                        MediaPresent,
+                                        NULL);
+        }
+
+        // READ CAPACITY is not critical for init, ignore all errors occuring during its execution
+        status = STATUS_SUCCESS;
+    }
+
+    // 6. Perform DVD-specific initialization
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceInitializeDvd(Device);
+    }
+
+    // 7. Miscellaneous initialization actions
+    if (NT_SUCCESS(status))
+    {
+        if (deviceExtension->PrivateFdoData != NULL)
+        {
+            deviceExtension->PrivateFdoData->Perf.OriginalSrbFlags = deviceExtension->SrbFlags;
+        }
+
+        if (deviceExtension->DeviceAdditionalData.Mmc.IsWriter)
+        {
+            // OPC can really take this long per IMAPIv1 timeout....
+            deviceExtension->TimeOutValue = max(deviceExtension->TimeOutValue, SCSI_CDROM_OPC_TIMEOUT);
+        }
+    }
+
+    // 8. Enable the main timer, create ARC name as needed
+    if (NT_SUCCESS(status))
+    {
+        // Device successfully added and initialized, increase CdRomCount.
+        IoGetConfigurationInformation()->CdRomCount++;
+
+        deviceExtension->IsInitialized = TRUE;
+
+        DeviceEnableMainTimer(deviceExtension);
+
+    }
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+    // 9. Set volume interface properties
+    if (NT_SUCCESS(status))
+    {
+        BOOLEAN isCritical = FALSE;
+        BOOLEAN isPortable = FALSE;
+        BOOLEAN isRemovable = TEST_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA);
+        DEVPROP_BOOLEAN propCritical = DEVPROP_FALSE;
+        DEVPROP_BOOLEAN propPortable = DEVPROP_FALSE;
+        DEVPROP_BOOLEAN propRemovable = DEVPROP_FALSE;
+
+        status = DeviceIsPortable(deviceExtension, &isPortable);
+
+        if (NT_SUCCESS(status))
+        {
+            if (isPortable) {
+                SET_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_PORTABLE_DEVICE);
+            }
+
+            propPortable = isPortable ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+            status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
+                                                      &DEVPKEY_Storage_Portable,
+                                                      0,
+                                                      0,
+                                                      DEVPROP_TYPE_BOOLEAN,
+                                                      sizeof(DEVPROP_BOOLEAN),
+                                                      &propPortable);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            propRemovable = isRemovable ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+            status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
+                                                      &DEVPKEY_Storage_Removable_Media,
+                                                      0,
+                                                      0,
+                                                      DEVPROP_TYPE_BOOLEAN,
+                                                      sizeof(DEVPROP_BOOLEAN),
+                                                      &propRemovable);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            isCritical = TEST_FLAG(deviceExtension->DeviceObject->Flags,
+                                   (DO_SYSTEM_SYSTEM_PARTITION |
+                                    DO_SYSTEM_BOOT_PARTITION   |
+                                    DO_SYSTEM_CRITICAL_PARTITION));
+
+            propCritical = isCritical ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+            status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
+                                                      &DEVPKEY_Storage_System_Critical,
+                                                      0,
+                                                      0,
+                                                      DEVPROP_TYPE_BOOLEAN,
+                                                      sizeof(DEVPROP_BOOLEAN),
+                                                      &propCritical);
+        }
+
+    }
+#endif
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitReleaseQueueContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Part of device initialize routine. Initialize ReleaseQueue related stuff.
+
+Arguments:
+
+    DeviceExtension - device extension of WDFDEVICE.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+
+    PAGED_CODE();
+
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                            CDROM_REQUEST_CONTEXT);
+    attributes.ParentObject = DeviceExtension->Device;
+
+    status = WdfRequestCreate(&attributes,
+                              DeviceExtension->IoTarget,
+                              &(DeviceExtension->ReleaseQueueRequest));
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Cannot create the release queue request\n"));
+
+        return status;
+    }
+
+    // Initialize ReleaseQueueInputMemory, a wrapper around ReleaseQueueSrb
+    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+    attributes.ParentObject = DeviceExtension->ReleaseQueueRequest;
+
+    status = WdfMemoryCreatePreallocated(&attributes,
+                                         &DeviceExtension->ReleaseQueueSrb,
+                                         sizeof(SCSI_REQUEST_BLOCK),
+                                         &DeviceExtension->ReleaseQueueInputMemory);
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Failed to allocate ReleaseQueueSrb.\n"));
+
+        return status;
+    }
+
+    // Preformat the release queue request here to ensure that this call will never
+    // fail during an actual release of the queue.
+    if (NT_SUCCESS(status))
+    {
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                                DeviceExtension->ReleaseQueueRequest,
+                                                                IOCTL_SCSI_EXECUTE_NONE,
+                                                                DeviceExtension->ReleaseQueueInputMemory,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL,
+                                                                NULL);
+    }
+
+    // Set a CompletionRoutine callback function for ReleaseQueueRequest.
+    if (NT_SUCCESS(status))
+    {
+        WdfRequestSetCompletionRoutine(DeviceExtension->ReleaseQueueRequest,
+                                       DeviceReleaseQueueCompletion,
+                                       DeviceExtension->Device);
+    }
+
+    // Create a spinlock for ReleaseQueueRequest
+    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+    attributes.ParentObject = DeviceExtension->Device;
+
+    status = WdfSpinLockCreate(&attributes,
+                               &(DeviceExtension->ReleaseQueueSpinLock));
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
+                    "DeviceInitReleaseQueueContext: Cannot create the release queue spinlock\n"));
+
+        return status;
+    }
+
+    // Initialize miscellaneous ReleaseQueue related fields
+    DeviceExtension->ReleaseQueueNeeded = FALSE;
+    DeviceExtension->ReleaseQueueInProgress = FALSE;
+    DeviceExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitPowerContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Part of device initialize routine. Initialize PowerContext related stuff.
+
+Arguments:
+
+    DeviceExtension - device extension of WDFDEVICE.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    WDF_OBJECT_ATTRIBUTES   attributes;
+
+    PAGED_CODE();
+
+    // create request object for Power operations
+
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                            CDROM_REQUEST_CONTEXT);
+    attributes.ParentObject = DeviceExtension->Device;
+
+    status = WdfRequestCreate(&attributes,
+                              DeviceExtension->IoTarget,
+                              &(DeviceExtension->PowerContext.PowerRequest) );
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "Cannot create the power request object.\n"));
+
+        return status;
+    }
+
+    // Preformat the power request. With this being done, we never need to worry about
+    // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later.
+    status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                            DeviceExtension->PowerContext.PowerRequest,
+                                                            IOCTL_SCSI_EXECUTE_IN,
+                                                            NULL, NULL,
+                                                            NULL, NULL,
+                                                            NULL, NULL);
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceCreateWellKnownName(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine creates a symbolic link to the cdrom device object
+    under \dosdevices.  The number of the cdrom device does not neccessarily
+    match between \dosdevices and \device, but usually will be the same.
+
+    Saves the buffer
+
+Arguments:
+
+    DeviceObject -
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    UNICODE_STRING  unicodeLinkName = {0};
+    WCHAR           wideLinkName[64] = {0};
+    PWCHAR          savedName;
+
+    LONG            cdromNumber = DeviceExtension->DeviceNumber;
+
+    PAGED_CODE();
+
+    // if already linked, assert then return
+    if (DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer != NULL)
+    {
+
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
+                    "DeviceCreateWellKnownName: link already exists %p\n",
+                    DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer));
+
+        NT_ASSERT(FALSE);
+
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    // find an unused CdRomNN to link to.
+    // It's doing this way because the same might be used for other device in another driver.
+    do
+    {
+        status = RtlStringCchPrintfW((NTSTRSAFE_PWSTR)wideLinkName,
+                                     RTL_NUMBER_OF(wideLinkName),
+                                     L"\\DosDevices\\CdRom%d",
+                                     cdromNumber);
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
+                        "DeviceCreateWellKnownName: Format symbolic link failed with error: 0x%X\n", status));
+            return status;
+        }
+
+        RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
+
+        status = WdfDeviceCreateSymbolicLink(DeviceExtension->Device,
+                                             &unicodeLinkName);
+
+        cdromNumber++;
+
+    } while((status == STATUS_OBJECT_NAME_COLLISION) ||
+            (status == STATUS_OBJECT_NAME_EXISTS));
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
+                    "DeviceCreateWellKnownName: Error %lx linking %wZ to "
+                    "device %wZ\n",
+                    status,
+                    &unicodeLinkName,
+                    &(DeviceExtension->DeviceName)));
+        return status;
+    }
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                "DeviceCreateWellKnownName: successfully linked %wZ "
+                "to device %wZ\n",
+                &unicodeLinkName,
+                &(DeviceExtension->DeviceName)));
+
+    // Save away the symbolic link name in the driver data block.  We need
+    // it so we can delete the link when the device is removed.
+    savedName = ExAllocatePoolWithTag(PagedPool,
+                                      unicodeLinkName.MaximumLength,
+                                      CDROM_TAG_STRINGS);
+
+    if (savedName == NULL)
+    {
+        // Test Note: test path should excise here to see if the symbolic is deleted by framework.
+        // IoDeleteSymbolicLink(&unicodeLinkName);
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                    "DeviceCreateWellKnownName: unable to allocate memory.\n"));
+
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(savedName, unicodeLinkName.MaximumLength);
+    RtlCopyMemory(savedName, unicodeLinkName.Buffer, unicodeLinkName.MaximumLength);
+
+    RtlInitUnicodeString(&(DeviceExtension->DeviceAdditionalData.WellKnownName), savedName);
+
+    // the name was saved and the link created
+
+    return STATUS_SUCCESS;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveScsiAddress(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ PSCSI_ADDRESS           ScsiAddress
+    )
+/*++
+
+Routine Description:
+
+    retrieve SCSI address information and put into device extension
+
+Arguments:
+
+    DeviceExtension - device context.
+    ScsiAddress - the buffer to put the scsi address info.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status;
+    WDF_MEMORY_DESCRIPTOR   outputDescriptor;
+
+    PAGED_CODE();
+
+    if ((DeviceExtension == NULL) ||
+        (ScsiAddress == NULL))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    //Get IOTARGET for sending request to port driver.
+    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor,
+                                      (PVOID)ScsiAddress,
+                                      sizeof(SCSI_ADDRESS));
+
+    status = WdfIoTargetSendIoctlSynchronously(DeviceExtension->IoTarget,
+                                               NULL,
+                                               IOCTL_SCSI_GET_ADDRESS,
+                                               NULL,
+                                               &outputDescriptor,
+                                               NULL,
+                                               NULL);
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                    "DeviceRetrieveScsiAddress: Get Address failed %lx\n",
+                    status));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                    "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
+                    ScsiAddress->PortNumber,
+                    ScsiAddress->PathId,
+                    ScsiAddress->TargetId,
+                    ScsiAddress->Lun));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitAllocateBuffers(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Part of device initialize routine.
+    Allocate all buffers in Device Extension.
+
+Arguments:
+
+    DeviceExtension - device extension of WDFDEVICE.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PVOID               senseData = NULL;
+
+    PAGED_CODE();
+
+    // allocate a private extension for class data
+    if (DeviceExtension->PrivateFdoData == NULL)
+    {
+        DeviceExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                                sizeof(CDROM_PRIVATE_FDO_DATA),
+                                                                CDROM_TAG_PRIVATE_DATA);
+    }
+
+    if (DeviceExtension->PrivateFdoData == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        // initialize the struct's various fields.
+        RtlZeroMemory(DeviceExtension->PrivateFdoData, sizeof(CDROM_PRIVATE_FDO_DATA));
+    }
+
+    // Allocate request sense buffer.
+    senseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                      SENSE_BUFFER_SIZE,
+                                      CDROM_TAG_SENSE_INFO);
+
+    if (senseData == NULL)
+    {
+        // The buffer cannot be allocated.
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        // Set the sense data pointer in the device extension.
+        DeviceExtension->SenseData = senseData;
+    }
+
+    // Allocate scratch buffer -- Must occur after determining
+    // max transfer size, but before other CD specific detection
+    // (which relies upon this buffer).
+    if (!ScratchBuffer_Allocate(DeviceExtension))
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                    "Failed to allocate scratch buffer, failing  %!STATUS!\n",
+                    status
+                    ));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveDescriptorsAndTransferLength(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Part of device initialize routine.
+    Retrieve Device Descriptor and Adaptor Descriptor.
+
+Arguments:
+
+    DeviceExtension - device extension of WDFDEVICE.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    STORAGE_PROPERTY_ID propertyId;
+
+    PAGED_CODE();
+
+    if (NT_SUCCESS(status))
+    {
+        // Call port driver to get adapter capabilities.
+        propertyId = StorageAdapterProperty;
+
+        status = DeviceRetrieveDescriptor(DeviceExtension->Device,
+                                          &propertyId,
+                                          (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->AdapterDescriptor);
+    }
+    if (NT_SUCCESS(status))
+    {
+        // Call port driver to get device descriptor.
+        propertyId = StorageDeviceProperty;
+
+        status = DeviceRetrieveDescriptor(DeviceExtension->Device,
+                                          &propertyId,
+                                          (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->DeviceDescriptor);
+    }
+    if (NT_SUCCESS(status))
+    {
+        // Call port driver to get device power property.
+        // Not all port drivers support this property, and it's not fatal if this query fails.
+        propertyId = StorageDevicePowerProperty;
+
+        (void) DeviceRetrieveDescriptor(DeviceExtension->Device,
+                                        &propertyId,
+                                        (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->PowerDescriptor);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Determine the maximum page-aligned and non-page-aligned transfer
+        // lengths here, so we needn't do this in common READ/WRITE code paths
+
+        // start with the number of pages the adapter can support
+        ULONG maxAlignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
+        ULONG maxUnalignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
+
+
+        // Unaligned buffers could cross a page boundary.
+        if (maxUnalignedTransfer > 1)
+        {
+            maxUnalignedTransfer--;
+        }
+
+        // if we'd overflow multiplying by page size, just max out the
+        // transfer length allowed by the number of pages limit.
+        if (maxAlignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
+        {
+            maxAlignedTransfer = (ULONG)-1;
+        }
+        else
+        {
+            maxAlignedTransfer *= PAGE_SIZE;
+        }
+
+        if (maxUnalignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
+        {
+            maxUnalignedTransfer = (ULONG)-1;
+        }
+        else
+        {
+            maxUnalignedTransfer *= PAGE_SIZE;
+        }
+
+        // finally, take the smaller of the above and the adapter's
+        // reported maximum number of bytes per transfer.
+        maxAlignedTransfer   = min(maxAlignedTransfer,   DeviceExtension->AdapterDescriptor->MaximumTransferLength);
+        maxUnalignedTransfer = min(maxUnalignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength);
+
+        // Make sure the values are reasonable and not zero.
+        maxAlignedTransfer   = max(maxAlignedTransfer,   PAGE_SIZE);
+        maxUnalignedTransfer = max(maxUnalignedTransfer, PAGE_SIZE);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                  "Device %p Max aligned/unaligned transfer size is %x/%x\n",
+                  DeviceExtension->Device,
+                  maxAlignedTransfer,
+                  maxUnalignedTransfer
+                  ));
+        DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes = maxAlignedTransfer;
+        DeviceExtension->DeviceAdditionalData.MaxUnalignedTransferBytes = maxUnalignedTransfer;
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "DeviceRetrieveDescriptorsAndTransferLength failed %lx\n", status));
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceRetrieveDescriptor(
+    _In_ WDFDEVICE                              Device,
+    _In_ PSTORAGE_PROPERTY_ID                   PropertyId,
+    _Outptr_ PSTORAGE_DESCRIPTOR_HEADER*        Descriptor
+    )
+/*++
+
+Routine Description:
+
+    This routine will perform a query for the specified property id and will
+    allocate a non-paged buffer to store the data in.  It is the responsibility
+    of the caller to ensure that this buffer is freed.
+
+    This routine must be run at IRQL_PASSIVE_LEVEL
+
+Arguments:
+
+    Device - the device object
+    PropertyId - type of property to retrieve
+    Descriptor - buffer allocated in this function to hold the descriptor data
+
+Return Value:
+
+    status
+
+--*/
+{
+    NTSTATUS                status;
+    WDF_MEMORY_DESCRIPTOR   memoryDescriptor;
+
+    STORAGE_PROPERTY_QUERY  query = {0};
+    ULONG                   bufferLength = 0;
+
+    PSTORAGE_DESCRIPTOR_HEADER  descriptor = NULL;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+
+    PAGED_CODE();
+
+    // Set the passed-in descriptor pointer to NULL as default
+    *Descriptor = NULL;
+
+    // On the first pass we just want to get the first few
+    // bytes of the descriptor so we can read it's size
+    query.PropertyId = *PropertyId;
+    query.QueryType = PropertyStandardQuery;
+
+    descriptor = (PVOID)&query;
+
+    NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
+
+    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
+                                      (PVOID)&query,
+                                      sizeof(STORAGE_PROPERTY_QUERY));
+
+    status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
+                                               NULL,
+                                               IOCTL_STORAGE_QUERY_PROPERTY,
+                                               &memoryDescriptor,
+                                               &memoryDescriptor,
+                                               NULL,
+                                               NULL);
+
+    if(!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: error %lx trying to "
+                       "query properties #1\n", status));
+        return status;
+    }
+
+    if (descriptor->Size == 0)
+    {
+        // This DebugPrint is to help third-party driver writers
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: size returned was zero?! (status "
+                    "%x\n", status));
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    // This time we know how much data there is so we can
+    // allocate a buffer of the correct size
+    bufferLength = descriptor->Size;
+    NT_ASSERT(bufferLength >= sizeof(STORAGE_PROPERTY_QUERY));
+    bufferLength = max(bufferLength, sizeof(STORAGE_PROPERTY_QUERY));
+
+    descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_DESCRIPTOR);
+
+    if(descriptor == NULL)
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: unable to memory for descriptor "
+                    "(%d bytes)\n", bufferLength));
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // setup the query again, as it was overwritten above
+    RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
+    query.PropertyId = *PropertyId;
+    query.QueryType = PropertyStandardQuery;
+
+    // copy the input to the new outputbuffer
+    RtlCopyMemory(descriptor,
+                  &query,
+                  sizeof(STORAGE_PROPERTY_QUERY));
+
+    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
+                                      (PVOID)descriptor,
+                                      bufferLength);
+
+    status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
+                                               NULL,
+                                               IOCTL_STORAGE_QUERY_PROPERTY,
+                                               &memoryDescriptor,
+                                               &memoryDescriptor,
+                                               NULL,
+                                               NULL);
+
+    if(!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "DeviceRetrieveDescriptor: error %lx trying to "
+                       "query properties #1\n", status));
+        FREE_POOL(descriptor);
+
+        return status;
+    }
+
+    // return the memory we've allocated to the caller
+    *Descriptor = descriptor;
+
+    return status;
+} // end DeviceRetrieveDescriptor()
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceRetrieveHackFlagsFromRegistry(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    try to retrieve hack flages from registry and put the information in
+    device extension.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+
+    none
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    WDFKEY          hardwareKey = NULL;
+    WDFKEY          subKey = NULL;
+    ULONG           deviceHacks = 0;
+
+    DECLARE_CONST_UNICODE_STRING(subKeyName, CLASSP_REG_SUBKEY_NAME);
+    DECLARE_CONST_UNICODE_STRING(valueName, CLASSP_REG_HACK_VALUE_NAME);
+
+    PAGED_CODE();
+
+    status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
+                                      PLUGPLAY_REGKEY_DEVICE,
+                                      KEY_READ,
+                                      WDF_NO_OBJECT_ATTRIBUTES,
+                                      &hardwareKey);
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRegistryOpenKey(hardwareKey,
+                                    &subKeyName,
+                                    KEY_READ,
+                                    WDF_NO_OBJECT_ATTRIBUTES,
+                                    &subKey);
+
+        if (NT_SUCCESS(status))
+        {
+            status = WdfRegistryQueryULong(subKey,
+                                           &valueName,
+                                           &deviceHacks);
+            if (NT_SUCCESS(status))
+            {
+                // remove unknown values and save...
+                CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
+                SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, deviceHacks);
+            }
+
+            WdfRegistryClose(subKey);
+        }
+
+        WdfRegistryClose(hardwareKey);
+    }
+
+
+    //
+    // we should modify the system hive to include another key for us to grab
+    // settings from.  in this case:  Classpnp\HackFlags
+    //
+    // the use of a DWORD value for the HackFlags allows 32 hacks w/o
+    // significant use of the registry, and also reduces OEM exposure.
+    //
+    // definition of bit flags:
+    //   0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
+    //                 cannot actually prevent removal.
+    //   0x00000002 -- Device hard-hangs or times out for GESN requests.
+    //   0x00000008 -- Device does not support RESERVE(6) and RELEASE(6).
+    //   0x00000010 -- Device may incorrecly report operational changes in GESN.
+    //   0x00000020 -- Device does not support streaming READ(12) / WRITE(12).
+    //   0x00000040 -- Device does not support asynchronous notification.
+    //   0xffffff80 -- Currently reserved, may be used later.
+    //
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID DeviceScanForSpecial(
+    _In_ PCDROM_DEVICE_EXTENSION          DeviceExtension,
+    _In_ CDROM_SCAN_FOR_SPECIAL_INFO      DeviceList[],
+    _In_ PCDROM_SCAN_FOR_SPECIAL_HANDLER  Function)
+/*++
+
+Routine Description:
+
+    scan the list of devices that should be hacked or not supported.
+
+Arguments:
+
+    DeviceExtension - the device context
+    DeviceList - the device list
+    Function - function used to scan from the list.
+
+Return Value:
+
+    none
+
+--*/
+{
+    PSTORAGE_DEVICE_DESCRIPTOR  deviceDescriptor;
+    PUCHAR                      vendorId;
+    PUCHAR                      productId;
+    PUCHAR                      productRevision;
+    UCHAR                       nullString[] = "";
+
+    PAGED_CODE();
+    NT_ASSERT(DeviceList);
+    NT_ASSERT(Function);
+
+    if (DeviceList == NULL)
+    {
+        return;
+    }
+    if (Function == NULL)
+    {
+        return;
+    }
+
+    deviceDescriptor = DeviceExtension->DeviceDescriptor;
+
+    // SCSI sets offsets to -1, ATAPI sets to 0.  check for both.
+    if (deviceDescriptor->VendorIdOffset != 0 &&
+        deviceDescriptor->VendorIdOffset != -1)
+    {
+        vendorId = ((PUCHAR)deviceDescriptor);
+        vendorId += deviceDescriptor->VendorIdOffset;
+    }
+    else
+    {
+        vendorId = nullString;
+    }
+
+    if (deviceDescriptor->ProductIdOffset != 0 &&
+        deviceDescriptor->ProductIdOffset != -1)
+    {
+        productId = ((PUCHAR)deviceDescriptor);
+        productId += deviceDescriptor->ProductIdOffset;
+    }
+    else
+    {
+        productId = nullString;
+    }
+
+    if (deviceDescriptor->ProductRevisionOffset != 0 &&
+        deviceDescriptor->ProductRevisionOffset != -1)
+    {
+        productRevision = ((PUCHAR)deviceDescriptor);
+        productRevision += deviceDescriptor->ProductRevisionOffset;
+    }
+    else
+    {
+        productRevision = nullString;
+    }
+
+    // loop while the device list is valid (not null-filled)
+    for (;(DeviceList->VendorId        != NULL ||
+           DeviceList->ProductId       != NULL ||
+           DeviceList->ProductRevision != NULL); DeviceList++)
+    {
+        if (StringsAreMatched(DeviceList->VendorId,        (LPSTR)vendorId) &&
+            StringsAreMatched(DeviceList->ProductId,       (LPSTR)productId) &&
+            StringsAreMatched(DeviceList->ProductRevision, (LPSTR)productRevision)
+            )
+        {
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: Found matching "
+                        "controller Ven: %s Prod: %s Rev: %s\n",
+                        (LPCSTR)vendorId, (LPCSTR)productId, (LPCSTR)productRevision));
+
+            // pass the context to the call back routine and exit
+            (Function)(DeviceExtension, DeviceList->Data);
+
+            // for CHK builds, try to prevent wierd stacks by having a debug
+            // print here. it's a hack, but i know of no other way to prevent
+            // the stack from being wrong.
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: "
+                        "completed callback\n"));
+            return;
+
+        } // else the strings did not match
+
+    } // none of the devices matched.
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: no match found for %p\n",
+                DeviceExtension->DeviceObject));
+    return;
+
+} // end DeviceScanForSpecial()
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceHackFlagsScan(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ ULONG_PTR                Data
+    )
+{
+    PAGED_CODE();
+
+    // remove invalid flags and save
+    CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
+    SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, Data);
+
+    return;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeHotplugInfo(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Retrieve information into struc STORAGE_HOTPLUG_INFO in DeviceExtension
+    initialize the hotplug information only after the ScanForSpecial routines,
+    as it relies upon the hack flags - DeviceExtension->PrivateFdoData->HackFlags.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
+    DEVICE_REMOVAL_POLICY   deviceRemovalPolicy = 0;
+    ULONG                   resultLength = 0;
+    ULONG                   writeCacheOverride;
+
+    PAGED_CODE();
+
+    // start with some default settings
+    RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
+
+    // set the size (aka version)
+    fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
+
+    // set if the device has removable media
+    if (DeviceExtension->DeviceDescriptor->RemovableMedia)
+    {
+        fdoData->HotplugInfo.MediaRemovable = TRUE;
+    }
+    else
+    {
+        fdoData->HotplugInfo.MediaRemovable = FALSE;
+    }
+
+    //
+    // this refers to devices which, for reasons not yet understood,
+    // do not fail PREVENT_MEDIA_REMOVAL requests even though they
+    // have no way to lock the media into the drive.  this allows
+    // the filesystems to turn off delayed-write caching for these
+    // devices as well.
+    //
+
+    if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_CANNOT_LOCK_MEDIA))
+    {
+        fdoData->HotplugInfo.MediaHotplug = TRUE;
+    }
+    else
+    {
+        fdoData->HotplugInfo.MediaHotplug = FALSE;
+    }
+
+    // Query the default removal policy from the kernel
+    status = WdfDeviceQueryProperty(DeviceExtension->Device,
+                                    DevicePropertyRemovalPolicy,
+                                    sizeof(DEVICE_REMOVAL_POLICY),
+                                    (PVOID)&deviceRemovalPolicy,
+                                    &resultLength);
+    if (NT_SUCCESS(status))
+    {
+        if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
+        {
+            status = STATUS_UNSUCCESSFUL;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Look into the registry to see if the user has chosen
+        // to override the default setting for the removal policy.
+        // User can override only if the default removal policy is
+        // orderly or suprise removal.
+
+        if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
+            (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
+        {
+            DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
+
+            DeviceGetParameter(DeviceExtension,
+                                CLASSP_REG_SUBKEY_NAME,
+                                CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+                                (PULONG)&userRemovalPolicy);
+
+            // Validate the override value and use it only if it is an
+            // allowed value.
+            if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
+                (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
+            {
+                deviceRemovalPolicy = userRemovalPolicy;
+            }
+        }
+
+        // use this info to set the DeviceHotplug setting
+        // don't rely on DeviceCapabilities, since it can't properly
+        // determine device relations, etc.  let the kernel figure this
+        // stuff out instead.
+        if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)
+        {
+            fdoData->HotplugInfo.DeviceHotplug = TRUE;
+        }
+        else
+        {
+            fdoData->HotplugInfo.DeviceHotplug = FALSE;
+        }
+
+        // this refers to the *filesystem* caching, but has to be included
+        // here since it's a per-device setting.  this may change to be
+        // stored by the system in the future.
+        writeCacheOverride = FALSE;
+        DeviceGetParameter(DeviceExtension,
+                            CLASSP_REG_SUBKEY_NAME,
+                            CLASSP_REG_WRITE_CACHE_VALUE_NAME,
+                            &writeCacheOverride);
+
+        if (writeCacheOverride)
+        {
+            fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
+        }
+        else
+        {
+            fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
+        }
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "Could not initialize hotplug information %lx\n", status));
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitMmcContext(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine initializes and populates the internal data structures that are
+    used to discover various MMC-defined capabilities of the device.
+
+    This routine will not clean up allocate resources if it fails - that
+    is left for device stop/removal routines
+
+Arguments:
+
+    DeviceExtension - device extension
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+
+    PAGED_CODE();
+
+    DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
+    DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = FALSE;
+    DeviceExtension->DeviceAdditionalData.Mmc.IsWriter = FALSE;
+    DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+    DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = FALSE;
+    DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_CD_ROM;
+
+    // Determine if the drive is MMC-Capable
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceGetMmcSupportInfo(DeviceExtension,
+                                         &DeviceExtension->DeviceAdditionalData.Mmc.IsMmc);
+
+        if (!NT_SUCCESS(status))
+        {
+            //Currently, only low resource error comes here.
+            //That is a success case for unsupporting this command.
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "DeviceInitMmcContext: Failed to get the support info for GET CONFIGURATION "
+                        "command, failng %!STATUS!\n", status
+                        ));
+
+            DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
+            status = STATUS_SUCCESS;
+        }
+    }
+
+    if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
+    {
+        // the drive supports at least a subset of MMC commands
+        // (and therefore supports READ_CD, etc...)
+
+        // allocate a buffer for all the capabilities and such
+        status = DeviceAllocateMmcResources(DeviceExtension->Device);
+    }
+
+    if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
+    {
+        PFEATURE_HEADER header = NULL;
+        FEATURE_NUMBER  validationSchema;
+        ULONG           blockingFactor;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                  "DeviceInitMmcContext: FDO %p GET CONFIGURATION buffer %p\n",
+                  DeviceExtension->Device,
+                  DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer
+                  ));
+
+        // Update several properties using the retrieved Configuration Data.
+
+        // print all the feature pages (DBG only)
+        DevicePrintAllFeaturePages(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                   DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize);
+
+        // if AACS feature exists, enable AACS flag in the driver
+        header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                       DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                       FeatureAACS);
+        if (header)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceInitMmcContext: Reporting AACS support for device due to "
+                        "GET CONFIGURATION showing support\n"
+                        ));
+            DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE;
+        }
+
+#ifdef ENABLE_AACS_TESTING
+        DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE; // just force it true for testing
+#endif // ENABLE_AACS_TESTING
+
+        // Check if it's a DVD device
+        header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                       DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                       FeatureDvdRead);
+        if (header != NULL)
+        {
+            DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_DVD;
+        }
+
+        // check if drive is writer
+        DeviceUpdateMmcWriteCapability(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                       DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                       FALSE,                //Check if the drive has the ability to write.
+                                       (PBOOLEAN)&(DeviceExtension->DeviceAdditionalData.Mmc.IsWriter),
+                                       &validationSchema,
+                                       &blockingFactor);
+
+        // check if there is a CSS protected DVD or CPPM-protected DVDAudio media in drive.
+        header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                       DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                       FeatureDvdCSS);
+
+        DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = (header != NULL) && (header->Current);
+
+        // Flag the StartIo routine to update its state and hook in the error handler
+        DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
+        DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForMmc;
+
+        SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+
+        // Read the CDROM mode sense page to get additional info for raw read requests.
+        // only valid for MMC devices
+        DeviceSetRawReadInfo(DeviceExtension);
+    }
+
+    // Set Read-Only device flag for non-MMC device.
+    if (!(DeviceExtension->DeviceAdditionalData.Mmc.IsMmc))
+    {
+        ULONG  deviceCharacteristics = WdfDeviceGetCharacteristics(DeviceExtension->Device);
+
+        deviceCharacteristics |= FILE_READ_ONLY_DEVICE;
+
+        WdfDeviceSetCharacteristics(DeviceExtension->Device, deviceCharacteristics);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                   "DeviceInitMmcContext: FDO %p Device is not an MMC compliant device, so setting "
+                   "to read-only (legacy) mode",
+                   DeviceExtension->Device
+                   ));
+    }
+
+    // Set DEV_SAFE_START_UNIT flag for newer devices.
+    if (DeviceExtension->DeviceAdditionalData.DriveDeviceType == FILE_DEVICE_DVD)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                    "DeviceInitMmcContext: DVD Devices require START UNIT\n"));
+        SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+
+    }
+    else if ((DeviceExtension->DeviceDescriptor->BusType != BusTypeScsi)  &&
+             (DeviceExtension->DeviceDescriptor->BusType != BusTypeAtapi) &&
+             (DeviceExtension->DeviceDescriptor->BusType != BusTypeUnknown)
+             )
+    {
+        // devices on the newer busses require START_UNIT
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                  "DeviceInitMmcContext: Devices for newer buses require START UNIT\n"));
+        SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetTimeOutValueFromRegistry()
+/*++
+
+Routine Description:
+
+    get the device time out value from registry
+
+Arguments:
+
+    None
+
+Return Value:
+
+    ULONG - value of timeout
+
+--*/
+{
+    NTSTATUS    status;
+    WDFKEY      registryKey = NULL;
+    ULONG        timeOutValue = 0;
+
+    DECLARE_CONST_UNICODE_STRING(registryValueName, L"TimeOutValue");
+
+    PAGED_CODE();
+
+    // open the service key.
+    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+                                                KEY_READ,
+                                                WDF_NO_OBJECT_ATTRIBUTES,
+                                                &registryKey);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRegistryQueryULong(registryKey,
+                                       &registryValueName,
+                                       &timeOutValue);
+
+        WdfRegistryClose(registryKey);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        timeOutValue = 0;
+    }
+
+    return timeOutValue;
+
+} // end DeviceGetTimeOutValueFromRegistry()
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceScanSpecialDevices(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This function checks to see if an SCSI logical unit requires an special
+    initialization or error processing.
+
+Arguments:
+
+    Device - device object.
+
+Return Value:
+
+    None.
+
+--*/
+{
+
+    PAGED_CODE();
+
+    // set our hack flags
+    DeviceScanForSpecial(DeviceExtension, CdromHackItems, ScanForSpecialHandler);
+
+    //
+    // All CDRom's can ignore the queue lock failure for power operations
+    // and do not require handling the SpinUp case (unknown result of sending
+    // a cdrom a START_UNIT command -- may eject disks?)
+    //
+    // We send the stop command mostly to stop outstanding asynch operations
+    // (like audio playback) from running when the system is powered off.
+    // Because of this and the unlikely chance that a PLAY command will be
+    // sent in the window between the STOP and the time the machine powers down
+    // we don't require queue locks.  This is important because without them
+    // classpnp's power routines will send the START_STOP_UNIT command to the
+    // device whether or not it supports locking (atapi does not support locking
+    // and if we requested them we would end up not stopping audio on atapi
+    // devices).
+//    SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_DISABLE_SPIN_UP);
+//    SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_NO_QUEUE_LOCK);
+
+    if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_TOSHIBA_SD_W1101))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                    "DeviceScanSpecialDevices: Found Toshiba SD-W1101 DVD-RAM "
+                    "-- This drive will *NOT* support DVD-ROM playback.\n"));
+    }
+    else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_HITACHI_GD_2000))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                    "DeviceScanSpecialDevices: Found Hitachi GD-2000\n"));
+
+        // Setup an error handler to spin up the drive when it idles out
+        // since it seems to like to fail to spin itself back up on its
+        // own for a REPORT_KEY command.  It may also lose the AGIDs that
+        // it has given, which will result in DVD playback failures.
+        // This routine will just do what it can...
+        DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForHitachiGD2000;
+
+        // this drive may require START_UNIT commands to spin
+        // the drive up when it's spun itself down.
+        SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+    }
+    else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_FUJITSU_FMCD_10x))
+    {
+        // When Read command is issued to FMCD-101 or FMCD-102 and there is a music
+        // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning
+        // error status.
+        DeviceExtension->TimeOutValue = 20;
+    }
+    else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_DEC_RRD))
+    {
+        NTSTATUS                   status;
+        PMODE_PARM_READ_WRITE_DATA modeParameters;
+        SCSI_REQUEST_BLOCK         srb = {0};
+        PCDB                       cdb;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                    "DeviceScanSpecialDevices:  Found DEC RRD.\n"));
+
+        DeviceExtension->DeviceAdditionalData.IsDecRrd = TRUE;
+
+        // Setup an error handler to reinitialize the cd rom after it is reset?
+        //
+        //DeviceExtension->DevInfo->ClassError = DecRrdProcessError;
+
+        // Found a DEC RRD cd-rom.  These devices do not pass MS HCT
+        // multi-media tests because the DEC firmware modifieds the block
+        // from the PC-standard 2K to 512.  Change the block transfer size
+        // back to the PC-standard 2K by using a mode select command.
+
+        modeParameters = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                               sizeof(MODE_PARM_READ_WRITE_DATA),
+                                               CDROM_TAG_MODE_DATA);
+        if (modeParameters == NULL)
+        {
+            return;
+        }
+
+        RtlZeroMemory(modeParameters, sizeof(MODE_PARM_READ_WRITE_DATA));
+        RtlZeroMemory(&srb,           sizeof(SCSI_REQUEST_BLOCK));
+
+        // Set the block length to 2K.
+        modeParameters->ParameterListHeader.BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
+
+        // Set block length to 2K (0x0800) in Parameter Block.
+        modeParameters->ParameterListBlock.BlockLength[0] = 0x00; //MSB
+        modeParameters->ParameterListBlock.BlockLength[1] = 0x08;
+        modeParameters->ParameterListBlock.BlockLength[2] = 0x00; //LSB
+
+        // Build the mode select CDB.
+        srb.CdbLength = 6;
+        srb.TimeOutValue = DeviceExtension->TimeOutValue;
+
+        cdb = (PCDB)srb.Cdb;
+        cdb->MODE_SELECT.PFBit               = 1;
+        cdb->MODE_SELECT.OperationCode       = SCSIOP_MODE_SELECT;
+        cdb->MODE_SELECT.ParameterListLength = HITACHI_MODE_DATA_SIZE;
+
+        // Send the request to the device.
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            &srb,
+                                            modeParameters,
+                                            sizeof(MODE_PARM_READ_WRITE_DATA),
+                                            TRUE,
+                                            NULL);
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "DeviceScanSpecialDevices: Setting DEC RRD to 2K block"
+                        "size failed [%x]\n", status));
+        }
+
+        ExFreePool(modeParameters);
+    }
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScanForSpecialHandler(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ ULONG_PTR               HackFlags
+    )
+{
+    PAGED_CODE();
+
+    CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS);
+
+    DeviceExtension->DeviceAdditionalData.HackFlags = HackFlags;
+
+    return;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceCacheDeviceInquiryData(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    get inquiry data from device and cache it into device extension
+    The first INQUIRY command sent is with 0x24 bytes required data,
+    as ATAport driver always sends this to enumerate devices and 0x24
+    bytes is the minimum data device should return by spec.
+
+Arguments:
+
+    DeviceExtension - device extension.
+
+Return Value:
+
+    NTSTATUS.
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    SCSI_REQUEST_BLOCK  srb = {0};
+    PCDB                cdb = (PCDB)(&srb.Cdb);
+    PINQUIRYDATA        tmpInquiry = NULL;
+
+    // by spec, device should return at least 36 bytes.
+    ULONG               requestedInquiryTransferBytes = MINIMUM_CDROM_INQUIRY_SIZE;
+    BOOLEAN             needResendCommand = TRUE;
+    BOOLEAN             portDriverHack = FALSE;
+
+    // this ensures that the strings vendorID, productID, and firmwareRevision
+    // are all available in the inquiry data.  In addition, MMC spec requires
+    // all type 5 devices to have minimum 36 bytes of inquiry.
+    static const UCHAR minInquiryAdditionalLength =
+                                    MINIMUM_CDROM_INQUIRY_SIZE -
+                                    RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
+
+    C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength) <= 8 );
+    C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) == MINIMUM_CDROM_INQUIRY_SIZE );
+
+    PAGED_CODE();
+
+    // short-circuit here for if already cached for this device
+    // required to avoid use of scratch buffer after initialization
+    // of MCN code.
+    if (DeviceExtension->DeviceAdditionalData.CachedInquiryData != NULL)
+    {
+        NT_ASSERT(DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount != 0);
+        return STATUS_SUCCESS;
+    }
+
+    // 1. retrieve the inquiry data length
+
+    // 1.1 allocate inquiry data buffer
+    tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                       requestedInquiryTransferBytes,
+                                       CDROM_TAG_INQUIRY);
+    if (tmpInquiry == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // 1.2 send INQUIRY command
+    if (NT_SUCCESS(status))
+    {
+        srb.CdbLength = 6;
+        cdb->AsByte[0] = SCSIOP_INQUIRY;
+        cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
+        cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            &srb,
+                                            tmpInquiry,
+                                            requestedInquiryTransferBytes,
+                                            FALSE,
+                                            NULL);
+    }
+
+    // 1.3 get required data length
+    if (NT_SUCCESS(status))
+    {
+        if ((requestedInquiryTransferBytes == srb.DataTransferLength) &&
+            (requestedInquiryTransferBytes == (tmpInquiry->AdditionalLength +
+                                               RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength))) )
+        {
+            // device has only 36 bytes of INQUIRY data. do not need to resend the command.
+            needResendCommand = FALSE;
+        }
+        else
+        {
+            // workaround an ATAPI.SYS bug where additional length field is set to zero
+            if (tmpInquiry->AdditionalLength == 0)
+            {
+                tmpInquiry->AdditionalLength = minInquiryAdditionalLength;
+                portDriverHack = TRUE;
+            }
+
+            requestedInquiryTransferBytes =
+                                tmpInquiry->AdditionalLength +
+                                RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
+
+            if (requestedInquiryTransferBytes >= MINIMUM_CDROM_INQUIRY_SIZE)
+            {
+                needResendCommand = TRUE;
+            }
+            else
+            {
+                needResendCommand = FALSE;
+                //Length is small than minimum length, error out.
+                status = STATUS_DEVICE_PROTOCOL_ERROR;
+            }
+        }
+    }
+
+    // 2. retrieve the inquiry data if still needed.
+
+    // 2.1 Clean up.
+    if (NT_SUCCESS(status) && needResendCommand)
+    {
+        FREE_POOL(tmpInquiry);
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                           requestedInquiryTransferBytes,
+                                           CDROM_TAG_INQUIRY);
+        if (tmpInquiry == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    // 2.2 resend INQUIRY command
+    if (NT_SUCCESS(status) && needResendCommand)
+    {
+        srb.CdbLength = 6;
+        cdb->AsByte[0] = SCSIOP_INQUIRY;
+        cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
+        cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
+
+        status = DeviceSendSrbSynchronously( DeviceExtension->Device,
+                                             &srb,
+                                             tmpInquiry,
+                                             requestedInquiryTransferBytes,
+                                             FALSE,
+                                             NULL);
+
+        if (!NT_SUCCESS(status))
+        {
+            // Workaround for drive reports that it has more INQUIRY data than reality.
+            if ((srb.SrbStatus == SRB_STATUS_DATA_OVERRUN) &&
+                (srb.DataTransferLength < requestedInquiryTransferBytes) &&
+                (srb.DataTransferLength >= MINIMUM_CDROM_INQUIRY_SIZE))
+            {
+                //Port driver says buffer size mismatch (buffer underrun),
+                //retry with the real buffer size it could return.
+                requestedInquiryTransferBytes = srb.DataTransferLength;
+
+                FREE_POOL(tmpInquiry);
+                RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+                tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                   requestedInquiryTransferBytes,
+                                                   CDROM_TAG_INQUIRY);
+                if (tmpInquiry == NULL)
+                {
+                    status = STATUS_INSUFFICIENT_RESOURCES;
+                }
+                else
+                {
+                    srb.CdbLength = 6;
+                    cdb->AsByte[0] = SCSIOP_INQUIRY;
+                    cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
+                    cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
+
+                    status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                                        &srb,
+                                                        tmpInquiry,
+                                                        requestedInquiryTransferBytes,
+                                                        FALSE,
+                                                        NULL);
+                }
+            }
+        }
+
+        //Check the transferred data length for safe.
+        if (NT_SUCCESS(status))
+        {
+            requestedInquiryTransferBytes = srb.DataTransferLength;
+
+            if (requestedInquiryTransferBytes < MINIMUM_CDROM_INQUIRY_SIZE)
+            {
+                // should never occur
+                status = STATUS_DEVICE_PROTOCOL_ERROR;
+            }
+        }
+
+        // ensure we got some non-zero data....
+        // This is done so we don't accidentally work around the
+        // ATAPI.SYS bug when no data was transferred.
+        if (NT_SUCCESS(status) && portDriverHack)
+        {
+            PULONG  tmp = (PULONG)tmpInquiry;
+            ULONG   i = MINIMUM_CDROM_INQUIRY_SIZE / sizeof(ULONG);
+            C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) % sizeof(ULONG) == 0 );
+
+            // wouldn't you know it -- there is no RtlIsMemoryZero() function; Make one up.
+            for ( ; i != 0; i--)
+            {
+                if (*tmp != 0)
+                {
+                    break; // out of this inner FOR loop -- guarantees 'i != 0'
+                }
+                tmp++;
+            }
+
+            if (i == 0) // all loop'd successfully
+            {
+                // should never occur to successfully get all zero'd data
+                status = STATUS_DEVICE_PROTOCOL_ERROR;
+            }
+        }
+    }
+
+    // if everything succeeded, then (and only then) modify the device extension
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->DeviceAdditionalData.CachedInquiryData = tmpInquiry;
+        DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount = requestedInquiryTransferBytes;
+    }
+    else
+    {
+        FREE_POOL(tmpInquiry);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetMmcSupportInfo(
+    _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _Out_ PBOOLEAN                  IsMmcDevice
+    )
+/*++
+
+Routine Description:
+
+    check if the device is MMC capable.
+
+Arguments:
+
+    DeviceExtension - device extension.
+
+Return Value:
+
+    NTSTATUS.
+    IsMmcDevice - TRUE (MMC capable); FALSE (not MMC device)
+
+--*/
+{
+    NTSTATUS    status;
+    ULONG       size;
+    ULONG       previouslyFailed;
+
+    PAGED_CODE();
+
+    *IsMmcDevice  = FALSE;
+
+    // read the registry in case the drive failed previously,
+    // and a timeout is occurring.
+    previouslyFailed = FALSE;
+    DeviceGetParameter(DeviceExtension,
+                       CDROM_SUBKEY_NAME,
+                       CDROM_NON_MMC_DRIVE_NAME,
+                       &previouslyFailed);
+
+    if (previouslyFailed)
+    {
+        SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT);
+    }
+
+    // read from the registry in case the drive reports bad profile lengths
+    previouslyFailed = FALSE;
+    DeviceGetParameter(DeviceExtension,
+                       CDROM_SUBKEY_NAME,
+                       CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
+                       &previouslyFailed);
+
+    if (previouslyFailed)
+    {
+        SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES);
+    }
+
+    // check for the ProfileList feature to determine if the drive is MMC compliant
+    // and set the "size" local variable to total GetConfig data size available.
+    // NOTE: This will exit this function in some error paths.
+    {
+        GET_CONFIGURATION_HEADER    localHeader = {0};
+        ULONG                       usable = 0;
+
+        status = DeviceGetConfiguration(DeviceExtension->Device,
+                                        &localHeader,
+                                        sizeof(GET_CONFIGURATION_HEADER),
+                                        &usable,
+                                        FeatureProfileList,
+                                        SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+
+        if (status == STATUS_INVALID_DEVICE_REQUEST ||
+            status == STATUS_NO_MEDIA_IN_DEVICE     ||
+            status == STATUS_IO_DEVICE_ERROR        ||
+            status == STATUS_IO_TIMEOUT)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                       "GetConfiguration Failed (%x), device %p not mmc-compliant\n",
+                       status, DeviceExtension->DeviceObject
+                       ));
+
+            previouslyFailed = TRUE;
+            DeviceSetParameter( DeviceExtension,
+                                CDROM_SUBKEY_NAME,
+                                CDROM_NON_MMC_DRIVE_NAME,
+                                previouslyFailed);
+
+            return STATUS_SUCCESS;
+        }
+        else if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                       "GetConfiguration Failed, status %x -- defaulting to -ROM\n",
+                       status));
+
+            return STATUS_SUCCESS;
+        }
+        else if (usable < sizeof(GET_CONFIGURATION_HEADER))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                       "GetConfiguration Failed, returned only %x bytes!\n", usable));
+            previouslyFailed = TRUE;
+            DeviceSetParameter( DeviceExtension,
+                                CDROM_SUBKEY_NAME,
+                                CDROM_NON_MMC_DRIVE_NAME,
+                                previouslyFailed);
+
+            return STATUS_SUCCESS;
+        }
+
+        size = (localHeader.DataLength[0] << 24) |
+               (localHeader.DataLength[1] << 16) |
+               (localHeader.DataLength[2] <<  8) |
+               (localHeader.DataLength[3] <<  0) ;
+
+
+        if ((size <= 4) || (size + 4 < size))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                       "GetConfiguration Failed, claims MMC support but doesn't "
+                       "correctly return config length! (%x)\n",
+                       size
+                       ));
+            previouslyFailed = TRUE;
+            DeviceSetParameter( DeviceExtension,
+                                CDROM_SUBKEY_NAME,
+                                CDROM_NON_MMC_DRIVE_NAME,
+                                previouslyFailed);
+
+            return STATUS_SUCCESS;
+        }
+        else if ((size % 4) != 0)
+        {
+            if ((size % 2) != 0)
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                           "GetConfiguration Failed, returned odd number of bytes %x!\n",
+                           size
+                           ));
+                previouslyFailed = TRUE;
+                DeviceSetParameter( DeviceExtension,
+                                    CDROM_SUBKEY_NAME,
+                                    CDROM_NON_MMC_DRIVE_NAME,
+                                    previouslyFailed);
+
+                return STATUS_SUCCESS;
+            }
+            else
+            {
+                if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES))
+                {
+                    // we've already caught this and ASSERT'd once, so don't do it again
+                }
+                else
+                {
+                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                               "GetConfiguration returned a size that is not per spec (%x bytes), this is probably because of a vendor specific data header with a size not divisible by 4.\n",
+                               size
+                               ));
+                    previouslyFailed = TRUE;
+                    DeviceSetParameter(DeviceExtension,
+                                       CDROM_SUBKEY_NAME,
+                                       CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
+                                       previouslyFailed);
+                }
+            }
+        }
+
+        size += 4; // sizeof the datalength fields
+    }
+
+    *IsMmcDevice = TRUE;
+
+    // This code doesn't handle total get config size over 64k
+    NT_ASSERT( size <= MAXUSHORT );
+
+    // Check for SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE support in the device.
+    // NOTE: This will exit this function in some error paths.
+    {
+        ULONG   featureSize = sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_HEADER);
+        ULONG   usable = 0;
+
+        PGET_CONFIGURATION_HEADER configBuffer = ExAllocatePoolWithTag(
+                                                            NonPagedPoolNx,
+                                                            featureSize,
+                                                            CDROM_TAG_GET_CONFIG);
+
+        if (configBuffer == NULL)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        // read the registry in case the drive failed previously,
+        // and a timeout is occurring.
+        previouslyFailed = FALSE;
+        DeviceGetParameter( DeviceExtension,
+                            CDROM_SUBKEY_NAME,
+                            CDROM_TYPE_ONE_GET_CONFIG_NAME,
+                            &previouslyFailed);
+
+        if (previouslyFailed)
+        {
+            SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
+            FREE_POOL(configBuffer);
+            return STATUS_SUCCESS;
+        }
+
+        // Get only the config and feature header
+        status = DeviceGetConfiguration(DeviceExtension->Device,
+                                        configBuffer,
+                                        featureSize,
+                                        &usable,
+                                        FeatureProfileList,
+                                        SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE);
+
+        if (!NT_SUCCESS(status) || (usable < featureSize))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                       "Type One GetConfiguration Failed. Usable buffer size: %d\n", usable));
+            previouslyFailed = TRUE;
+        }
+        else
+        {
+            PFEATURE_HEADER featureHeader;
+            ULONG           totalAvailableBytes = 0;
+            ULONG           expectedAvailableBytes = 0;
+
+            REVERSE_BYTES(&totalAvailableBytes, configBuffer->DataLength);
+            totalAvailableBytes += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+            featureHeader = (PFEATURE_HEADER) ((PUCHAR)configBuffer + sizeof(GET_CONFIGURATION_HEADER));
+            expectedAvailableBytes = sizeof(GET_CONFIGURATION_HEADER) +
+                                     sizeof(FEATURE_HEADER) +
+                                     featureHeader->AdditionalLength;
+
+            if (totalAvailableBytes > expectedAvailableBytes)
+            {
+                // Device is returning more than required size. Most likely the device
+                // is returning TYPE ALL data. Set the flag to use TYPE ALL for TYPE ONE
+                // requets
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                           "Type One GetConfiguration Failed. "
+                           "Device returned %d bytes instead of %d bytes\n",
+                           size, featureSize));
+
+                previouslyFailed = TRUE;
+            }
+        }
+
+        FREE_POOL(configBuffer);
+
+        if (previouslyFailed == TRUE)
+        {
+            DeviceSetParameter( DeviceExtension,
+                                CDROM_SUBKEY_NAME,
+                                CDROM_TYPE_ONE_GET_CONFIG_NAME,
+                                previouslyFailed);
+
+            SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
+        }
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceSetRawReadInfo(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine reads the CDROM capabilities mode page and save information
+    in the device extension needed for raw reads.
+    NOTE: this function is only valid for MMC device
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    PUCHAR      buffer = NULL;
+    ULONG       count = 0;
+
+    PAGED_CODE();
+
+    // Check whether the device can return C2 error flag bits and the block
+    // error byte.  If so, save this info and fill in appropriate flag during
+    // raw read requests.
+
+    // Start by checking the GET_CONFIGURATION data
+    {
+        PFEATURE_DATA_CD_READ   cdReadHeader = NULL;
+        ULONG                   additionalLength = sizeof(FEATURE_DATA_CD_READ) - sizeof(FEATURE_HEADER);
+
+        cdReadHeader = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                            DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                            FeatureCdRead);
+
+        if ((cdReadHeader != NULL) &&
+            (cdReadHeader->Header.AdditionalLength >= additionalLength) &&
+            (cdReadHeader->C2ErrorData)
+            )
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                      "DeviceSetRawReadInfo: FDO %p GET_CONFIG shows ability to read C2 error bits\n",
+                      DeviceExtension->DeviceObject
+                      ));
+            DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE;    // Device returns C2 error info.
+        }
+    }
+
+    // Unfortunately, the only way to check for the ability to read R-W subcode
+    // information is via MODE_SENSE.  Do so here, and check the C2 bit as well
+    // in case the drive has a firmware bug where it fails to report this ability
+    // in GET_CONFIG (which some drives do).
+    for (count = 0; count < 6; count++)
+    {
+        SCSI_REQUEST_BLOCK  srb = {0};
+        PCDB                cdb = (PCDB)srb.Cdb;
+        ULONG               bufferLength = 0;
+
+        // Build the MODE SENSE CDB.  Try 10-byte CDB first.
+        if ((count/3) == 0)
+        {
+            bufferLength = sizeof(CDVD_CAPABILITIES_PAGE)  +
+                           sizeof(MODE_PARAMETER_HEADER10) +
+                           sizeof(MODE_PARAMETER_BLOCK);
+
+            cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+            cdb->MODE_SENSE10.Dbd = 1;
+            cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
+            cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferLength >> 8);
+            cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferLength >> 0);
+            srb.CdbLength = 10;
+        }
+        else
+        {
+            bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) +
+                           sizeof(MODE_PARAMETER_HEADER)  +
+                           sizeof(MODE_PARAMETER_BLOCK);
+
+            cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+            cdb->MODE_SENSE.Dbd = 1;
+            cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES;
+            cdb->MODE_SENSE.AllocationLength = (UCHAR)bufferLength;
+            srb.CdbLength = 6;
+        }
+
+        // Set timeout value from device extension.
+        srb.TimeOutValue = DeviceExtension->TimeOutValue;
+
+        buffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_MODE_DATA);
+
+        if (buffer == NULL)
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                      "DeviceSetRawReadInfo: cannot allocate "
+                      "buffer, so not setting raw read info for FDO %p\n",
+                      DeviceExtension->DeviceObject
+                      ));
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto FnExit;
+        }
+
+        RtlZeroMemory(buffer, bufferLength);
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            &srb,
+                                            buffer,
+                                            bufferLength,
+                                            FALSE,
+                                            NULL);
+
+        if (NT_SUCCESS(status) ||
+            (status == STATUS_DATA_OVERRUN) ||
+            (status == STATUS_BUFFER_OVERFLOW))
+        {
+            PCDVD_CAPABILITIES_PAGE capabilities = NULL;
+
+            // determine where the capabilities page really is
+            if ((count/3) == 0)
+            {
+                PMODE_PARAMETER_HEADER10 p = (PMODE_PARAMETER_HEADER10)buffer;
+                capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
+                                                         sizeof(MODE_PARAMETER_HEADER10) +
+                                                         (p->BlockDescriptorLength[0] * 256) +
+                                                         p->BlockDescriptorLength[1]);
+            }
+            else
+            {
+                PMODE_PARAMETER_HEADER p = (PMODE_PARAMETER_HEADER)buffer;
+                capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
+                                                         sizeof(MODE_PARAMETER_HEADER) +
+                                                         p->BlockDescriptorLength);
+            }
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                      "DeviceSetRawReadInfo: FDO %p CDVD Capabilities buffer %p\n",
+                      DeviceExtension->DeviceObject,
+                      buffer
+                      ));
+
+            if (capabilities->PageCode == MODE_PAGE_CAPABILITIES)
+            {
+                if (capabilities->C2Pointers)
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                              "DeviceSetRawReadInfo: FDO %p supports C2 error bits in READ_CD command\n",
+                              DeviceExtension->DeviceObject
+                              ));
+                    DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE;
+                }
+
+                if (capabilities->RWSupported)
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                              "DeviceSetRawReadInfo: FDO %p supports raw subcode in READ_CD command\n",
+                              DeviceExtension->DeviceObject
+                              ));
+                    DeviceExtension->DeviceAdditionalData.Mmc.ReadCdSubCode = TRUE;
+                }
+
+                break;
+            }
+        }
+
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                  "DeviceSetRawReadInfo: FDO %p failed %x byte mode sense, status %x\n",
+                  DeviceExtension->DeviceObject,
+                  (((count/3) == 0) ? 10 : 6),
+                  status
+                  ));
+
+        FREE_POOL(buffer);
+    }
+
+    if (count == 6)
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                  "DeviceSetRawReadInfo: FDO %p couldn't get mode sense data\n",
+                  DeviceExtension->DeviceObject
+                  ));
+    }
+
+FnExit:
+
+    if (buffer)
+    {
+        FREE_POOL(buffer);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceInitializeDvd(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+    This routine sets the region of DVD drive
+    NOTE: this routine uses ScratchBuffer, it must be called after ScratchBuffer allocated.
+
+Arguments:
+
+    Device - device object
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION     deviceExtension;
+    PDVD_COPY_PROTECT_KEY       copyProtectKey = NULL;
+    PDVD_RPC_KEY                rpcKey = NULL;
+    ULONG                       bufferLen = 0;
+    size_t                      bytesReturned;
+
+    PAGED_CODE();
+
+    deviceExtension = DeviceGetExtension(Device);
+
+    // check to see if we have a DVD device
+    if (deviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    // we got a DVD drive.
+    bufferLen = DVD_RPC_KEY_LENGTH;
+    copyProtectKey = (PDVD_COPY_PROTECT_KEY)ExAllocatePoolWithTag(PagedPool,
+                                                                  bufferLen,
+                                                                  DVD_TAG_RPC2_CHECK);
+
+    if (copyProtectKey == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // get the device region
+    RtlZeroMemory (copyProtectKey, bufferLen);
+    copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
+    copyProtectKey->KeyType = DvdGetRpcKey;
+
+    // perform IOCTL_DVD_READ_KEY
+    status = DvdStartSessionReadKey(deviceExtension,
+                                    IOCTL_DVD_READ_KEY,
+                                    NULL,
+                                    copyProtectKey,
+                                    DVD_RPC_KEY_LENGTH,
+                                    copyProtectKey,
+                                    DVD_RPC_KEY_LENGTH,
+                                    &bytesReturned);
+
+    if (NT_SUCCESS(status))
+    {
+        rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
+
+        // TypeCode of zero means that no region has been set.
+        if (rpcKey->TypeCode == 0)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,
+                        "DVD Initialize (%p): must choose DVD region\n",
+                        Device));
+            deviceExtension->DeviceAdditionalData.PickDvdRegion = 1;
+
+            // set the device region code to be the same as region code on media.
+            if (deviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
+            {
+                DevicePickDvdRegion(Device);
+            }
+        }
+    }
+
+    FREE_POOL(copyProtectKey);
+
+    // return status of IOCTL_DVD_READ_KEY will be ignored.
+    return STATUS_SUCCESS;
+}
+
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceIsPortable(
+    _In_  PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _Out_ PBOOLEAN                  IsPortable
+    )
+/*++
+
+Routine Description:
+
+    This routine checks if the volume is on a portable storage device.
+
+Arguments:
+
+    DeviceExtension - device context
+    IsPortable - device is portable
+
+Return Value:
+
+    NTSTATUS.
+
+--*/
+
+{
+    DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE;
+    BOOLEAN         isPortable = FALSE;
+    ULONG           size       = 0;
+    NTSTATUS        status     = STATUS_SUCCESS;
+    DEVPROPTYPE     type       = DEVPROP_TYPE_EMPTY;
+
+    PAGED_CODE();
+
+    *IsPortable = FALSE;
+
+    // Check to see if the underlying device object is in local machine container
+    status = IoGetDevicePropertyData(DeviceExtension->LowerPdo,
+                                     &DEVPKEY_Device_InLocalMachineContainer,
+                                     0,
+                                     0,
+                                     sizeof(isInternal),
+                                     &isInternal,
+                                     &size,
+                                     &type);
+
+    if (!NT_SUCCESS(status))
+    {
+        goto Cleanup;
+    }
+
+    NT_ASSERT(size == sizeof(isInternal));
+    NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
+
+    // Volume is hot-pluggable if the disk pdo container id differs from that of root device
+    if (isInternal == DEVPROP_TRUE)
+    {
+        goto Cleanup;
+    }
+
+    isPortable = TRUE;
+
+    // Examine the bus type to  ensure that this really is a fixed device
+    if (DeviceExtension->DeviceDescriptor->BusType == BusTypeFibre ||
+        DeviceExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
+        DeviceExtension->DeviceDescriptor->BusType == BusTypeRAID)
+    {
+        isPortable = FALSE;
+    }
+
+    *IsPortable = isPortable;
+
+Cleanup:
+
+    return status;
+}
+#endif
+
+
+#pragma warning(pop) // un-sets any local warning changes
+
diff --git a/drivers/storage/class/cdrom_new/ioctl.c b/drivers/storage/class/cdrom_new/ioctl.c
new file mode 100644 (file)
index 0000000..f1db772
--- /dev/null
@@ -0,0 +1,9134 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    ioctl.c
+
+Abstract:
+
+    Include all funtions for processing IOCTLs
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "ioctl.h"
+#include "scratch.h"
+#include "mmc.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "ioctl.tmh"
+#endif
+
+
+#define FirstDriveLetter 'C'
+#define LastDriveLetter  'Z'
+
+#if DBG
+    LPCSTR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = {
+        "Physical",
+        "Copyright",
+        "DiskKey",
+        "BCA",
+        "Manufacturer",
+        "Unknown"
+    };
+#endif // DBG
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+GetConfigurationDataConversionTypeAllToTypeOne(
+    _In_    FEATURE_NUMBER       RequestedFeature,
+    _In_    PSCSI_REQUEST_BLOCK  Srb,
+    _Out_   size_t *             DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+GetConfigurationDataSynthesize(
+    _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
+    _In_                            ULONG           InputBufferSize,
+    _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
+    _In_                            size_t          OutputBufferSize,
+    _In_                            FEATURE_NUMBER  StartingFeature,
+    _In_                            ULONG           RequestType,
+    _Out_                           size_t *        DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+PCDB
+RequestGetScsiPassThroughCdb(
+    _In_ PIRP Irp
+    );
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceIsPlayActive)
+#pragma alloc_text(PAGE, RequestHandleGetDvdRegion)
+#pragma alloc_text(PAGE, RequestHandleReadTOC)
+#pragma alloc_text(PAGE, RequestHandleReadTocEx)
+#pragma alloc_text(PAGE, RequestHandleGetConfiguration)
+#pragma alloc_text(PAGE, RequestHandleGetDriveGeometry)
+#pragma alloc_text(PAGE, RequestHandleDiskVerify)
+#pragma alloc_text(PAGE, RequestHandleCheckVerify)
+#pragma alloc_text(PAGE, RequestHandleFakePartitionInfo)
+#pragma alloc_text(PAGE, RequestHandleEjectionControl)
+#pragma alloc_text(PAGE, RequestHandleEnableStreaming)
+#pragma alloc_text(PAGE, RequestHandleSendOpcInformation)
+#pragma alloc_text(PAGE, RequestHandleGetPerformance)
+#pragma alloc_text(PAGE, RequestHandleMcnSyncFakeIoctl)
+#pragma alloc_text(PAGE, RequestHandleLoadEjectMedia)
+#pragma alloc_text(PAGE, RequestHandleReserveRelease)
+#pragma alloc_text(PAGE, RequestHandlePersistentReserve)
+#pragma alloc_text(PAGE, DeviceHandleRawRead)
+#pragma alloc_text(PAGE, DeviceHandlePlayAudioMsf)
+#pragma alloc_text(PAGE, DeviceHandleReadQChannel)
+#pragma alloc_text(PAGE, ReadQChannel)
+#pragma alloc_text(PAGE, DeviceHandlePauseAudio)
+#pragma alloc_text(PAGE, DeviceHandleResumeAudio)
+#pragma alloc_text(PAGE, DeviceHandleSeekAudioMsf)
+#pragma alloc_text(PAGE, DeviceHandleStopAudio)
+#pragma alloc_text(PAGE, DeviceHandleGetSetVolume)
+#pragma alloc_text(PAGE, DeviceHandleReadDvdStructure)
+#pragma alloc_text(PAGE, ReadDvdStructure)
+#pragma alloc_text(PAGE, DeviceHandleDvdEndSession)
+#pragma alloc_text(PAGE, DeviceHandleDvdStartSessionReadKey)
+#pragma alloc_text(PAGE, DvdStartSessionReadKey)
+#pragma alloc_text(PAGE, DeviceHandleDvdSendKey)
+#pragma alloc_text(PAGE, DvdSendKey)
+#pragma alloc_text(PAGE, DeviceHandleSetReadAhead)
+#pragma alloc_text(PAGE, DeviceHandleSetSpeed)
+#pragma alloc_text(PAGE, RequestHandleExclusiveAccessQueryLockState)
+#pragma alloc_text(PAGE, RequestHandleExclusiveAccessLockDevice)
+#pragma alloc_text(PAGE, RequestHandleExclusiveAccessUnlockDevice)
+#pragma alloc_text(PAGE, RequestHandleScsiPassThrough)
+#pragma alloc_text(PAGE, RequestGetScsiPassThroughCdb)
+#pragma alloc_text(PAGE, GetConfigurationDataConversionTypeAllToTypeOne)
+#pragma alloc_text(PAGE, GetConfigurationDataSynthesize)
+
+#endif
+
+NTSTATUS
+RequestHandleUnknownIoctl(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    )
+/*++
+
+Routine Description:
+
+    All unknown IOCTLs will be forward to lower level driver.
+
+Arguments:
+
+    Device - device object
+    Request - request to be handled
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_UNSUCCESSFUL;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
+    BOOLEAN                     syncRequired = requestContext->SyncRequired;
+
+    ULONG                       sendOptionsFlags = 0;
+    BOOLEAN                     requestSent = FALSE;
+
+    WdfRequestFormatRequestUsingCurrentType(Request);
+
+    if (syncRequired)
+    {
+        sendOptionsFlags = WDF_REQUEST_SEND_OPTION_SYNCHRONOUS;
+    }
+    else
+    {
+        WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL);
+    }
+
+    status = RequestSend(deviceExtension,
+                         Request,
+                         deviceExtension->IoTarget,
+                         sendOptionsFlags,
+                         &requestSent);
+
+    if (requestSent)
+    {
+        if (syncRequired)
+        {
+            // the request needs to be completed here.
+            RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+        }
+    }
+    else
+    {
+        // failed to send the request to IoTarget
+        RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+DeviceIsPlayActive(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+    This routine determines if the cd is currently playing music.
+
+Arguments:
+
+    Device - Device object.
+
+Return Value:
+
+    BOOLEAN - TRUE if the device is playing music.
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PSUB_Q_CURRENT_POSITION currentBuffer;
+    size_t                  bytesRead = 0;
+
+    PAGED_CODE ();
+
+    // if we don't think it is playing audio, don't bother checking.
+    if (!deviceExtension->DeviceAdditionalData.PlayActive)
+    {
+        return FALSE;
+    }
+
+    // Allocate the required memory
+    NT_ASSERT(sizeof(SUB_Q_CURRENT_POSITION) >= sizeof(CDROM_SUB_Q_DATA_FORMAT));
+    currentBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                          sizeof(SUB_Q_CURRENT_POSITION),
+                                          CDROM_TAG_PLAY_ACTIVE);
+    if (currentBuffer == NULL)
+    {
+        return FALSE;
+    }
+
+    // set the options in the output buffer format
+    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
+    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
+
+    // Send SCSI command to read Q Channel information.
+    status = ReadQChannel(deviceExtension,
+                          NULL,
+                          currentBuffer,
+                          sizeof(CDROM_SUB_Q_DATA_FORMAT),
+                          currentBuffer,
+                          sizeof(SUB_Q_CURRENT_POSITION),
+                          &bytesRead);
+
+    if (!NT_SUCCESS(status))
+    {
+        ExFreePool(currentBuffer);
+        return FALSE;
+    }
+
+    // update the playactive flag appropriately
+    if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
+    {
+        deviceExtension->DeviceAdditionalData.PlayActive = TRUE;
+    }
+    else
+    {
+        deviceExtension->DeviceAdditionalData.PlayActive = FALSE;
+    }
+
+    ExFreePool(currentBuffer);
+
+    return deviceExtension->DeviceAdditionalData.PlayActive;
+}
+
+NTSTATUS
+RequestHandleGetInquiryData(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength)
+/*++
+
+Routine Description:
+
+   Handler for IOCTL_CDROM_GET_INQUIRY_DATA
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else
+    {
+        PVOID outputBuffer = NULL;
+
+        *DataLength = min(cdData->CachedInquiryDataByteCount,
+                          RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
+
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+
+        if (NT_SUCCESS(status) &&
+            (outputBuffer != NULL))
+        {
+            // Always copy as much data as possible
+            RtlCopyMemory(outputBuffer,
+                          cdData->CachedInquiryData,
+                          *DataLength);
+        }
+
+        // and finally decide between two possible status values
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < cdData->CachedInquiryDataByteCount)
+        {
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestHandleGetMediaTypeEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handler for IOCTL_STORAGE_GET_MEDIA_TYPES_EX
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    PGET_MEDIA_TYPES        mediaTypes = NULL;
+    PDEVICE_MEDIA_INFO      mediaInfo = NULL; //&mediaTypes->MediaInfo[0];
+    ULONG                   sizeNeeded = 0;
+    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    *DataLength = 0;
+
+    // Must run below dispatch level.
+    if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
+    {
+        NT_ASSERT(FALSE);
+        return STATUS_INVALID_LEVEL;
+    }
+
+    sizeNeeded = sizeof(GET_MEDIA_TYPES);
+
+    // IsMmc is static...
+    if (cdData->Mmc.IsMmc)
+    {
+        sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types
+    }
+
+    status = WdfRequestRetrieveOutputBuffer(Request,
+                                            sizeNeeded,
+                                            (PVOID*)&mediaTypes,
+                                            NULL);
+
+    if (NT_SUCCESS(status) &&
+        (mediaTypes != NULL))
+    {
+        mediaInfo = &mediaTypes->MediaInfo[0];
+
+        RtlZeroMemory(mediaTypes, sizeNeeded);
+
+        // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX
+
+        mediaTypes->DeviceType = cdData->DriveDeviceType;
+
+        mediaTypes->MediaInfoCount = 1;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
+        mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;
+
+        if (cdData->Mmc.IsMmc)
+        {
+            // also report a removable disk
+            mediaTypes->MediaInfoCount += 1;
+
+            mediaInfo++;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
+            mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;
+            mediaInfo--;
+
+        }
+
+        // Status will either be success, if media is present, or no media.
+        // It would be optimal to base from density code and medium type, but not all devices
+        // have values for these fields.
+
+        // Send a TUR to determine if media is present, only if the device is not in ZPODD mode.
+        if ((!EXCLUSIVE_MODE(cdData) ||
+             EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request))) &&
+            ((zpoddInfo == NULL) ||
+             (zpoddInfo->InZeroPowerState == FALSE)))
+        {
+            SCSI_REQUEST_BLOCK  srb;
+            PCDB                cdb = (PCDB)srb.Cdb;
+
+            RtlZeroMemory(&srb,sizeof(SCSI_REQUEST_BLOCK));
+
+            srb.CdbLength = 6;
+            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+            srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
+
+            status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                                &srb,
+                                                NULL,
+                                                0,
+                                                FALSE,
+                                                Request);
+
+            if (NT_SUCCESS(status))
+            {
+                // set the disk's media as current if we can write to it.
+                if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed)
+                {
+                    mediaInfo++;
+                    SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
+                             MEDIA_CURRENTLY_MOUNTED);
+                    mediaInfo--;
+                }
+                else
+                {
+                    SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
+                             MEDIA_CURRENTLY_MOUNTED);
+                }
+            }
+            else
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                           "RequestHandleGetMediaTypeEx: GET_MEDIA_TYPES status of TUR - %lx\n", status));
+            }
+        }
+
+        // per legacy cdrom behavior, always return success
+        status = STATUS_SUCCESS;
+    }
+
+    *DataLength = sizeNeeded;
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetDvdRegion(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handler for IOCTL_DVD_GET_REGION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    PVOID               outputBuffer = NULL;
+    size_t              bytesReturned = 0;
+
+    PDVD_COPY_PROTECT_KEY       copyProtectKey = NULL;
+    ULONG                       keyLength = 0;
+    PDVD_DESCRIPTOR_HEADER      dvdHeader;
+    PDVD_COPYRIGHT_DESCRIPTOR   copyRightDescriptor;
+    PDVD_REGION                 dvdRegion = NULL;
+    PDVD_READ_STRUCTURE         readStructure = NULL;
+    PDVD_RPC_KEY                rpcKey;
+
+    PAGED_CODE ();
+
+    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                "RequestHandleGetDvdRegion: [%p] IOCTL_DVD_GET_REGION\n", Request));
+
+    *DataLength = 0;
+
+    // reject the request if it's not a DVD device.
+    if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                sizeof(DVD_REGION),
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // figure out how much data buffer we need
+        keyLength = max((sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR)),
+                        sizeof(DVD_READ_STRUCTURE));
+        keyLength = max(keyLength,
+                        DVD_RPC_KEY_LENGTH);
+
+        // round the size to nearest ULONGLONG -- why?
+        // could this be to deal with device alignment issues?
+        keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1));
+
+        readStructure = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                              keyLength,
+                                              DVD_TAG_READ_KEY);
+        if (readStructure == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory (readStructure, keyLength);
+        readStructure->Format = DvdCopyrightDescriptor;
+
+        // use READ_STRUCTURE to read copyright descriptor
+        status = ReadDvdStructure(DeviceExtension,
+                                  Request,
+                                  readStructure,
+                                  keyLength,
+                                  readStructure,
+                                  sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+                                  &bytesReturned);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // we got the copyright descriptor, so now get the region if possible
+        dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure;
+        copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data;
+
+        // the original irp's systembuffer has a copy of the info that
+        // should be passed down in the request
+        dvdRegion = outputBuffer;
+
+        dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType;
+        dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation;
+
+        // now reuse the buffer to request the copy protection info
+        copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure;
+        RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH);
+        copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
+        copyProtectKey->KeyType = DvdGetRpcKey;
+
+        // send a request for READ_KEY
+        status = DvdStartSessionReadKey(DeviceExtension,
+                                        IOCTL_DVD_READ_KEY,
+                                        Request,
+                                        copyProtectKey,
+                                        DVD_RPC_KEY_LENGTH,
+                                        copyProtectKey,
+                                        DVD_RPC_KEY_LENGTH,
+                                        &bytesReturned);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // the request succeeded.  if a supported scheme is returned,
+        // then return the information to the caller
+        rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
+
+        if (rpcKey->RpcScheme == 1)
+        {
+            if (rpcKey->TypeCode)
+            {
+                dvdRegion->SystemRegion = ~rpcKey->RegionMask;
+                dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
+            }
+            else
+            {
+                // the drive has not been set for any region
+                dvdRegion->SystemRegion = 0;
+                dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
+            }
+
+            *DataLength = sizeof(DVD_REGION);
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestHandleGetDvdRegion => rpcKey->RpcScheme != 1\n"));
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    // Clean up
+    if (readStructure != NULL)
+    {
+        ExFreePool(readStructure);
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateRawRead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_RAW_READ
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    PVOID               inputBuffer = NULL;
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  currentStack = NULL;
+
+    LARGE_INTEGER       startingOffset = {0};
+    ULONGLONG           transferBytes = 0;
+    ULONGLONG           endOffset;
+    ULONGLONG           mdlBytes;
+    RAW_READ_INFO       rawReadInfo = {0};
+
+    *DataLength = 0;
+
+    irp = WdfRequestWdmGetIrp(Request);
+    currentStack = IoGetCurrentIrpStackLocation(irp);
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    // Check that ending sector is on disc and buffers are there and of
+    // correct size.
+    if (NT_SUCCESS(status) &&
+        (RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer == NULL))
+    {
+        //  This is a call from user space.  This is the only time that we need to validate parameters.
+        //  Validate the input and get the input buffer into Type3InputBuffer
+        //  so the rest of the code will be uniform.
+        if (inputBuffer != NULL)
+        {
+            currentStack->Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;
+            RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;
+
+            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(RAW_READ_INFO))
+            {
+                *DataLength = sizeof(RAW_READ_INFO);
+                status = STATUS_BUFFER_TOO_SMALL;
+            }
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        //  Since this ioctl is METHOD_OUT_DIRECT, we need to copy away the input buffer before interpreting it.
+        //  This prevents a malicious app from messing with the input buffer while we are interpreting it.
+        rawReadInfo = *(PRAW_READ_INFO)RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer;
+
+        startingOffset.QuadPart = rawReadInfo.DiskOffset.QuadPart;
+
+        if ((rawReadInfo.TrackMode == CDDA)        ||
+            (rawReadInfo.TrackMode == YellowMode2) ||
+            (rawReadInfo.TrackMode == XAForm2)     )
+        {
+            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
+        }
+        else if (rawReadInfo.TrackMode == RawWithSubCode)
+        {
+            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
+        }
+        else if (rawReadInfo.TrackMode == RawWithC2)
+        {
+            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
+        }
+        else if (rawReadInfo.TrackMode == RawWithC2AndSubCode)
+        {
+            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                      "RequestValidateRawRead: Invalid TrackMode type %x for XA read\n",
+                      rawReadInfo.TrackMode
+                      ));
+        }
+
+        endOffset = (ULONGLONG)rawReadInfo.SectorCount * COOKED_SECTOR_SIZE;
+        endOffset += startingOffset.QuadPart;
+
+        // check for overflows....
+        if (rawReadInfo.SectorCount == 0)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid I/O parameters for XA "
+                        "Read (zero sectors requested)\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (transferBytes < (ULONGLONG)(rawReadInfo.SectorCount))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid I/O parameters for XA "
+                        "Read (TransferBytes Overflow)\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (endOffset < (ULONGLONG)startingOffset.QuadPart)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid I/O parameters for XA "
+                        "Read (EndingOffset Overflow)\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < transferBytes)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid I/O parameters for XA "
+                        "Read (Bad buffer size)\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (endOffset > (ULONGLONG)DeviceExtension->PartitionLength.QuadPart)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid I/O parameters for XA "
+                        "Read (Request Out of Bounds)\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // cannot validate the MdlAddress, since it is not included in any
+        // other location per the DDK and file system calls.
+
+        // validate the mdl describes at least the number of bytes
+        // requested from us.
+        mdlBytes = (ULONGLONG)MmGetMdlByteCount(irp->MdlAddress);
+        if (mdlBytes < transferBytes)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestValidateRawRead: Invalid MDL %s, Irp %p\n",
+                        "size (5)", irp));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // check the buffer for alignment
+        // This is important for x86 as some busses (ie ATAPI)
+        // require word-aligned buffers.
+        if ( ((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) &
+             DeviceExtension->AdapterDescriptor->AlignmentMask )
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                      "RequestValidateRawRead: Invalid I/O parameters for "
+                      "XA Read (Buffer %p not aligned with mask %x\n",
+                      RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer,
+                      DeviceExtension->AdapterDescriptor->AlignmentMask));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Validate the request is not too large for the adapter
+        BOOLEAN bufferIsPageAligned = FALSE;
+        ULONG   maxLength = 0;
+
+        // if buffer is not page-aligned, then subtract one as the
+        // transfer could cross a page boundary.
+        if ((((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) & (PAGE_SIZE-1)) == 0)
+        {
+            bufferIsPageAligned = TRUE;
+        }
+
+        if (bufferIsPageAligned)
+        {
+            maxLength = cdData->MaxPageAlignedTransferBytes;
+        }
+        else
+        {
+            maxLength = cdData->MaxUnalignedTransferBytes;
+        }
+
+        if (transferBytes > maxLength)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                      "RequestValidateRawRead: The XA Read (type %x) would require %I64x bytes, "
+                      "but the adapter can only handle %x bytes (for a%saligned buffer)\n",
+                      rawReadInfo.TrackMode,
+                      transferBytes,
+                      maxLength,
+                      (bufferIsPageAligned ? " " : "n un")
+                      ));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        //
+        // HACKHACK - REF #0001
+        // The retry count will be in this irp's IRP_MN function,
+        // as the new irp was freed, and we therefore cannot use
+        // this irp's next stack location for this function.
+        // This may be a good location to store this info for
+        // when we remove RAW_READ (mode switching), as we will
+        // no longer have the nextIrpStackLocation to play with
+        // when that occurs
+        //
+        currentStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateReadTocEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_READ_TOC_EX
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    PCDROM_READ_TOC_EX  inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+        sizeof(CDROM_READ_TOC_EX))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+             MINIMUM_CDROM_READ_TOC_EX_SIZE)
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = MINIMUM_CDROM_READ_TOC_EX_SIZE;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
+             ((USHORT)-1))
+    {
+        status = STATUS_INVALID_PARAMETER;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+             DeviceExtension->AdapterDescriptor->AlignmentMask)
+    {
+        status = STATUS_INVALID_PARAMETER;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if ((inputBuffer->Reserved1 != 0) ||
+            (inputBuffer->Reserved2 != 0) ||
+            (inputBuffer->Reserved3 != 0))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        // NOTE: when adding new formats, ensure that first two bytes
+        //       specify the amount of additional data available.
+        else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC     ) ||
+                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) ||
+                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT  ))
+        {
+            // SessionTrack field is used
+        }
+        else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) ||
+                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA)     ||
+                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP))
+        {
+            // SessionTrack field is reserved
+            if (inputBuffer->SessionTrack != 0)
+            {
+                status = STATUS_INVALID_PARAMETER;
+            }
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateReadToc(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_READ_TOC
+
+Arguments:
+
+    DeviceExtension - device context
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+        sizeof(CDROM_TOC))
+    {
+        // they didn't request the entire TOC -- use _EX version
+        // for partial transfers and such.
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(CDROM_TOC);
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+             DeviceExtension->AdapterDescriptor->AlignmentMask)
+    {
+        status = STATUS_INVALID_PARAMETER;
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateGetLastSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_GET_LAST_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+        sizeof(CDROM_TOC_SESSION_DATA))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(CDROM_TOC_SESSION_DATA);
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+             DeviceExtension->AdapterDescriptor->AlignmentMask)
+    {
+        status = STATUS_INVALID_PARAMETER;
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateReadQChannel(
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_READ_Q_CHANNEL
+
+Arguments:
+
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PCDROM_SUB_Q_DATA_FORMAT inputBuffer = NULL;
+    ULONG                    transferByteCount = 0;
+
+    *DataLength = 0;
+
+    if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+       sizeof(CDROM_SUB_Q_DATA_FORMAT))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // check for all valid types of request
+        if (inputBuffer->Format == IOCTL_CDROM_CURRENT_POSITION)
+        {
+            transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
+        }
+        else if (inputBuffer->Format == IOCTL_CDROM_MEDIA_CATALOG)
+        {
+            transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
+        }
+        else if (inputBuffer->Format == IOCTL_CDROM_TRACK_ISRC)
+        {
+            transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
+        }
+        else
+        {
+            // Format not valid
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            transferByteCount)
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            *DataLength = transferByteCount;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateDvdReadStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_DVD_READ_STRUCTURE
+
+Arguments:
+
+    DeviceExtension - device context
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+           sizeof(DVD_READ_STRUCTURE))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestValidateDvdReadStructure - input buffer "
+                        "length too small (was %d should be %d)\n",
+                        (int)RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                        sizeof(DVD_READ_STRUCTURE)));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+                sizeof(READ_DVD_STRUCTURES_HEADER))
+        {
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestValidateDvdReadStructure - output buffer "
+                        "cannot hold header information\n"));
+            status = STATUS_BUFFER_TOO_SMALL;
+            *DataLength = sizeof(READ_DVD_STRUCTURES_HEADER);
+        }
+        else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
+                MAXUSHORT)
+        {
+            // key length must fit in two bytes
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestValidateDvdReadStructure - output buffer "
+                        "too large\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+                 DeviceExtension->AdapterDescriptor->AlignmentMask)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+        {
+            // reject the request if it's not a DVD device.
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateDvdStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Validate request of IOCTL_DVD_START_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+           sizeof(DVD_SESSION_ID))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestValidateDvdStartSession: DVD_START_SESSION - output "
+                        "buffer too small\n"));
+            status = STATUS_BUFFER_TOO_SMALL;
+            *DataLength = sizeof(DVD_SESSION_ID);
+        }
+        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+        {
+            // reject the request if it's not a DVD device.
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateDvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_DVD_SEND_KEY, IOCTL_DVD_SEND_KEY2
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PDVD_COPY_PROTECT_KEY   key = NULL;
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &key,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        if((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
+           (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != key->KeyLength))
+        {
+
+            //
+            // Key is too small to have a header or the key length doesn't
+            // match the input buffer length.  Key must be invalid
+            //
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestValidateDvdSendKey: [%p] IOCTL_DVD_SEND_KEY - "
+                        "key is too small or does not match KeyLength\n",
+                        Request));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // allow only certain key type (non-destructive) to go through
+        // IOCTL_DVD_SEND_KEY (which only requires READ access to the device)
+        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_SEND_KEY)
+        {
+            if ((key->KeyType != DvdChallengeKey) &&
+                (key->KeyType != DvdBusKey2) &&
+                (key->KeyType != DvdInvalidateAGID))
+            {
+                status = STATUS_INVALID_PARAMETER;
+            }
+        }
+        else if ((key->KeyType != DvdChallengeKey) &&
+                (key->KeyType != DvdBusKey1) &&
+                (key->KeyType != DvdBusKey2) &&
+                (key->KeyType != DvdTitleKey) &&
+                (key->KeyType != DvdAsf) &&
+                (key->KeyType != DvdSetRpcKey) &&
+                (key->KeyType != DvdGetRpcKey) &&
+                (key->KeyType != DvdDiskKey) &&
+                (key->KeyType != DvdInvalidateAGID))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+        {
+            // reject the request if it's not a DVD device.
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateGetConfiguration(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_GET_CONFIGURATION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+        sizeof(GET_CONFIGURATION_HEADER))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(GET_CONFIGURATION_HEADER);
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0xffff)
+    {
+        // output buffer is too large
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+        DeviceExtension->AdapterDescriptor->AlignmentMask)
+    {
+        // buffer is not proper size multiple
+        status = STATUS_INVALID_PARAMETER;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+
+#if BUILD_WOW64_ENABLED && defined(_WIN64)
+
+        if (WdfRequestIsFrom32BitProcess(Request))
+        {
+            PGET_CONFIGURATION_IOCTL_INPUT32 inputBuffer = NULL;
+
+            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+                sizeof(GET_CONFIGURATION_IOCTL_INPUT32))
+            {
+                status = STATUS_INFO_LENGTH_MISMATCH;
+            }
+
+            //
+            // also verify the arguments are reasonable.
+            //
+            if (NT_SUCCESS(status))
+            {
+                status = WdfRequestRetrieveInputBuffer(Request,
+                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                                       &inputBuffer,
+                                                       NULL);
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                if (inputBuffer->Feature > 0xffff)
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+                else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
+                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+                else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+        }
+        else
+
+#endif
+
+        {
+            PGET_CONFIGURATION_IOCTL_INPUT inputBuffer = NULL;
+
+            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+                sizeof(GET_CONFIGURATION_IOCTL_INPUT))
+            {
+                status = STATUS_INFO_LENGTH_MISMATCH;
+            }
+
+            // also verify the arguments are reasonable.
+            if (NT_SUCCESS(status))
+            {
+                status = WdfRequestRetrieveInputBuffer(Request,
+                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                                       &inputBuffer,
+                                                       NULL);
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                if (inputBuffer->Feature > 0xffff)
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+                else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
+                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+                else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
+                {
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateSetSpeed(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_SET_SPEED
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PCDROM_SET_SPEED        inputBuffer = NULL;
+    ULONG                   requiredLength = 0;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SET_SPEED))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Get the request type using CDROM_SET_SPEED structure
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(CDROM_SET_SPEED),
+                                               &inputBuffer,
+                                               NULL);
+
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->RequestType > CdromSetStreaming)
+        {
+            // Unknown request type.
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (inputBuffer->RequestType == CdromSetSpeed)
+        {
+            requiredLength = sizeof(CDROM_SET_SPEED);
+        }
+        else
+        {
+            // Don't send SET STREAMING command if this is not a MMC compliant device
+            if (cdData->Mmc.IsMmc == FALSE)
+            {
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+
+            requiredLength = sizeof(CDROM_SET_STREAMING);
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < requiredLength)
+        {
+            // Input buffer too small
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsReadMediaKeyBlock(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_READ_MEDIA_KEY_BLOCK
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PAACS_LAYER_NUMBER      layerNumber = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_LAYER_NUMBER))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < 8)
+    {
+        // This is a variable-length structure, but we're pretty sure
+        // it can never be less than eight bytes...
+        *DataLength = 8;
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &layerNumber,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*layerNumber > 255)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_START_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID))
+    {
+        *DataLength = sizeof(DVD_SESSION_ID);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsSendCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_SEND_CERTIFICATE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PAACS_SEND_CERTIFICATE  inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CERTIFICATE))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsGetCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_GET_CERTIFICATE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PDVD_SESSION_ID         sessionId = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CERTIFICATE))
+    {
+        *DataLength = sizeof(AACS_CERTIFICATE);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CERTIFICATE))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &sessionId,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*sessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsGetChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_GET_CHALLENGE_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PDVD_SESSION_ID         sessionId = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CHALLENGE_KEY))
+    {
+        *DataLength = sizeof(AACS_CHALLENGE_KEY);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CHALLENGE_KEY))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &sessionId,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*sessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsSendChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_SEND_CHALLENGE_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
+    PAACS_SEND_CHALLENGE_KEY    inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CHALLENGE_KEY))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsReadVolumeId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_READ_VOLUME_ID
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PDVD_SESSION_ID         sessionId = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_VOLUME_ID))
+    {
+        *DataLength = sizeof(AACS_VOLUME_ID);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_VOLUME_ID))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &sessionId,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*sessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsReadSerialNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_READ_SERIAL_NUMBER
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PDVD_SESSION_ID         sessionId = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_SERIAL_NUMBER))
+    {
+        *DataLength = sizeof(AACS_SERIAL_NUMBER);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_SERIAL_NUMBER))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &sessionId,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*sessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsReadMediaId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_READ_MEDIA_ID
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
+    PDVD_SESSION_ID         sessionId = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_MEDIA_ID))
+    {
+        *DataLength = sizeof(AACS_MEDIA_ID);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_MEDIA_ID))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &sessionId,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (*sessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateAacsBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_READ_BINDING_NONCE
+                       IOCTL_AACS_GENERATE_BINDING_NONCE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PCDROM_DATA              cdData = &(DeviceExtension->DeviceAdditionalData);
+    PAACS_READ_BINDING_NONCE inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.IsAACS)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_READ_BINDING_NONCE))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_BINDING_NONCE))
+    {
+        *DataLength = sizeof(AACS_BINDING_NONCE);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_BINDING_NONCE))
+    {
+        status = STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (inputBuffer->NumberOfSectors > 255)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (inputBuffer->StartLba > MAXULONG)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateExclusiveAccess(
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_CDROM_EXCLUSIVE_ACCESS
+
+Arguments:
+
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
+
+    *DataLength = 0;
+
+    if (KeGetCurrentIrql() != PASSIVE_LEVEL)
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: IOCTL must be called at passive level.\n"));
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_EXCLUSIVE_ACCESS))
+    {
+
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &exclusiveAccess,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        switch (exclusiveAccess->RequestType)
+        {
+            case ExclusiveAccessQueryState:
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+                    sizeof(CDROM_EXCLUSIVE_LOCK_STATE))
+                {
+                    //
+                    // Output buffer too small.
+                    //
+                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Output buffer too small\n"));
+                    *DataLength = sizeof(CDROM_EXCLUSIVE_LOCK_STATE);
+                    status = STATUS_BUFFER_TOO_SMALL;
+                }
+                break;
+            }
+
+            case ExclusiveAccessLockDevice:
+            {
+                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+                    sizeof(CDROM_EXCLUSIVE_LOCK))
+                {
+                    //
+                    // Input buffer too small
+                    //
+                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
+                    status = STATUS_INFO_LENGTH_MISMATCH;
+                }
+                break;
+            }
+            case ExclusiveAccessUnlockDevice:
+            {
+                //
+                // Nothing to check
+                //
+                break;
+            }
+
+            default:
+            {
+                //
+                // Unknown request type.
+                //
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Invalid request type\n"));
+                status = STATUS_INVALID_PARAMETER;
+            }
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessQueryLockState(
+    _In_ WDFDEVICE Device,
+    _In_ WDFREQUEST Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessQueryState
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA                 cdData = &deviceExtension->DeviceAdditionalData;
+    PCDROM_EXCLUSIVE_LOCK_STATE exclusiveLockState = NULL;
+
+    PAGED_CODE();
+
+    status = WdfRequestRetrieveOutputBuffer(Request,
+                                            sizeof(CDROM_EXCLUSIVE_LOCK_STATE),
+                                            &exclusiveLockState,
+                                            NULL);
+    NT_ASSERT(NT_SUCCESS(status));
+
+    RtlZeroMemory(exclusiveLockState, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));
+
+    if (EXCLUSIVE_MODE(cdData))
+    {
+        // Device is locked for exclusive use
+        exclusiveLockState->LockState = TRUE;
+
+        RtlCopyMemory(&exclusiveLockState->CallerName,
+                      &cdData->CallerName,
+                      CDROM_EXCLUSIVE_CALLER_LENGTH);
+
+    }
+    else
+    {
+        // Device is not locked
+        exclusiveLockState->LockState = FALSE;
+    }
+
+    RequestCompletion(deviceExtension, Request, status, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessLockDevice(
+    _In_ WDFDEVICE Device,
+    _In_ WDFREQUEST Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessLockDevice
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA             cdData = &deviceExtension->DeviceAdditionalData;
+    PCDROM_EXCLUSIVE_LOCK   exclusiveLock = NULL;
+    PIO_ERROR_LOG_PACKET    logEntry;
+
+    WDFFILEOBJECT           fileObject = NULL;
+    ULONG                   idx = 0;
+    ULONG                   nameLength = 0;
+
+    PAGED_CODE();
+
+    fileObject = WdfRequestGetFileObject(Request);
+
+    if (fileObject == NULL)
+    {
+        status = STATUS_INVALID_HANDLE;
+
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                    "RequestHandleExclusiveAccessLockDevice: FileObject is NULL, cannot grant exclusive access\n"));
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(CDROM_EXCLUSIVE_LOCK),
+                                               &exclusiveLock,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Validate the caller name string
+        for (idx = 0; (idx < CDROM_EXCLUSIVE_CALLER_LENGTH) && (exclusiveLock->CallerName[idx] != '\0'); idx++)
+        {
+            if (!ValidChar(exclusiveLock->CallerName[idx]))
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                            "RequestHandleExclusiveAccessLockDevice: Invalid characters in caller name\n"));
+                // error out
+                status = STATUS_INVALID_PARAMETER;
+                break;
+            }
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if ((idx == 0) || (idx >= CDROM_EXCLUSIVE_CALLER_LENGTH))
+        {
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestHandleExclusiveAccessLockDevice: Not a valid null terminated string.\n"));
+            //error out
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            nameLength = idx+1; // Add 1 for the NULL character
+            NT_ASSERT(nameLength <= CDROM_EXCLUSIVE_CALLER_LENGTH);
+        }
+    }
+
+    // If the file system is still mounted on this device fail the request,
+    // unless the force lock flag is set.
+    if (NT_SUCCESS(status))
+    {
+        if ((TEST_FLAG(exclusiveLock->Access.Flags, CDROM_LOCK_IGNORE_VOLUME) == FALSE) &&
+            IsVolumeMounted(deviceExtension->DeviceObject))
+        {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                            "RequestHandleExclusiveAccessLockDevice: Unable to lock device, file system mounted\n"));
+                status = STATUS_INVALID_DEVICE_STATE;
+        }
+    }
+
+    // Lock the device for exclusive access if the device is not already locked
+    if (NT_SUCCESS(status))
+    {
+        if (InterlockedCompareExchangePointer((PVOID)&cdData->ExclusiveOwner, (PVOID)fileObject, NULL) == NULL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "RequestHandleExclusiveAccessLockDevice: Entering exclusive mode! Device locked by file object %p\n", fileObject));
+
+            // Zero out the CallerName before storing it in the extension
+            RtlZeroMemory(&cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH);
+            RtlCopyMemory(&cdData->CallerName,
+                          &exclusiveLock->CallerName,
+                          nameLength);
+
+            // Send Exclusive Lock notification
+            DeviceSendNotification(deviceExtension,
+                                   &GUID_IO_CDROM_EXCLUSIVE_LOCK,
+                                   0,
+                                   NULL);
+
+            // Log an informational event with the caller name
+            logEntry = IoAllocateErrorLogEntry(
+                                deviceExtension->DeviceObject,
+                                sizeof(IO_ERROR_LOG_PACKET) + CDROM_EXCLUSIVE_CALLER_LENGTH);
+
+            if (logEntry != NULL)
+            {
+                PUCHAR dumpDataPtr = (PUCHAR) logEntry->DumpData;
+
+                logEntry->FinalStatus       = STATUS_SUCCESS;
+                logEntry->ErrorCode         = IO_CDROM_EXCLUSIVE_LOCK;
+                logEntry->SequenceNumber    = 0;
+                logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
+                logEntry->IoControlCode     = IOCTL_CDROM_EXCLUSIVE_ACCESS;
+                logEntry->RetryCount        = 0;
+                logEntry->UniqueErrorValue  = 0x1;
+                logEntry->DumpDataSize      = CDROM_EXCLUSIVE_CALLER_LENGTH;
+
+                RtlCopyMemory(dumpDataPtr,
+                                (PUCHAR)&cdData->CallerName,
+                                CDROM_EXCLUSIVE_CALLER_LENGTH);
+
+                // Write the error log packet.
+                IoWriteErrorLogEntry(logEntry);
+            }
+
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestHandleExclusiveAccessLockDevice: Unable to lock device, device already locked.\n"));
+
+            status = STATUS_ACCESS_DENIED;
+        }
+    }
+
+    RequestCompletion(deviceExtension, Request, status, 0);
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessUnlockDevice(
+    _In_ WDFDEVICE Device,
+    _In_ WDFREQUEST Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessUnlockDevice
+
+Arguments:
+
+    Device - device handle
+    Request - request to be handled
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
+    WDFFILEOBJECT           fileObject = NULL;
+
+    PAGED_CODE();
+
+    fileObject = WdfRequestGetFileObject(Request);
+
+    if (fileObject == NULL)
+    {
+        // The device can be unlocked from exclusive mode only via the file object which locked it.
+        status = STATUS_INVALID_HANDLE;
+
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                    "RequestHandleExclusiveAccessUnlockDevice: FileObject is NULL, cannot release exclusive access\n"));
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(PCDROM_EXCLUSIVE_ACCESS),
+                                               &exclusiveAccess,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = DeviceUnlockExclusive(deviceExtension, fileObject,
+                        TEST_FLAG(exclusiveAccess->Flags, CDROM_NO_MEDIA_NOTIFICATIONS));
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessUnlockDevice: Device unlocked\n"));
+    }
+
+    RequestCompletion(deviceExtension, Request, status, 0);
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleQueryPropertyRetrieveCachedData(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_QUERY_PROPERTY when the required data is cached.
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PSTORAGE_PROPERTY_QUERY inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->PropertyId == StorageDeviceProperty)
+        {
+            // check output buffer length
+            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
+            {
+                // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
+                // so this must be a success case with no data transferred
+                *DataLength = 0;
+                status = STATUS_SUCCESS;
+            }
+            else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
+            {
+                // Buffer too small
+                *DataLength = DeviceExtension->DeviceDescriptor->Size;
+                status = STATUS_BUFFER_TOO_SMALL;
+            }
+            else
+            {
+                PSTORAGE_DEVICE_DESCRIPTOR  outputDescriptor = NULL;
+                CHAR*                       localDescriptorBuffer = (CHAR*)DeviceExtension->DeviceDescriptor;
+
+                status = WdfRequestRetrieveOutputBuffer(Request,
+                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                        &outputDescriptor,
+                                                        NULL);
+
+                if (NT_SUCCESS(status))
+                {
+                    // transfer as much data out as the buffer will allow
+                    *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                      DeviceExtension->DeviceDescriptor->Size);
+
+                    RtlCopyMemory(outputDescriptor,
+                                  DeviceExtension->DeviceDescriptor,
+                                  *DataLength);
+
+                    // walk through and update offset variables to reflect data that didn't make it into the output buffer
+                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, VendorIdOffset)) &&
+                        (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0) &&
+                        (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0xFFFFFFFF))
+                    {
+                        // set VendorIdOffset appropriately
+                        if (*DataLength <
+                            (DeviceExtension->DeviceDescriptor->VendorIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->VendorIdOffset)))
+                        {
+                            outputDescriptor->VendorIdOffset = 0;
+                        }
+                    }
+
+                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductIdOffset)) &&
+                        (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0) &&
+                        (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0xFFFFFFFF))
+                    {
+                        // set ProductIdOffset appropriately
+                        if (*DataLength <
+                            (DeviceExtension->DeviceDescriptor->ProductIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductIdOffset)))
+                        {
+                            outputDescriptor->ProductIdOffset = 0;
+                        }
+                    }
+
+                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductRevisionOffset)) &&
+                        (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0) &&
+                        (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0xFFFFFFFF))
+                    {
+                        // set ProductRevisionOffset appropriately
+                        if (*DataLength <
+                            (DeviceExtension->DeviceDescriptor->ProductRevisionOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductRevisionOffset)))
+                        {
+                            outputDescriptor->ProductRevisionOffset = 0;
+                        }
+                    }
+
+                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, SerialNumberOffset)) &&
+                        (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0) &&
+                        (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0xFFFFFFFF))
+                    {
+                        // set SerialNumberOffset appropriately
+                        if (*DataLength <
+                            (DeviceExtension->DeviceDescriptor->SerialNumberOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->SerialNumberOffset)))
+                        {
+                            // NOTE: setting this to 0 since that is what most port drivers do
+                            //       [this could cause issues with SCSI port devices whose clients expect -1 in this field]
+                            outputDescriptor->SerialNumberOffset = 0;
+                        }
+                    }
+                    status = STATUS_SUCCESS;
+                }
+            }
+        }   //end of StorageDeviceProperty
+        else if (inputBuffer->PropertyId == StorageAdapterProperty)
+        {
+            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
+            {
+                // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
+                // so this must be a success case with no data transferred
+                *DataLength = 0;
+                status = STATUS_SUCCESS;
+            }
+            else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
+            {
+                // Buffer too small
+                *DataLength = DeviceExtension->AdapterDescriptor->Size;
+                status = STATUS_BUFFER_TOO_SMALL;
+            }
+            else
+            {
+                PSTORAGE_ADAPTER_DESCRIPTOR outputDescriptor = NULL;
+
+                status = WdfRequestRetrieveOutputBuffer(Request,
+                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                        &outputDescriptor,
+                                                        NULL);
+                if (NT_SUCCESS(status))
+                {
+                    // copy as much data out as the buffer will allow
+                    *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                      DeviceExtension->AdapterDescriptor->Size);
+
+                    RtlCopyMemory(outputDescriptor,
+                                  DeviceExtension->AdapterDescriptor,
+                                  *DataLength);
+
+                    // set status
+                    status = STATUS_SUCCESS;
+                }
+            }
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleQueryPropertyDeviceUniqueId(
+    _In_ WDFDEVICE        Device,
+    _In_ WDFREQUEST       Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceUniqueIdProperty.
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    PSTORAGE_PROPERTY_QUERY     inputBuffer = NULL;
+    PSTORAGE_DESCRIPTOR_HEADER  descHeader = NULL;
+    size_t                      outLength = 0;
+    WDF_REQUEST_PARAMETERS      requestParameters;
+
+    // Get the Request parameters
+    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+    WdfRequestGetParameters(Request, &requestParameters);
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           requestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        BOOLEAN overflow = FALSE;
+        BOOLEAN infoFound = FALSE;
+
+        // Must run at less then dispatch.
+        if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
+        {
+            NT_ASSERT(FALSE);
+            outLength = 0;
+            status = STATUS_INVALID_LEVEL;
+        }
+        else if (inputBuffer->QueryType == PropertyExistsQuery)
+        {
+            outLength = 0;
+            status = STATUS_SUCCESS;
+        }
+        else if (inputBuffer->QueryType != PropertyStandardQuery)
+        {
+            outLength = 0;
+            status = STATUS_NOT_SUPPORTED;
+        }
+        else
+        {
+            // Check AdditionalParameters validity.
+            if (inputBuffer->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS)
+            {
+                // Do nothing
+            }
+            else if (inputBuffer->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY)
+            {
+                // Do nothing
+            }
+            else
+            {
+                outLength = 0;
+                status = STATUS_INVALID_PARAMETER;
+            }
+
+            if (NT_SUCCESS(status) &&
+                (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)))
+            {
+                outLength = 0;
+                status = STATUS_INFO_LENGTH_MISMATCH;
+            }
+        }
+
+        // From this point forward the status depends on the overflow
+        // and infoFound flags.
+        if (NT_SUCCESS(status))
+        {
+            outLength = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+            status = WdfRequestRetrieveOutputBuffer(Request,
+                                                    requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                    &descHeader,
+                                                    NULL);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            RtlZeroMemory(descHeader, outLength);
+
+            descHeader->Version = DUID_VERSION_1;
+            descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER);
+
+            // Try to build device unique id from StorageDeviceIdProperty.
+            status = RequestDuidGetDeviceIdProperty(deviceExtension,
+                                                    Request,
+                                                    requestParameters,
+                                                    &outLength);
+
+            if (status == STATUS_BUFFER_OVERFLOW)
+            {
+                overflow = TRUE;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                infoFound = TRUE;
+            }
+
+            // Try to build device unique id from StorageDeviceProperty.
+            status = RequestDuidGetDeviceProperty(deviceExtension,
+                                                  Request,
+                                                  requestParameters,
+                                                  &outLength);
+
+            if (status == STATUS_BUFFER_OVERFLOW)
+            {
+                overflow = TRUE;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                infoFound = TRUE;
+            }
+
+            // Return overflow, success, or a generic error.
+            if (overflow)
+            {
+                // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
+                // success to the user.  Otherwise, send an error so the user
+                // knows a larger buffer is required.
+                if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER))
+                {
+                    status = STATUS_SUCCESS;
+                }
+                else
+                {
+                    outLength = (ULONG)WdfRequestGetInformation(Request);
+                    status = STATUS_BUFFER_OVERFLOW;
+                }
+
+            }
+            else if (infoFound)
+            {
+                status = STATUS_SUCCESS;
+
+                // Exercise the compare routine.  This should always succeed.
+                NT_ASSERT(DuidExactMatch == CompareStorageDuids((PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader,
+                                                             (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader));
+
+            }
+            else
+            {
+                status = STATUS_NOT_FOUND;
+            }
+        }
+    }
+
+    RequestCompletion(deviceExtension, Request, status, outLength);
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleQueryPropertyWriteCache(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceWriteCacheProperty.
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                      status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION       deviceExtension = DeviceGetExtension(Device);
+    PSTORAGE_PROPERTY_QUERY       query = NULL;
+    PSTORAGE_WRITE_CACHE_PROPERTY writeCache = NULL;
+    PMODE_PARAMETER_HEADER        modeData = NULL;
+    PMODE_CACHING_PAGE            pageData = NULL;
+    size_t                        length = 0;
+    ULONG                         information = 0;
+    PSCSI_REQUEST_BLOCK           srb = NULL;
+    WDF_REQUEST_PARAMETERS        requestParameters;
+
+    // Get the Request parameters
+    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
+    WdfRequestGetParameters(Request, &requestParameters);
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           requestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &query,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+
+        // Must run at less then dispatch.
+        if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
+        {
+            NT_ASSERT(FALSE);
+            status = STATUS_INVALID_LEVEL;
+        }
+        else if (query->QueryType == PropertyExistsQuery)
+        {
+            information = 0;
+            status = STATUS_SUCCESS;
+        }
+        else if (query->QueryType != PropertyStandardQuery)
+        {
+            status = STATUS_NOT_SUPPORTED;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        length = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+
+        if (length < sizeof(STORAGE_DESCRIPTOR_HEADER))
+        {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &writeCache,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(writeCache, length);
+
+        // Set version and required size.
+        writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+        writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+
+        if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY))
+        {
+            // caller only wants header information, bail out.
+            information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+            status = STATUS_SUCCESS;
+
+            RequestCompletion(deviceExtension, Request, status, information);
+            return status;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                    sizeof(SCSI_REQUEST_BLOCK) +
+                                    (sizeof(ULONG_PTR) * 2),
+                                    CDROM_TAG_SRB);
+
+        if (srb == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        // Set known values
+        writeCache->NVCacheEnabled = FALSE;
+        writeCache->UserDefinedPowerProtection = TEST_FLAG(deviceExtension->DeviceFlags, DEV_POWER_PROTECTED);
+
+        // Check for flush cache support by sending a sync cache command
+        // to the device.
+
+        // Set timeout value and mark the request as not being a tagged request.
+        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+        srb->TimeOutValue = TimeOutValueGetCapValue(deviceExtension->TimeOutValue, 4);
+        srb->QueueTag = SP_UNTAGGED;
+        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+        srb->SrbFlags = deviceExtension->SrbFlags;
+
+        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->CdbLength = 10;
+
+        srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+
+        status = DeviceSendSrbSynchronously(Device,
+                                            srb,
+                                            NULL,
+                                            0,
+                                            TRUE,   //flush drive cache
+                                            Request);
+
+        if (NT_SUCCESS(status))
+        {
+            writeCache->FlushCacheSupported = TRUE;
+        }
+        else
+        {
+            // Device does not support sync cache
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "RequestHandleQueryPropertyWriteCache: Synchronize cache failed with status 0x%X\n", status));
+            writeCache->FlushCacheSupported = FALSE;
+
+            // Reset the status if there was any failure
+            status = STATUS_SUCCESS;
+        }
+
+        modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                         MODE_PAGE_DATA_SIZE,
+                                         CDROM_TAG_MODE_DATA);
+
+        if (modeData == NULL)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                        "RequestHandleQueryPropertyWriteCache: Unable to allocate mode data buffer\n"));
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
+
+        length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
+                                                     (PCHAR)modeData,
+                                                     MODE_PAGE_DATA_SIZE,
+                                                     MODE_PAGE_CACHING,
+                                                     MODE_SENSE_CURRENT_VALUES);
+
+        if (length < sizeof(MODE_PARAMETER_HEADER))
+        {
+            // Retry the request in case of a check condition.
+            length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
+                                                         (PCHAR)modeData,
+                                                         MODE_PAGE_DATA_SIZE,
+                                                         MODE_PAGE_CACHING,
+                                                         MODE_SENSE_CURRENT_VALUES);
+
+            if (length < sizeof(MODE_PARAMETER_HEADER))
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));
+                status = STATUS_IO_DEVICE_ERROR;
+            }
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // If the length is greater than length indicated by the mode data reset
+        // the data to the mode data.
+        if (length > (ULONG) (modeData->ModeDataLength + 1))
+        {
+            length = modeData->ModeDataLength + 1;
+        }
+
+        // Look for caching page in the returned mode page data.
+        pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
+                                             length,
+                                             MODE_PAGE_CACHING,
+                                             TRUE);
+
+        // Check if valid caching page exists.
+        if (pageData == NULL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));
+
+            // Set write cache value as unknown.
+            writeCache->WriteCacheEnabled = WriteCacheEnableUnknown;
+            writeCache->WriteCacheType = WriteCacheTypeUnknown;
+        }
+        else
+        {
+            writeCache->WriteCacheEnabled = pageData->WriteCacheEnable
+                                            ? WriteCacheEnabled
+                                            : WriteCacheDisabled;
+
+            writeCache->WriteCacheType = pageData->WriteCacheEnable
+                                         ? WriteCacheTypeWriteBack
+                                         : WriteCacheTypeUnknown;
+        }
+
+        // Check write through support.
+        if (modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED)
+        {
+            writeCache->WriteThroughSupported = WriteThroughSupported;
+        }
+        else
+        {
+            writeCache->WriteThroughSupported = WriteThroughNotSupported;
+        }
+
+        // Get the changeable caching mode page and check write cache is changeable.
+        RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
+
+        length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
+                                                     (PCHAR) modeData,
+                                                     MODE_PAGE_DATA_SIZE,
+                                                     MODE_PAGE_CACHING,
+                                                     MODE_SENSE_CHANGEABLE_VALUES);
+
+        if (length < sizeof(MODE_PARAMETER_HEADER))
+        {
+            // Retry the request in case of a check condition.
+            length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
+                                                         (PCHAR) modeData,
+                                                         MODE_PAGE_DATA_SIZE,
+                                                         MODE_PAGE_CACHING,
+                                                         MODE_SENSE_CHANGEABLE_VALUES);
+
+            if (length < sizeof(MODE_PARAMETER_HEADER))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));
+
+                // If the device fails to return changeable pages, then
+                // set the write cache changeable value to unknown.
+                writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
+                information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+            }
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // If the length is greater than length indicated by the mode data reset
+        // the data to the mode data.
+        if (length > (ULONG) (modeData->ModeDataLength + 1))
+        {
+            length = modeData->ModeDataLength + 1;
+        }
+
+        // Look for caching page in the returned mode page data.
+        pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
+                                             length,
+                                             MODE_PAGE_CACHING,
+                                             TRUE);
+        // Check if valid caching page exists.
+        if (pageData == NULL)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));
+
+            // Set write cache changeable value to unknown.
+            writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
+        }
+        else
+        {
+            writeCache->WriteCacheChangeable = pageData->WriteCacheEnable
+                                               ? WriteCacheChangeable
+                                               : WriteCacheNotChangeable;
+        }
+
+        information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+
+    }
+
+    FREE_POOL(srb);
+    FREE_POOL(modeData);
+
+    RequestCompletion(deviceExtension, Request, status, information);
+
+    return status;
+}
+
+NTSTATUS
+RequestValidateDvdReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_DVD_READ_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
+    ULONG                   keyLength = 0;
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &keyParameters,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "DvdDeviceControl: EstablishDriveKey - challenge "
+                        "key buffer too small\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        switch(keyParameters->KeyType)
+        {
+
+            case DvdChallengeKey:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_CHALLENGE_KEY_LENGTH);
+                keyLength = DVD_CHALLENGE_KEY_LENGTH;
+                break;
+            }
+            case DvdBusKey1:
+            case DvdBusKey2:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_BUS_KEY_LENGTH);
+                keyLength = DVD_BUS_KEY_LENGTH;
+                break;
+            }
+            case DvdTitleKey:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_TITLE_KEY_LENGTH);
+                keyLength = DVD_TITLE_KEY_LENGTH;
+                break;
+            }
+            case DvdAsf:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_ASF_LENGTH);
+                keyLength = DVD_ASF_LENGTH;
+                break;
+            }
+            case DvdDiskKey:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_DISK_KEY_LENGTH);
+                keyLength = DVD_DISK_KEY_LENGTH;
+                break;
+            }
+            case DvdGetRpcKey:
+            {
+                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_RPC_KEY_LENGTH);
+                keyLength = DVD_RPC_KEY_LENGTH;
+                break;
+            }
+            default:
+            {
+                keyLength = sizeof(DVD_COPY_PROTECT_KEY);
+                break;
+            }
+        }
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < keyLength)
+        {
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "DvdDeviceControl: EstablishDriveKey - output "
+                        "buffer too small\n"));
+            status = STATUS_BUFFER_TOO_SMALL;
+            *DataLength = keyLength;
+        }
+        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
+                 DeviceExtension->AdapterDescriptor->AlignmentMask)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+        {
+            // reject the request if it's not a DVD device.
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestValidateDvdEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DVD_END_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID sessionId = NULL;
+
+    UNREFERENCED_PARAMETER(DeviceExtension);
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &sessionId,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(DVD_SESSION_ID))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "DvdDeviceControl: EndSession - input buffer too "
+                        "small\n"));
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestValidateAacsEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_AACS_END_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID sessionId = NULL;
+    PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &sessionId,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        if (!cdData->Mmc.IsAACS)
+        {
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+        else if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestValidateEnableStreaming(
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Validates an IOCTL_CDROM_ENABLE_STREAMING request
+
+Arguments:
+
+    Request - request to be handled
+    RequestParameters - request parameters
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+
+    PCDROM_STREAMING_CONTROL    inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+        sizeof(CDROM_STREAMING_CONTROL))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Get the request type using CDROM_STREAMING_CONTROL structure
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(CDROM_STREAMING_CONTROL),
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->RequestType != CdromStreamingDisable &&
+            inputBuffer->RequestType != CdromStreamingEnableForReadOnly &&
+            inputBuffer->RequestType != CdromStreamingEnableForWriteOnly &&
+            inputBuffer->RequestType != CdromStreamingEnableForReadWrite)
+        {
+            // Unknown request type
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestValidateSendOpcInformation(
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Validates an IOCTL_CDROM_SEND_OPC_INFORMATION request
+
+Arguments:
+
+    Request - request to be handled
+    RequestParameters - request parameters
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+
+    PCDROM_SIMPLE_OPC_INFO      inputBuffer = NULL;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+        sizeof(CDROM_SIMPLE_OPC_INFO))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Get the request type using CDROM_SIMPLE_OPC_INFO structure
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(CDROM_SIMPLE_OPC_INFO),
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->RequestType != SimpleOpcInfo)
+        {
+            // Unknown request type
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+
+NTSTATUS
+RequestValidateGetPerformance(
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Validates an IOCTL_CDROM_GET_PERFORMANCE request
+
+Arguments:
+
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_WRITE_SPEED_REQUEST  writeSpeedRequest = NULL;
+    PCDROM_PERFORMANCE_REQUEST  performanceRequest = NULL;
+
+    *DataLength = 0;
+
+    // CDROM_WRITE_SPEED_REQUEST is the smallest performance request that we support.
+    // We use it to retrieve request type and then check input length more carefully
+    // on a per request type basis.
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+        sizeof(CDROM_WRITE_SPEED_REQUEST))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               sizeof(CDROM_WRITE_SPEED_REQUEST),
+                                               (PVOID*)&writeSpeedRequest,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (writeSpeedRequest->RequestType == CdromPerformanceRequest)
+        {
+            // CDROM_PERFORMANCE_REQUEST is bigger than CDROM_WRITE_SPEED_REQUEST,
+            // so we perform more checks and retrieve more bytes through WDF.
+            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+                sizeof(CDROM_PERFORMANCE_REQUEST))
+            {
+                status = STATUS_INFO_LENGTH_MISMATCH;
+            }
+            if (NT_SUCCESS(status))
+            {
+                status = WdfRequestRetrieveInputBuffer(Request,
+                                                       sizeof(CDROM_PERFORMANCE_REQUEST),
+                                                       &performanceRequest,
+                                                       NULL);
+            }
+
+            if (!NT_SUCCESS(status))
+            {
+                // just pass the status code from above
+            }
+            // validate all enum-type fields of CDROM_PERFORMANCE_REQUEST
+            else if (performanceRequest->PerformanceType != CdromReadPerformance &&
+                     performanceRequest->PerformanceType != CdromWritePerformance)
+            {
+                status = STATUS_INVALID_PARAMETER;
+            }
+            else if (performanceRequest->Exceptions != CdromNominalPerformance &&
+                     performanceRequest->Exceptions != CdromEntirePerformanceList &&
+                     performanceRequest->Exceptions != CdromPerformanceExceptionsOnly)
+            {
+                status = STATUS_INVALID_PARAMETER;
+            }
+            else if (performanceRequest->Tolerance != Cdrom10Nominal20Exceptions)
+            {
+                status = STATUS_INVALID_PARAMETER;
+            }
+        }
+        else if (writeSpeedRequest->RequestType == CdromWriteSpeedRequest)
+        {
+            // No additional checks here: all remaining fields are ignored
+            // if RequestType == CdromWriteSpeedRequest.
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    // finally, check output buffer length
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+                 sizeof(CDROM_PERFORMANCE_HEADER))
+        {
+            status = STATUS_BUFFER_TOO_SMALL;
+            *DataLength = sizeof(CDROM_PERFORMANCE_HEADER);
+        }
+        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
+            ((USHORT)-1))
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+PCDB
+RequestGetScsiPassThroughCdb(
+    _In_ PIRP Irp
+    )
+/*++
+
+Routine Description:
+
+    Get the CDB structure from the SCSI pass through
+
+Arguments:
+
+    Irp - request to be handled
+
+Return Value:
+
+    PCDB
+
+--*/
+{
+    PCDB                cdb = NULL;
+    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+    ULONG               inputBufferLength = 0;
+    PVOID               inputBuffer = NULL;
+    BOOLEAN             legacyPassThrough = FALSE;
+
+    PAGED_CODE();
+
+    if (((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) ||
+         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
+         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
+         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX)) &&
+        (Irp->AssociatedIrp.SystemBuffer != NULL))
+    {
+        inputBufferLength = currentIrpStack->Parameters.DeviceIoControl.InputBufferLength;
+        inputBuffer = Irp->AssociatedIrp.SystemBuffer;
+        legacyPassThrough = TRUE;
+
+        if ((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
+            (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX))
+        {
+            legacyPassThrough = FALSE;
+        }
+
+        //
+        //  If this is a 32 bit application running on 64 bit then thunk the
+        //  input structures to grab the cdb.
+        //
+
+#if BUILD_WOW64_ENABLED && defined(_WIN64)
+
+        if (IoIs32bitProcess(Irp))
+        {
+            if (legacyPassThrough)
+            {
+                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32))
+                {
+                    cdb = (PCDB)((PSCSI_PASS_THROUGH32)inputBuffer)->Cdb;
+                }
+            }
+            else
+            {
+                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32_EX))
+                {
+                    cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)inputBuffer)->Cdb;
+                }
+            }
+
+        }
+        else
+
+#endif
+
+        {
+            if (legacyPassThrough)
+            {
+                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH))
+                {
+                    cdb = (PCDB)((PSCSI_PASS_THROUGH)inputBuffer)->Cdb;
+                }
+            }
+            else
+            {
+                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH_EX))
+                {
+                    cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)inputBuffer)->Cdb;
+                }
+            }
+        }
+    }
+
+    return cdb;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleScsiPassThrough(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_SCSI_PASS_THROUGH
+                     IOCTL_SCSI_PASS_THROUGH_DIRECT
+
+   The function sets the MinorFunction field of irpStack,
+   and pass the request to lower level driver.
+
+Arguments:
+
+    Device - device object
+    Request - request to be handled
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_UNSUCCESSFUL;
+    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
+    PIRP                        irp = WdfRequestWdmGetIrp(Request);
+    PZERO_POWER_ODD_INFO        zpoddInfo = deviceExtension->ZeroPowerODDInfo;
+    PCDB                        cdb = NULL;
+    BOOLEAN                     isSoftEject = FALSE;
+
+#if DBG
+    PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
+#endif
+
+
+    PAGED_CODE();
+
+#if DBG
+    // SPTI is always processed in sync manner.
+    NT_ASSERT(requestContext->SyncRequired);
+#endif
+
+    if ((zpoddInfo != NULL) &&
+        (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
+        (zpoddInfo->Load == 0))                                         // Drawer
+    {
+        cdb = RequestGetScsiPassThroughCdb(irp);
+
+        if ((cdb != NULL) &&
+            (cdb->AsByte[0] == SCSIOP_START_STOP_UNIT) &&
+            (cdb->START_STOP.LoadEject == 1) &&
+            (cdb->START_STOP.Start == 0))
+        {
+            isSoftEject = TRUE;
+        }
+    }
+
+    WdfRequestFormatRequestUsingCurrentType(Request);
+
+    // Special for SPTI, set the MinorFunction.
+    {
+        PIO_STACK_LOCATION  nextStack = IoGetNextIrpStackLocation(irp);
+
+        nextStack->MinorFunction = 1;
+    }
+
+
+    status = RequestSend(deviceExtension,
+                         Request,
+                         deviceExtension->IoTarget,
+                         WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+                         NULL);
+
+
+    if (!NT_SUCCESS(status) &&
+        (isSoftEject != FALSE))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                   "RequestHandleScsiPassThrough: soft eject detected, device marked as active\n"));
+
+        DeviceMarkActive(deviceExtension, TRUE, FALSE);
+    }
+
+    RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleMountQueryUniqueId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PMOUNTDEV_UNIQUE_ID uniqueId = NULL;
+
+    *DataLength = 0;
+
+    if (!DeviceExtension->MountedDeviceInterfaceName.Buffer)
+    {
+        status = STATUS_INVALID_PARAMETER;
+    }
+    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID))
+    {
+        *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
+        status = STATUS_BUFFER_TOO_SMALL;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &uniqueId,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(uniqueId, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
+
+        uniqueId->UniqueIdLength = DeviceExtension->MountedDeviceInterfaceName.Length;
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            (sizeof(USHORT) + DeviceExtension->MountedDeviceInterfaceName.Length))
+        {
+            *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlCopyMemory(uniqueId->UniqueId,
+                      DeviceExtension->MountedDeviceInterfaceName.Buffer,
+                      uniqueId->UniqueIdLength);
+
+        *DataLength = sizeof(USHORT) + uniqueId->UniqueIdLength;
+        status = STATUS_SUCCESS;
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleMountQueryDeviceName(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    PMOUNTDEV_NAME  name = NULL;
+
+    *DataLength = 0;
+
+    NT_ASSERT(DeviceExtension->DeviceName.Buffer);
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(MOUNTDEV_NAME);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &name,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(name, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
+        name->NameLength = DeviceExtension->DeviceName.Length;
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+            (sizeof(USHORT) + DeviceExtension->DeviceName.Length))
+        {
+            status = STATUS_BUFFER_OVERFLOW;
+            *DataLength = sizeof(MOUNTDEV_NAME);
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlCopyMemory(name->Name,
+                      DeviceExtension->DeviceName.Buffer,
+                      name->NameLength);
+
+        status = STATUS_SUCCESS;
+        *DataLength = sizeof(USHORT) + name->NameLength;
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleMountQuerySuggestedLinkName(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+
+    PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName = NULL;
+
+    WCHAR                    driveLetterNameBuffer[10] = {0};
+    RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
+    PWSTR                    valueName = NULL;
+    UNICODE_STRING           driveLetterName = {0};
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
+        sizeof(MOUNTDEV_SUGGESTED_LINK_NAME))
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        valueName = ExAllocatePoolWithTag(PagedPool,
+                                          DeviceExtension->DeviceName.Length + sizeof(WCHAR),
+                                          CDROM_TAG_STRINGS);
+        if (valueName == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlCopyMemory(valueName,
+                      DeviceExtension->DeviceName.Buffer,
+                      DeviceExtension->DeviceName.Length);
+        valueName[DeviceExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
+
+        driveLetterName.Buffer = driveLetterNameBuffer;
+        driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
+        driveLetterName.Length = 0;
+
+        queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
+        queryTable[0].Name = valueName;
+        queryTable[0].EntryContext = &driveLetterName;
+        queryTable[0].DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
+
+        status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                        L"\\Registry\\Machine\\System\\DISK", // why hard coded?
+                                        queryTable, NULL, NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if ((driveLetterName.Length == 4) &&
+            (driveLetterName.Buffer[0] == '%') &&
+            (driveLetterName.Buffer[1] == ':'))
+        {
+            driveLetterName.Buffer[0] = 0xFF;
+        }
+        else if ((driveLetterName.Length != 4) ||
+                 (driveLetterName.Buffer[0] < FirstDriveLetter) ||
+                 (driveLetterName.Buffer[0] > LastDriveLetter) ||
+                 (driveLetterName.Buffer[1] != ':'))
+        {
+            status = STATUS_NOT_FOUND;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &suggestedName,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlZeroMemory(suggestedName, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
+        suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
+        suggestedName->NameLength = 28;
+
+        *DataLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
+
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < *DataLength)
+        {
+            *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                               L"\\Registry\\Machine\\System\\DISK",
+                               valueName);
+
+        RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
+        suggestedName->Name[12] = driveLetterName.Buffer[0];
+        suggestedName->Name[13] = ':';
+    }
+
+    FREE_POOL(valueName);
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleReadTOC(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_READ_TOC
+                     IOCTL_CDROM_GET_LAST_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    VOID*    outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (DeviceIsPlayActive(DeviceExtension->Device))
+    {
+        status = STATUS_DEVICE_BUSY;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    // handle the request
+    if (NT_SUCCESS(status))
+    {
+        size_t  transferSize;
+        CDB     cdb;
+
+        transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, sizeof(CDROM_TOC));
+
+        RtlZeroMemory(outputBuffer, transferSize);
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION)
+        {
+            // Set format to return first and last session numbers.
+            cdb.READ_TOC.Format2 = CDROM_READ_TOC_EX_FORMAT_SESSION;
+        }
+        else
+        {
+            // Use MSF addressing
+            cdb.READ_TOC.Msf = 1;
+        }
+
+        cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
+        cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
+        cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+            RtlCopyMemory(outputBuffer,
+                          DeviceExtension->ScratchContext.ScratchBuffer,
+                          *DataLength);
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleReadTocEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_READ_TOC_EX
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_READ_TOC_EX  inputBuffer = NULL;
+    VOID*               outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (DeviceIsPlayActive(DeviceExtension->Device))
+    {
+        status = STATUS_DEVICE_BUSY;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    // handle the request
+    if (NT_SUCCESS(status))
+    {
+        size_t  transferSize;
+        CDB     cdb;
+
+        transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, MAXUSHORT);
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
+        cdb.READ_TOC.Msf = inputBuffer->Msf;
+        cdb.READ_TOC.Format2 = inputBuffer->Format;
+        cdb.READ_TOC.StartingTrack = inputBuffer->SessionTrack;
+        cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
+        cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE)
+            {
+                *DataLength = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            else
+            {
+                *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+                RtlCopyMemory(outputBuffer,
+                              DeviceExtension->ScratchContext.ScratchBuffer,
+                              *DataLength);
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+GetConfigurationDataConversionTypeAllToTypeOne(
+    _In_    FEATURE_NUMBER       RequestedFeature,
+    _In_    PSCSI_REQUEST_BLOCK  Srb,
+    _Out_   size_t *             DataLength
+    )
+/*++
+
+Routine Description:
+
+    Some CDROM devices do not handle the GET CONFIGURATION commands with
+    TYPE ONE request. The command will time out causing a bus reset.
+    To avoid this problem we set a device flag during start device if the device
+    fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
+    tried as TYPE ALL request and the data will be converted to TYPE ONE format
+    in this routine.
+
+Arguments:
+
+    RequestedFeature - device context
+    Srb - request to be handled
+    DataLength - transfer data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    PFEATURE_HEADER     featureHeader = NULL;
+    FEATURE_NUMBER      thisFeature;
+    ULONG               totalLength = 0;
+    ULONG               featureLength = 0;
+    ULONG               headerLength = 0;
+
+    PGET_CONFIGURATION_HEADER   header = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (Srb->DataTransferLength < RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
+    {
+        // do not have valid data.
+        return;
+    }
+
+    // Calculate the length of valid data available in the
+    // capabilities buffer from the DataLength field
+    header = (PGET_CONFIGURATION_HEADER) Srb->DataBuffer;
+    REVERSE_BYTES(&totalLength, header->DataLength);
+
+    totalLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+    // Make sure the we have enough data in the SRB
+    totalLength = min(totalLength, Srb->DataTransferLength);
+
+    // If we have received enough data from the device
+    // check for the given feature.
+    if (totalLength >= (sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)))
+    {
+        // Feature information is present. Verify the feature.
+        featureHeader = (PFEATURE_HEADER)((PUCHAR)Srb->DataBuffer + sizeof(GET_CONFIGURATION_HEADER));
+
+        thisFeature  = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);
+
+        if (thisFeature == RequestedFeature)
+        {
+            // Calculate the feature length
+            featureLength = sizeof(FEATURE_HEADER) + featureHeader->AdditionalLength;
+        }
+    }
+
+    // Calculate the total size
+    totalLength = sizeof(GET_CONFIGURATION_HEADER) + featureLength;
+
+    headerLength = totalLength -
+        RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+    REVERSE_BYTES(header->DataLength, &headerLength);
+
+    *DataLength = totalLength;
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+GetConfigurationDataSynthesize(
+    _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
+    _In_                            ULONG           InputBufferSize,
+    _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
+    _In_                            size_t          OutputBufferSize,
+    _In_                            FEATURE_NUMBER  StartingFeature,
+    _In_                            ULONG           RequestType,
+    _Out_                           size_t *        DataLength
+    )
+/*++
+
+Routine Description:
+
+    Get Configuration is a frequently used command, and we don't want it to wake
+    up the device in case it is in Zero Power state. Before entering Zero Power state,
+    the complete response of the command is saved in cache, and since the response
+    is always the same in case there is no media, we can synthesize the response
+    based on the user request.
+
+Arguments:
+
+    InputBuffer - buffer containing cached command response
+    InputBufferSize - size of above buffer
+    OutputBuffer - buffer to fill in result
+    OutputBufferSize - size of above buffer
+    StartingFeature - requested Starting Feature Number
+    RequestType - requested Request Type
+    DataLength - transfer data length
+
+Return Value:
+
+--*/
+{
+    PFEATURE_HEADER     featureHeader = NULL;
+    ULONG               validLength = 0;
+    ULONG               featureLength = 0;
+    ULONG               headerLength = 0;
+    PUCHAR              buffer = NULL;
+    ULONG               bytesRemaining = 0;
+    FEATURE_NUMBER      featureCode = 0;
+    BOOLEAN             shouldCopy = FALSE;
+    size_t              copyLength = 0;
+    size_t              transferedLength = 0;
+    size_t              requiredLength = 0;
+
+    PGET_CONFIGURATION_HEADER   header = NULL;
+
+    PAGED_CODE ();
+
+    if (InputBufferSize < sizeof (GET_CONFIGURATION_HEADER))
+    {
+        // do not have valid data.
+        *DataLength = 0;
+
+        return;
+    }
+
+    // Calculate the length of valid data available in the
+    // capabilities buffer from the DataLength field
+    header = (PGET_CONFIGURATION_HEADER) InputBuffer;
+    REVERSE_BYTES(&validLength, header->DataLength);
+
+    validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+    // Make sure we have enough data
+    validLength = min(validLength, InputBufferSize);
+
+    // Copy the header first
+    copyLength = min(OutputBufferSize, sizeof (GET_CONFIGURATION_HEADER));
+
+    RtlMoveMemory(OutputBuffer,
+                  InputBuffer,
+                  copyLength);
+
+    transferedLength = copyLength;
+    requiredLength = sizeof (GET_CONFIGURATION_HEADER);
+
+    if (validLength > sizeof (GET_CONFIGURATION_HEADER))
+    {
+        buffer = header->Data;
+        bytesRemaining = validLength - sizeof (GET_CONFIGURATION_HEADER);
+
+        // Ignore incomplete feature descriptor
+        while (bytesRemaining >= sizeof (FEATURE_HEADER))
+        {
+            featureHeader = (PFEATURE_HEADER) buffer;
+            shouldCopy = FALSE;
+
+            featureCode = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);
+            featureLength = sizeof (FEATURE_HEADER) + featureHeader->AdditionalLength;
+
+            if (featureCode >= StartingFeature)
+            {
+                switch (RequestType) {
+
+                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL:
+
+                    shouldCopy = TRUE;
+                    break;
+
+                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT:
+
+                    if (featureHeader->Current)
+                    {
+                        shouldCopy = TRUE;
+                    }
+                    break;
+
+                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE:
+
+                    if (featureCode == StartingFeature)
+                    {
+                        shouldCopy = TRUE;
+                    }
+                    break;
+
+                default:
+
+                    break;
+                }
+            }
+
+            if (shouldCopy != FALSE)
+            {
+                copyLength = min(featureLength, bytesRemaining);
+                copyLength = min(copyLength, OutputBufferSize - transferedLength);
+
+                RtlMoveMemory((PUCHAR) OutputBuffer + transferedLength,
+                              buffer,
+                              copyLength);
+
+                transferedLength += copyLength;
+                requiredLength += featureLength;
+            }
+
+            buffer += min(featureLength, bytesRemaining);
+            bytesRemaining -= min(featureLength, bytesRemaining);
+        }
+    }
+
+    // Adjust Data Length field in header
+    if (transferedLength >= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
+    {
+        headerLength = (ULONG) requiredLength - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+        header = (PGET_CONFIGURATION_HEADER) OutputBuffer;
+        REVERSE_BYTES(header->DataLength, &headerLength);
+    }
+
+    *DataLength = transferedLength;
+
+    return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetConfiguration(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_GET_CONFIGURATION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PCDROM_DATA                     cdData = &(DeviceExtension->DeviceAdditionalData);
+    PGET_CONFIGURATION_IOCTL_INPUT  inputBuffer = NULL;
+    VOID*                           outputBuffer = NULL;
+    size_t                          transferByteCount = 0;
+    PZERO_POWER_ODD_INFO            zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+    BOOLEAN                         inZeroPowerState = FALSE;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    //
+    if (!cdData->Mmc.IsMmc)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+    else
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &inputBuffer,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // If device is Zero Power state, there should be no media in device, thus we can synthesize the response
+        // from our cache. Avoid waking up the device in this case.
+        if ((zpoddInfo != NULL) &&
+            (zpoddInfo->InZeroPowerState != FALSE))
+        {
+            inZeroPowerState = TRUE;
+        }
+
+        if ((inZeroPowerState == FALSE) ||
+            (zpoddInfo->GetConfigurationBuffer == NULL))
+        {
+            CDB cdb;
+
+            //The maximum number of bytes that a Drive may return
+            //to describe its Features in one GET CONFIGURATION Command is 65,534
+            transferByteCount = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, (MAXUSHORT - 1));
+
+            // If this is a TYPE ONE request and if this device can't handle this
+            // request, then we need to send TYPE ALL request to the device and
+            // convert the data in the completion routine. If required allocate a big
+            // buffer to get both configuration and feature header.
+            if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+                TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
+            {
+                transferByteCount = max(transferByteCount,
+                                        sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER));
+            }
+
+            ScratchBuffer_BeginUse(DeviceExtension);
+
+            RtlZeroMemory(&cdb, sizeof(CDB));
+            // Set up the CDB
+            cdb.GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
+            cdb.GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8);
+            cdb.GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff);
+
+            cdb.GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8);
+            cdb.GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff);
+            cdb.GET_CONFIGURATION.RequestType        = (UCHAR)(inputBuffer->RequestType);
+
+            // If the device does not support TYPE ONE get configuration commands
+            // then change the request type to TYPE ALL. Convert the returned data to
+            // TYPE ONE format in the completion routine.
+            if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+                TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
+            {
+
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                           "DeviceHandleGetConfiguration: Changing TYPE_ONE Get Config to TYPE_ALL\n"));
+                cdb.GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL;
+            }
+
+            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE, &cdb, 12);
+
+            if (NT_SUCCESS(status))
+            {
+                if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+                    TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
+                {
+
+                    if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(GET_CONFIGURATION_HEADER))
+                    {
+                        // Not enough data to calculate the data length.
+                        // So assume feature is not present
+                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DeviceHandleGetConfiguration: No get config header!\n"));
+                        *DataLength = 0;
+                        status = STATUS_INVALID_DEVICE_REQUEST;
+                    }
+                    else
+                    {
+                        //Some CDROM devices do not handle the GET CONFIGURATION commands with
+                        //TYPE ONE request. The command will time out causing a bus reset.
+                        //To avoid this problem we set a device flag during start device if the device
+                        //fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
+                        //tried as TYPE ALL request and the data will be converted to TYPE ONE format
+                        //in this routine.
+                        GetConfigurationDataConversionTypeAllToTypeOne(inputBuffer->Feature, DeviceExtension->ScratchContext.ScratchSrb, DataLength);
+                        *DataLength = min(*DataLength, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
+                    }
+                }
+                else
+                {
+                    *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+                }
+
+                // copy data to output buffer
+                if (NT_SUCCESS(status))
+                {
+                    RtlMoveMemory(outputBuffer,
+                                  DeviceExtension->ScratchContext.ScratchBuffer,
+                                  *DataLength);
+                }
+            }
+
+            ScratchBuffer_EndUse(DeviceExtension);
+        }
+        else
+        {
+            // We are in Zero Power state, and our cached response is available.
+            // Synthesize the requested data.
+            GetConfigurationDataSynthesize(zpoddInfo->GetConfigurationBuffer,
+                                           zpoddInfo->GetConfigurationBufferSize,
+                                           outputBuffer,
+                                           RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                           inputBuffer->Feature,
+                                           inputBuffer->RequestType,
+                                           DataLength
+                                           );
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleGetDriveGeometry(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DISK_GET_LENGTH_INFO
+                     IOCTL_DISK_GET_DRIVE_GEOMETRY
+                     IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
+                     IOCTL_CDROM_GET_DRIVE_GEOMETRY
+                     IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX
+                     IOCTL_STORAGE_READ_CAPACITY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    VOID*               outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveOutputBuffer(Request,
+                                            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                            &outputBuffer,
+                                            NULL);
+
+    // Issue ReadCapacity to update device extension
+    // with information for current media.
+    if (NT_SUCCESS(status))
+    {
+        status = MediaReadCapacity(DeviceExtension->Device);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        switch(RequestParameters.Parameters.DeviceIoControl.IoControlCode)
+        {
+            case IOCTL_DISK_GET_LENGTH_INFO:
+            {
+                PGET_LENGTH_INFORMATION lengthInfo = (PGET_LENGTH_INFORMATION)outputBuffer;
+
+                lengthInfo->Length = DeviceExtension->PartitionLength;
+                *DataLength = sizeof(GET_LENGTH_INFORMATION);
+                break;
+            }
+            case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+            case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
+            {
+                PDISK_GEOMETRY geometry = (PDISK_GEOMETRY)outputBuffer;
+
+                *geometry = DeviceExtension->DiskGeometry;
+                *DataLength = sizeof(DISK_GEOMETRY);
+                break;
+            }
+            case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+            case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
+            {
+                PDISK_GEOMETRY_EX geometryEx = (PDISK_GEOMETRY_EX)outputBuffer;
+
+                geometryEx->DiskSize = DeviceExtension->PartitionLength;
+                geometryEx->Geometry = DeviceExtension->DiskGeometry;
+                *DataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
+                break;
+            }
+            case IOCTL_STORAGE_READ_CAPACITY:
+            {
+                PSTORAGE_READ_CAPACITY readCapacity = (PSTORAGE_READ_CAPACITY)outputBuffer;
+
+                readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
+                readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);
+
+                readCapacity->BlockLength = DeviceExtension->DiskGeometry.BytesPerSector;
+                if (readCapacity->BlockLength > 0)
+                {
+                    readCapacity->NumberOfBlocks.QuadPart = DeviceExtension->PartitionLength.QuadPart/readCapacity->BlockLength;
+                }
+                else
+                {
+                    readCapacity->NumberOfBlocks.QuadPart = 0;
+                }
+
+                readCapacity->DiskLength = DeviceExtension->PartitionLength;
+
+                *DataLength = sizeof(STORAGE_READ_CAPACITY);
+                break;
+            }
+            default:
+            {
+                NT_ASSERT(FALSE);
+                break;
+            }
+        } // end of switch()
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleDiskVerify(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DISK_VERIFY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+    PVERIFY_INFORMATION verifyInfo = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (!cdData->Mmc.WriteAllowed)
+    {
+        status = STATUS_MEDIA_WRITE_PROTECTED;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &verifyInfo,
+                                               NULL);
+    }
+
+    // handle the request
+    if (NT_SUCCESS(status))
+    {
+        LARGE_INTEGER byteOffset = {0};
+
+        // Add disk offset to starting sector.
+        byteOffset.QuadPart = DeviceExtension->StartingOffset.QuadPart +
+                              verifyInfo->StartingOffset.QuadPart;
+
+        // prevent overflow returning success but only validating small area
+        if (((DeviceExtension->StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart) < DeviceExtension->StartingOffset.QuadPart) ||
+            ((verifyInfo->Length >> DeviceExtension->SectorShift) > MAXUSHORT) ||
+            ((byteOffset.QuadPart >> DeviceExtension->SectorShift) > MAXULONG) )
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+        else
+        {
+            ULONG   transferSize = 0;
+            ULONG   timeoutValue = 0;
+            CDB     cdb;
+            ULONG   sectorOffset;
+            USHORT  sectorCount;
+
+            ScratchBuffer_BeginUse(DeviceExtension);
+
+            // Convert byte offset to sector offset.
+            sectorOffset = (ULONG)(byteOffset.QuadPart >> DeviceExtension->SectorShift);
+
+            // Convert ULONG byte count to USHORT sector count.
+            sectorCount = (USHORT)(verifyInfo->Length >> DeviceExtension->SectorShift);
+
+            RtlZeroMemory(&cdb, sizeof(CDB));
+            // Set up the CDB
+            cdb.CDB10.OperationCode = SCSIOP_VERIFY;
+
+            // Move little endian values into CDB in big endian format.
+            cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
+            cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
+            cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
+            cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
+
+            cdb.CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
+            cdb.CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->Byte0;
+
+            // The verify command is used by the NT FORMAT utility and
+            // requests are sent down for 5% of the volume size. The
+            // request timeout value is calculated based on the number of
+            // sectors verified.
+            if (sectorCount != 0)
+            {
+                // sectorCount is a USHORT, so no overflow here...
+                timeoutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, ((sectorCount + 128) / 128));
+            }
+
+            status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 10, timeoutValue);
+
+            // nothing to do after the command finishes.
+            ScratchBuffer_EndUse(DeviceExtension);
+        }
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleCheckVerify(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_CHECK_VERIFY2
+                     IOCTL_STORAGE_CHECK_VERIFY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+        status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 6, CDROM_TEST_UNIT_READY_TIMEOUT);
+
+        if (NT_SUCCESS(status))
+        {
+            if((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) &&
+               (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength))
+            {
+                PULONG outputBuffer = NULL;
+                status = WdfRequestRetrieveOutputBuffer(Request,
+                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                        &outputBuffer,
+                                                        NULL);
+
+                if (outputBuffer != NULL)
+                {
+                    *outputBuffer = DeviceExtension->MediaChangeCount;
+                    *DataLength = sizeof(ULONG);
+                }
+            }
+            else
+            {
+                *DataLength = 0;
+            }
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleFakePartitionInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DISK_GET_DRIVE_LAYOUT
+                     IOCTL_DISK_GET_DRIVE_LAYOUT_EX
+                     IOCTL_DISK_GET_PARTITION_INFO
+                     IOCTL_DISK_GET_PARTITION_INFO_EX
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    VOID*       outputBuffer = NULL;
+    ULONG       ioctl = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) &&
+            (ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) &&
+            (ioctl != IOCTL_DISK_GET_PARTITION_INFO) &&
+            (ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX))
+        {
+            status = STATUS_INTERNAL_ERROR;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    // handle the request
+    if (NT_SUCCESS(status))
+    {
+        switch (ioctl)
+        {
+        case IOCTL_DISK_GET_DRIVE_LAYOUT:
+            *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
+            RtlZeroMemory(outputBuffer, *DataLength);
+            break;
+        case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
+            *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
+            RtlZeroMemory(outputBuffer, *DataLength);
+            break;
+        case IOCTL_DISK_GET_PARTITION_INFO:
+            *DataLength = sizeof(PARTITION_INFORMATION);
+            RtlZeroMemory(outputBuffer, *DataLength);
+            break;
+        case IOCTL_DISK_GET_PARTITION_INFO_EX:
+            *DataLength = sizeof(PARTITION_INFORMATION_EX);
+            RtlZeroMemory(outputBuffer, *DataLength);
+            break;
+        default:
+            NT_ASSERT(!"Invalid ioctl should not have reached this point\n");
+            break;
+        }
+
+        // if we are getting the drive layout, then we need to start by
+        // adding some of the non-partition stuff that says we have
+        // exactly one partition available.
+        if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT)
+        {
+            PDRIVE_LAYOUT_INFORMATION layout;
+            layout = (PDRIVE_LAYOUT_INFORMATION)outputBuffer;
+            layout->PartitionCount = 1;
+            layout->Signature = 1;
+            outputBuffer = (PVOID)(layout->PartitionEntry);
+            ioctl = IOCTL_DISK_GET_PARTITION_INFO;
+        }
+        else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX)
+        {
+            PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
+            layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)outputBuffer;
+            layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
+            layoutEx->PartitionCount = 1;
+            layoutEx->Mbr.Signature = 1;
+            outputBuffer = (PVOID)(layoutEx->PartitionEntry);
+            ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX;
+        }
+
+        // NOTE: the local var 'ioctl' is now modified to either EX or
+        // non-EX version. the local var 'systemBuffer' is now pointing
+        // to the partition information structure.
+        if (ioctl == IOCTL_DISK_GET_PARTITION_INFO)
+        {
+            PPARTITION_INFORMATION partitionInfo;
+            partitionInfo = (PPARTITION_INFORMATION)outputBuffer;
+            partitionInfo->RewritePartition = FALSE;
+            partitionInfo->RecognizedPartition = TRUE;
+            partitionInfo->PartitionType = PARTITION_FAT32;
+            partitionInfo->BootIndicator = FALSE;
+            partitionInfo->HiddenSectors = 0;
+            partitionInfo->StartingOffset.QuadPart = 0;
+            partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
+            partitionInfo->PartitionNumber = 0;
+        }
+        else
+        {
+            PPARTITION_INFORMATION_EX partitionInfo;
+            partitionInfo = (PPARTITION_INFORMATION_EX)outputBuffer;
+            partitionInfo->PartitionStyle = PARTITION_STYLE_MBR;
+            partitionInfo->RewritePartition = FALSE;
+            partitionInfo->Mbr.RecognizedPartition = TRUE;
+            partitionInfo->Mbr.PartitionType = PARTITION_FAT32;
+            partitionInfo->Mbr.BootIndicator = FALSE;
+            partitionInfo->Mbr.HiddenSectors = 0;
+            partitionInfo->StartingOffset.QuadPart = 0;
+            partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
+            partitionInfo->PartitionNumber = 0;
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleGetDeviceNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_GET_DEVICE_NUMBER
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
+       sizeof(STORAGE_DEVICE_NUMBER))
+    {
+        PSTORAGE_DEVICE_NUMBER deviceNumber = NULL;
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &deviceNumber,
+                                                NULL);
+        if (NT_SUCCESS(status))
+        {
+            deviceNumber->DeviceType = DeviceExtension->DeviceObject->DeviceType;
+            deviceNumber->DeviceNumber = DeviceExtension->DeviceNumber;
+            deviceNumber->PartitionNumber = (ULONG)-1; // legacy reason, return (-1) for this IOCTL.
+
+            status = STATUS_SUCCESS;
+            *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
+        }
+    }
+    else
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleGetHotPlugInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_GET_HOTPLUG_INFO
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+
+    *DataLength = 0;
+
+    if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
+       sizeof(STORAGE_HOTPLUG_INFO))
+    {
+        PSTORAGE_HOTPLUG_INFO info = NULL;
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &info,
+                                                NULL);
+        if (NT_SUCCESS(status))
+        {
+            *info = DeviceExtension->PrivateFdoData->HotplugInfo;
+
+            status = STATUS_SUCCESS;
+            *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
+        }
+    }
+    else
+    {
+        status = STATUS_BUFFER_TOO_SMALL;
+        *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleSetHotPlugInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_SET_HOTPLUG_INFO
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PSTORAGE_HOTPLUG_INFO   info = NULL;
+
+    *DataLength = 0;
+
+    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+        sizeof(STORAGE_HOTPLUG_INFO))
+    {
+        // Indicate unsuccessful status and no data transferred.
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &info,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (info->Size != DeviceExtension->PrivateFdoData->HotplugInfo.Size)
+        {
+            status = STATUS_INVALID_PARAMETER_1;
+        }
+
+        if (info->MediaRemovable != DeviceExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
+        {
+            status = STATUS_INVALID_PARAMETER_2;
+        }
+
+        if (info->MediaHotplug != DeviceExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
+        {
+            status = STATUS_INVALID_PARAMETER_3;
+        }
+
+        if (info->WriteCacheEnableOverride != DeviceExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
+        {
+            status = STATUS_INVALID_PARAMETER_5;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
+
+        // Store the user-defined override in the registry
+        DeviceSetParameter(DeviceExtension,
+                           CLASSP_REG_SUBKEY_NAME,
+                           CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+                           (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleEventNotification(
+    _In_      PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST               Request,
+    _In_opt_  PWDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_     size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    This routine handles the process of IOCTL_STORAGE_EVENT_NOTIFICATION
+
+Arguments:
+
+    DeviceExtension - device context
+
+    Request - request to be handled
+
+    RequestParameters - request parameter
+
+    DataLength - data transferred
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                     status = STATUS_SUCCESS;
+    PMEDIA_CHANGE_DETECTION_INFO info = NULL;
+    LONG                         requestInUse;
+    PSTORAGE_EVENT_NOTIFICATION  eventBuffer = NULL;
+
+    *DataLength = 0;
+
+    info = DeviceExtension->MediaChangeDetectionInfo;
+
+    // Since AN is ASYNCHRONOUS and can happen at any time,
+    // make certain not to do anything before properly initialized.
+    if ((!DeviceExtension->IsInitialized) || (info == NULL))
+    {
+        status = STATUS_UNSUCCESSFUL;
+    }
+
+    if (NT_SUCCESS(status) && (Request != NULL) && (RequestParameters != NULL)) {
+
+        //
+        // Validate IOCTL parameters
+        //
+        if (RequestParameters->Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(STORAGE_EVENT_NOTIFICATION)) {
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+
+        //
+        // Check for an supported event
+        //
+        if (NT_SUCCESS(status)) {
+            status = WdfRequestRetrieveInputBuffer(Request,
+                                                   RequestParameters->Parameters.DeviceIoControl.InputBufferLength,
+                                                   &eventBuffer,
+                                                   NULL);
+            if (NT_SUCCESS(status)) {
+                if ((eventBuffer->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) ||
+                    (eventBuffer->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) {
+                    status = STATUS_INVALID_PARAMETER;
+                } else if ((eventBuffer->Events &
+                           (STORAGE_EVENT_MEDIA_STATUS | STORAGE_EVENT_DEVICE_STATUS | STORAGE_EVENT_DEVICE_OPERATION)) == 0) {
+                    status = STATUS_NOT_SUPPORTED;
+                }
+            }
+        }
+
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (info->MediaChangeDetectionDisableCount != 0)
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+                       "RequestHandleEventNotification: device %p has detection disabled \n",
+                       DeviceExtension->DeviceObject));
+            status = STATUS_UNSUCCESSFUL;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // if the request is not in use, mark it as such.
+        requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);
+
+        if (requestInUse != 0)
+        {
+            status = STATUS_UNSUCCESSFUL;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // The last MCN finished. ok to issue the new one.
+        RequestSetupMcnSyncIrp(DeviceExtension);
+
+        // The irp will go into KMDF framework and a request will be created there to represent it.
+        IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleEjectionControl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_MEDIA_REMOVAL
+                     IOCTL_STORAGE_EJECTION_CONTROL
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PPREVENT_MEDIA_REMOVAL  mediaRemoval = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+       sizeof(PREVENT_MEDIA_REMOVAL))
+    {
+        status = STATUS_INFO_LENGTH_MISMATCH;
+    }
+    else
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               &mediaRemoval,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = PerformEjectionControl(DeviceExtension,
+                                        Request,
+                                        ((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECTION_CONTROL)
+                                          ? SecureMediaLock
+                                          : SimpleMediaLock),
+                                        mediaRemoval->PreventMediaRemoval);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleEnableStreaming(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Handles an IOCTL_CDROM_ENABLE_STREAMING request
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Notes:
+
+    This IOCTL is serialized because it changes read/write
+    behavior and we want to make sure that all previous
+    reads/writes have been completed before we change the
+    behavior.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
+    PCDROM_PRIVATE_FDO_DATA     fdoData = DeviceExtension->PrivateFdoData;
+
+    WDFFILEOBJECT               fileObject = NULL;
+    PFILE_OBJECT_CONTEXT        fileObjectContext = NULL;
+
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_STREAMING_CONTROL    inputBuffer = NULL;
+
+    BOOLEAN                     enforceStreamingRead = FALSE;
+    BOOLEAN                     enforceStreamingWrite = FALSE;
+    BOOLEAN                     streamingReadSupported, streamingWriteSupported;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           sizeof(CDROM_STREAMING_CONTROL),
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        // get file object context
+        fileObject = WdfRequestGetFileObject(Request);
+        if (fileObject != NULL) {
+            fileObjectContext = FileObjectGetContext(fileObject);
+        }
+        NT_ASSERT(fileObjectContext != NULL);
+
+        if (fileObjectContext == NULL)
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestHandleEnableStreaming: cannot find file object context\n"));
+            status = STATUS_INVALID_HANDLE;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (inputBuffer->RequestType == CdromStreamingDisable)
+        {
+            enforceStreamingRead = FALSE;
+            enforceStreamingWrite = FALSE;
+        }
+        else if (inputBuffer->RequestType == CdromStreamingEnableForReadOnly)
+        {
+            enforceStreamingRead = TRUE;
+            enforceStreamingWrite = FALSE;
+        }
+        else if (inputBuffer->RequestType == CdromStreamingEnableForWriteOnly)
+        {
+            enforceStreamingRead = FALSE;
+            enforceStreamingWrite = TRUE;
+        }
+        else if (inputBuffer->RequestType == CdromStreamingEnableForReadWrite)
+        {
+            enforceStreamingRead = TRUE;
+            enforceStreamingWrite = TRUE;
+        }
+
+        streamingReadSupported = cdData->Mmc.StreamingReadSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
+        streamingWriteSupported = cdData->Mmc.StreamingWriteSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
+        if ((enforceStreamingRead && !streamingReadSupported) ||
+            (enforceStreamingWrite && !streamingWriteSupported))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                        "RequestHandleEnableStreaming: requested Streaming mode is not supported\n"));
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+        else
+        {
+            fileObjectContext->EnforceStreamingRead = enforceStreamingRead;
+            fileObjectContext->EnforceStreamingWrite = enforceStreamingWrite;
+        }
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleSendOpcInformation(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Handles an IOCTL_CDROM_SEND_OPC_INFORMATION request
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_SIMPLE_OPC_INFO  inputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           sizeof(CDROM_SIMPLE_OPC_INFO),
+                                           (PVOID*)&inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        CDB                         cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+
+        // we support only the simplest version for now
+        cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION;
+        cdb.SEND_OPC_INFORMATION.DoOpc = 1;
+        cdb.SEND_OPC_INFORMATION.Exclude0 = (inputBuffer->Exclude0 ? 1 : 0);
+        cdb.SEND_OPC_INFORMATION.Exclude1 = (inputBuffer->Exclude1 ? 1 : 0);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, 0, FALSE, &cdb, sizeof(cdb.SEND_OPC_INFORMATION));
+
+        // nothing to do after the command finishes
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetPerformance(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Handles an IOCTL_CDROM_GET_PERFORMANCE request
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PCDROM_PERFORMANCE_REQUEST  inputBuffer = NULL;
+    PVOID                       outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    // Retrieve pointers to input/output data. The size has been validated earlier
+    // in RequestValidateGetPerformance, so we do not check it again.
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               (PVOID*)&inputBuffer,
+                                               NULL);
+    }
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        USHORT                      descriptorSize = 0;
+        USHORT                      descriptorCount = 0;
+        size_t                      transferSize = 0;
+        CDB                         cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        // Set up the CDB
+        RtlZeroMemory(&cdb, sizeof(CDB));
+
+        if (inputBuffer->RequestType == CdromPerformanceRequest)
+        {
+            cdb.GET_PERFORMANCE.Type = 0;
+
+            // 10b is the only defined tolerance in MMCr6
+            cdb.GET_PERFORMANCE.Tolerance = 2;
+
+            switch (inputBuffer->Exceptions) {
+                case CdromNominalPerformance:
+                    cdb.GET_PERFORMANCE.Except = 0;
+                    descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
+                    break;
+                case CdromEntirePerformanceList:
+                    cdb.GET_PERFORMANCE.Except = 1;
+                    descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
+                    break;
+                case CdromPerformanceExceptionsOnly:
+                    cdb.GET_PERFORMANCE.Except = 2;
+                    descriptorSize = sizeof(CDROM_EXCEPTION_PERFORMANCE_DESCRIPTOR);
+                    break;
+            }
+
+            switch (inputBuffer->PerformanceType) {
+                case CdromReadPerformance:
+                    cdb.GET_PERFORMANCE.Write = 0; break;
+                case CdromWritePerformance:
+                    cdb.GET_PERFORMANCE.Write = 1; break;
+            }
+
+            REVERSE_BYTES(&cdb.GET_PERFORMANCE.StartingLBA, &inputBuffer->StaringLba);
+        }
+        else if (inputBuffer->RequestType == CdromWriteSpeedRequest)
+        {
+            cdb.GET_PERFORMANCE.Type = 3;
+            descriptorSize = sizeof(CDROM_WRITE_SPEED_DESCRIPTOR);
+        }
+
+        cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;
+
+        // calculate how many descriptors can fit into the output buffer
+        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
+                sizeof(CDROM_PERFORMANCE_HEADER) &&
+            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <=
+                MAXUSHORT &&
+            descriptorSize > 0)
+        {
+            descriptorCount = (USHORT)(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength - sizeof(CDROM_PERFORMANCE_HEADER));
+            descriptorCount /= descriptorSize;
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+
+        REVERSE_BYTES_SHORT(&cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors, &descriptorCount);
+
+        // Calculate transfer size. We round it up to meet adapter requirements.
+        // Extra bytes are discarded later, when we copy data to the output buffer.
+        transferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+        transferSize += DeviceExtension->AdapterDescriptor->AlignmentMask;
+        transferSize &= ~DeviceExtension->AdapterDescriptor->AlignmentMask;
+
+        if (NT_SUCCESS(status))
+        {
+            status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, sizeof(cdb.GET_PERFORMANCE), CDROM_GET_PERFORMANCE_TIMEOUT);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(CDROM_PERFORMANCE_HEADER))
+            {
+                *DataLength = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            else
+            {
+                *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                 DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
+                RtlCopyMemory(outputBuffer,
+                              DeviceExtension->ScratchContext.ScratchBuffer,
+                              *DataLength);
+            }
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleMcnSyncFakeIoctl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+    Handles an IOCTL_MCN_SYNC_FAKE_IOCTL request
+
+Arguments:
+
+    DeviceExtension - device context
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                        status = STATUS_SUCCESS;
+    PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
+    BOOLEAN                         shouldRetry = TRUE;
+    BOOLEAN                         requestSent = FALSE;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    //
+    // Try to acquire the media change event.  If we can't do it immediately
+    // then bail out and assume the caller will try again later.
+    //
+    while (shouldRetry)
+    {
+
+        status = RequestSetupMcnRequest(DeviceExtension,
+                                        info->Gesn.Supported);
+
+        if (!NT_SUCCESS(status))
+        {
+            shouldRetry = FALSE;
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            requestSent = RequestSendMcnRequest(DeviceExtension);
+
+            if (requestSent)
+            {
+                shouldRetry = RequestPostWorkMcnRequest(DeviceExtension);
+            }
+            else
+            {
+                shouldRetry = FALSE;
+            }
+        }
+    }
+
+    // If there were any media change notifications that were not delivered
+    // for some reason, make an attempt to do so at this time.
+    DeviceSendDelayedMediaChangeNotifications(DeviceExtension);
+
+    // Set the status and then complete the original request.
+    // The timer handler will be able to send the next request.
+    status = STATUS_SUCCESS;
+
+    return status;
+}
+
+BOOLEAN
+RequestIsRealtimeStreaming(
+    _In_  WDFREQUEST       Request,
+    _In_  BOOLEAN          IsReadRequest
+    )
+/*++
+
+Routine Description:
+
+    Checks whether a given read/write request should
+    be performed in Real-Time Streaming mode.
+
+Arguments:
+
+    Request - request to be checked
+    IsReadRequest - TRUE = read request; FALSE = write request
+
+Return Value:
+
+    TRUE  - a Real-Time Streaming operation has to be performed
+    FALSE - a normal (non-Streaming) operation has to be performed
+
+--*/
+{
+    BOOLEAN     useStreaming = FALSE;
+
+    if (!useStreaming) {
+        //
+        // Check if we're required to use Streaming via I/O Stack Location flags
+        //
+        UCHAR currentStackLocationFlags = 0;
+        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
+
+        useStreaming = TEST_FLAG(currentStackLocationFlags, SL_REALTIME_STREAM);
+    }
+
+    if (!useStreaming) {
+        //
+        // Check if we were previously requested to enforce Streaming for
+        // the file handle through which this request was sent.
+        //
+
+        WDFFILEOBJECT           fileObject;
+        PFILE_OBJECT_CONTEXT    fileObjectContext;
+
+        fileObject = WdfRequestGetFileObject(Request);
+
+        if (fileObject != NULL) {
+            fileObjectContext = FileObjectGetContext(fileObject);
+            NT_ASSERT(fileObjectContext != NULL);
+
+            if (IsReadRequest && fileObjectContext->EnforceStreamingRead)
+            {
+                useStreaming = TRUE;
+            }
+
+            if (!IsReadRequest && fileObjectContext->EnforceStreamingWrite)
+            {
+                useStreaming = TRUE;
+            }
+        }
+    }
+
+    return useStreaming;
+}
+
+
+NTSTATUS
+RequestValidateReadWrite(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
+    )
+/*++
+
+Routine Description:
+
+   Validate Read/Write request
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    BOOLEAN             isValid = TRUE;
+    LONGLONG            startingOffset = 0;
+    size_t              transferByteCount = 0;
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  currentStack = NULL;
+
+    irp = WdfRequestWdmGetIrp(Request);
+    currentStack = IoGetCurrentIrpStackLocation(irp);
+
+    if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
+        (currentStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
+        !TEST_FLAG(currentStack->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.
+
+        //set the status for volume verification.
+        status = STATUS_VERIFY_REQUIRED;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (PLAY_ACTIVE(DeviceExtension))
+        {
+            status = STATUS_DEVICE_BUSY;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // 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)))
+        {
+            // This request is not from the owner. We can't let the operation go.
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Access Denied! Device in exclusive mode.\n"));
+
+            status = STATUS_ACCESS_DENIED;
+        }
+    }
+
+    // Validate the request alignment.
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Type == WdfRequestTypeRead)
+        {
+            startingOffset = RequestParameters.Parameters.Read.DeviceOffset;
+            transferByteCount = RequestParameters.Parameters.Read.Length;
+        }
+        else
+        {
+            startingOffset = RequestParameters.Parameters.Write.DeviceOffset;
+            transferByteCount = RequestParameters.Parameters.Write.Length;
+        }
+
+        if (!DeviceExtension->DiskGeometry.BytesPerSector)
+        {
+            DeviceExtension->DiskGeometry.BytesPerSector = 2048;
+        }
+
+        if (!DeviceExtension->SectorShift)
+        {
+            DeviceExtension->SectorShift = 11;
+        }
+
+        // Perform some basic validation up front
+        if (TEST_FLAG(startingOffset, DeviceExtension->DiskGeometry.BytesPerSector - 1) ||
+            TEST_FLAG(transferByteCount,       DeviceExtension->DiskGeometry.BytesPerSector - 1))
+        {
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    // validate the request against the current mmc schema
+    if (NT_SUCCESS(status))
+    {
+        FEATURE_NUMBER schema = cdData->Mmc.ValidationSchema;
+
+        // We validate read requests according to the RandomWritable schema, except in the
+        // case of IncrementalStreamingWritable, wherein  the drive is responsible for all
+        // of the verification
+        if (RequestParameters.Type == WdfRequestTypeRead)
+        {
+            if (!cdData->Mmc.WriteAllowed)
+            {
+                // standard legacy validation of read irps
+                // if writing is not allowed on the media
+                schema =  FeatureRandomWritable;
+            }
+            else if (schema != FeatureIncrementalStreamingWritable)
+            {
+                // standard legacy validation of read irps
+                // if not using streaming writes on writable media
+                schema =  FeatureRandomWritable;
+            }
+        }
+
+        // Fail write requests to read-only media
+        if ((RequestParameters.Type == WdfRequestTypeWrite) &&
+            !(cdData->Mmc.WriteAllowed))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Write request to read-only media\n"));
+            isValid = FALSE;
+        }
+
+        if (isValid)
+        {
+            switch (schema)
+            {
+                case FeatureDefectManagement:
+                case FeatureRandomWritable:
+                    // Ensure that the request is within bounds for ROM drives.
+                    // Writer drives do not need to have bounds as outbounds request should not damage the drive.
+                    if(!cdData->Mmc.IsWriter)
+                    {
+                        if ((startingOffset >= DeviceExtension->PartitionLength.QuadPart) ||
+                            startingOffset < 0)
+                        {
+                            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
+                            isValid = FALSE;
+
+                        }
+                        else
+                        {
+                            ULONGLONG bytesRemaining = DeviceExtension->PartitionLength.QuadPart - startingOffset;
+
+                            if ((ULONGLONG)transferByteCount > bytesRemaining)
+                            {
+                                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
+                                isValid = FALSE;
+                            }
+                        }
+                    }
+                    break;
+
+                case FeatureRigidRestrictedOverwrite:
+                    // Ensure that the number of blocks is a multiple of the blocking size
+                    if (((transferByteCount >> DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
+                    {
+                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
+                                   "RequestValidateReadWrite: Number of blocks is not a multiple of the blocking size (%x)\n",
+                                   cdData->Mmc.Blocking));
+
+                        isValid = FALSE;
+                    }
+                    // Fall through
+                case FeatureRestrictedOverwrite:
+                    // Ensure that the request begins on a blocking boundary
+                    if ((Int64ShrlMod32(startingOffset, DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
+                    {
+                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
+                                   "RequestValidateReadWrite: Starting block is not a multiple of the blocking size (%x)\n",
+                                   cdData->Mmc.Blocking));
+
+                        isValid = FALSE;
+                    }
+                    break;
+
+                case FeatureIncrementalStreamingWritable:
+                    // Let the drive handle the verification
+                    break;
+
+                default:
+                    // Unknown schema. Fail the request
+                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
+                               "RequestValidateReadWrite: Unknown validation schema (%x)\n",
+                               schema));
+
+                    isValid = FALSE;
+                    break;
+            } //end of switch (schema)
+        } // end of if (isValid)
+
+        if (!isValid)
+        {
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        }
+    } // end of mmc schema validation
+
+    // validate that the Real-Time Streaming requests meet device capabilties
+    if (NT_SUCCESS(status))
+    {
+        // We do not check for FDO_HACK_NO_STREAMING in DeviceExtension->PrivateFdoData->HackFlags here,
+        // because we're going to hide device failures related to streaming reads/writes from the sender
+        // of the request. FDO_HACK_NO_STREAMING is going to be taken into account later during actual
+        // processing of the request.
+        if (RequestIsRealtimeStreaming(Request, RequestParameters.Type == WdfRequestTypeRead))
+        {
+            if (RequestParameters.Type == WdfRequestTypeRead && !cdData->Mmc.StreamingReadSupported)
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
+                           "RequestValidateReadWrite: Streaming reads are not supported.\n"));
+
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            if (RequestParameters.Type == WdfRequestTypeWrite && !cdData->Mmc.StreamingWriteSupported)
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
+                           "RequestValidateReadWrite: Streaming writes are not supported.\n"));
+
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+        }
+    }
+
+    return status;
+}
+
+NTSTATUS
+RequestHandleReadWrite(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
+    )
+/*++
+
+Routine Description:
+
+    Handle a read/write request
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+
+    size_t              transferByteCount = 0;
+    PIRP                irp = NULL;
+    PIO_STACK_LOCATION  currentStack = NULL;
+
+    PUCHAR              dataBuffer;
+
+
+    irp = WdfRequestWdmGetIrp(Request);
+    currentStack = IoGetCurrentIrpStackLocation(irp);
+    dataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);
+
+    if (NT_SUCCESS(status))
+    {
+        if (RequestParameters.Type == WdfRequestTypeRead)
+        {
+            transferByteCount = RequestParameters.Parameters.Read.Length;
+        }
+        else
+        {
+            transferByteCount = RequestParameters.Parameters.Write.Length;
+        }
+
+        if (transferByteCount == 0)
+        {
+            //  Several parts of the code turn 0 into 0xffffffff,
+            //  so don't process a zero-length request any further.
+            status = STATUS_SUCCESS;
+            RequestCompletion(DeviceExtension, Request, status, 0);
+            return status;
+        }
+
+        // Add partition byte offset to make starting byte relative to
+        // beginning of disk.
+        currentStack->Parameters.Read.ByteOffset.QuadPart += (DeviceExtension->StartingOffset.QuadPart);
+
+        //not very necessary as the starting offset for CD/DVD device is always 0.
+        if (RequestParameters.Type == WdfRequestTypeRead)
+        {
+            RequestParameters.Parameters.Read.DeviceOffset = currentStack->Parameters.Read.ByteOffset.QuadPart;
+        }
+        else
+        {
+            RequestParameters.Parameters.Write.DeviceOffset = currentStack->Parameters.Write.ByteOffset.QuadPart;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   entireXferLen = currentStack->Parameters.Read.Length;
+        ULONG   maxLength = 0;
+        ULONG   packetsCount = 0;
+
+        PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext;
+        PCDROM_REQUEST_CONTEXT            requestContext;
+        PCDROM_REQUEST_CONTEXT            originalRequestContext;
+
+        // get the count of packets we need to send.
+        if ((((ULONG_PTR)dataBuffer) & (PAGE_SIZE-1)) == 0)
+        {
+            maxLength = cdData->MaxPageAlignedTransferBytes;
+        }
+        else
+        {
+            maxLength = cdData->MaxUnalignedTransferBytes;
+        }
+
+        packetsCount = entireXferLen / maxLength;
+
+        if (entireXferLen % maxLength != 0)
+        {
+            packetsCount++;
+        }
+
+        originalRequestContext = RequestGetContext(Request);
+
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
+        requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
+
+        readWriteContext->PacketsCount = packetsCount;
+        readWriteContext->EntireXferLen = entireXferLen;
+        readWriteContext->MaxLength = maxLength;
+        readWriteContext->StartingOffset = currentStack->Parameters.Read.ByteOffset;
+        readWriteContext->DataBuffer = dataBuffer;
+        readWriteContext->TransferedBytes = 0;
+        readWriteContext->IsRead = (RequestParameters.Type == WdfRequestTypeRead);
+
+        requestContext->OriginalRequest = Request;
+        requestContext->DeviceExtension = DeviceExtension;
+
+        //
+        // Setup the READ/WRITE fields in the original request which is what
+        // we use to properly synchronize cancellation logic between the
+        // cancel callback and the timer routine.
+        //
+
+        originalRequestContext->ReadWriteIsCompleted = FALSE;
+        originalRequestContext->ReadWriteRetryInitialized = FALSE;
+        originalRequestContext->DeviceExtension = DeviceExtension;
+
+        status = ScratchBuffer_PerformNextReadWrite(DeviceExtension, TRUE);
+
+        // We do not call ScratchBuffer_EndUse here, because we're not releasing the scratch SRB.
+        // It will be released in the completion routine.
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleLoadEjectMedia(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_EJECT_MEDIA
+                     IOCTL_STORAGE_LOAD_MEDIA
+                     IOCTL_STORAGE_LOAD_MEDIA2
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    PZERO_POWER_ODD_INFO        zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        // Synchronize with ejection control and ejection cleanup code as
+        // well as other eject/load requests.
+        WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
+
+        if(DeviceExtension->ProtectedLockCount != 0)
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "RequestHandleLoadEjectMedia: call to eject protected locked "
+                                                                "device - failure\n"));
+            status = STATUS_DEVICE_BUSY;
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            SCSI_REQUEST_BLOCK  srb;
+            PCDB                cdb = (PCDB)srb.Cdb;
+
+            RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+            srb.CdbLength = 6;
+
+            cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+            cdb->START_STOP.LoadEject = 1;
+
+            if(RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECT_MEDIA)
+            {
+                cdb->START_STOP.Start = 0;
+
+                // We are sending down a soft eject, and in this case we should take an active ref
+                // if the command succeeds.
+                if ((zpoddInfo != NULL) &&
+                    (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
+                    (zpoddInfo->Load == 0))                                         // Drawer
+                {
+                    zpoddInfo->MonitorStartStopUnit = TRUE;
+                }
+            }
+            else
+            {
+                cdb->START_STOP.Start = 1;
+            }
+
+            status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                                &srb,
+                                                NULL,
+                                                0,
+                                                FALSE,
+                                                Request);
+
+            if (zpoddInfo != NULL)
+            {
+                zpoddInfo->MonitorStartStopUnit = FALSE;
+            }
+        }
+
+        WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleReserveRelease(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_RESERVE
+                     IOCTL_STORAGE_RELEASE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PSCSI_REQUEST_BLOCK srb = NULL;
+    PCDB                cdb = NULL;
+    ULONG               ioctlCode = 0;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+                         sizeof(SCSI_REQUEST_BLOCK) +
+                         (sizeof(ULONG_PTR) * 2),
+                         CDROM_TAG_SRB);
+
+    if (srb == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+        cdb = (PCDB)srb->Cdb;
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
+        {
+            srb->CdbLength = 10;
+            cdb->CDB10.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
+        }
+        else
+        {
+            srb->CdbLength = 6;
+            cdb->CDB6GENERIC.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
+        }
+
+        // Set timeout value.
+        srb->TimeOutValue = DeviceExtension->TimeOutValue;
+
+        // Send reserves as tagged requests.
+        if (ioctlCode == IOCTL_STORAGE_RESERVE)
+        {
+            SET_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+            srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+        }
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            srb,
+                                            NULL,
+                                            0,
+                                            FALSE,
+                                            Request);
+        // no data transfer.
+        *DataLength = 0;
+
+        FREE_POOL(srb);
+    }
+
+    return status;
+} // end RequestHandleReserveRelease()
+
+__inline
+BOOLEAN
+ValidPersistentReserveScope(
+    UCHAR Scope)
+{
+    switch (Scope) {
+    case RESERVATION_SCOPE_LU:
+    case RESERVATION_SCOPE_ELEMENT:
+
+        return TRUE;
+
+    default:
+
+        return FALSE;
+    }
+}
+
+__inline
+BOOLEAN
+ValidPersistentReserveType(
+    UCHAR Type)
+{
+    switch (Type) {
+    case RESERVATION_TYPE_WRITE_EXCLUSIVE:
+    case RESERVATION_TYPE_EXCLUSIVE:
+    case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS:
+    case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS:
+
+        return TRUE;
+
+    default:
+
+        return FALSE;
+    }
+}
+
+NTSTATUS
+RequestValidatePersistentReserve(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Validate request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
+                     IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+
+    PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&reserveCommand,
+                                           NULL);
+    if (NT_SUCCESS(status))
+    {
+        if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(PERSISTENT_RESERVE_COMMAND)) ||
+            (reserveCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND)))
+        {
+            *DataLength = 0;
+            status = STATUS_INFO_LENGTH_MISMATCH;
+        }
+        else if ((ULONG_PTR)reserveCommand & DeviceExtension->AdapterDescriptor->AlignmentMask)
+        {
+            // Check buffer alignment. Only an issue if another kernel mode component
+            // (not the I/O manager) allocates the buffer.
+            *DataLength = 0;
+            status = STATUS_INVALID_USER_BUFFER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
+        {
+            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < reserveCommand->PR_IN.AllocationLength)
+            {
+                *DataLength = 0;
+                status = STATUS_INVALID_PARAMETER;
+            }
+            else
+            {
+                switch (reserveCommand->PR_IN.ServiceAction)
+                {
+                case RESERVATION_ACTION_READ_KEYS:
+                    if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST))
+                    {
+                        *DataLength = 0;
+                        status = STATUS_INVALID_PARAMETER;
+                    }
+                    break;
+
+                case RESERVATION_ACTION_READ_RESERVATIONS:
+                    if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST))
+                    {
+                        *DataLength = 0;
+                        status = STATUS_INVALID_PARAMETER;
+                    }
+                    break;
+
+                default:
+                    *DataLength = 0;
+                    status = STATUS_INVALID_PARAMETER;
+                    break;
+                }
+            }
+        }
+        else // case of IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
+        {
+            // Verify ServiceAction, Scope, and Type
+            switch (reserveCommand->PR_OUT.ServiceAction)
+            {
+            case RESERVATION_ACTION_REGISTER:
+            case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING:
+            case RESERVATION_ACTION_CLEAR:
+                // Scope and type ignored.
+                break;
+
+            case RESERVATION_ACTION_RESERVE:
+            case RESERVATION_ACTION_RELEASE:
+            case RESERVATION_ACTION_PREEMPT:
+            case RESERVATION_ACTION_PREEMPT_ABORT:
+                if (!ValidPersistentReserveScope(reserveCommand->PR_OUT.Scope) ||
+                    !ValidPersistentReserveType(reserveCommand->PR_OUT.Type))
+                {
+                    *DataLength = 0;
+                    status = STATUS_INVALID_PARAMETER;
+                }
+                break;
+
+            default:
+                *DataLength = 0;
+                status = STATUS_INVALID_PARAMETER;
+                break;
+            }
+
+            // Check input buffer for PR Out.
+            // Caller must include the PR parameter list.
+            if (NT_SUCCESS(status))
+            {
+                if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
+                        (sizeof(PERSISTENT_RESERVE_COMMAND) + sizeof(PRO_PARAMETER_LIST))) ||
+                    (reserveCommand->Size < RequestParameters.Parameters.DeviceIoControl.InputBufferLength))
+                {
+                    *DataLength = 0;
+                    status = STATUS_INVALID_PARAMETER;
+                }
+            }
+        } // end of validation for IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
+    }
+
+    return status;
+} // end RequestValidatePersistentReserve()
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandlePersistentReserve(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
+                     IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
+    PSCSI_REQUEST_BLOCK srb = NULL;
+    PCDB                cdb = NULL;
+    BOOLEAN             writeToDevice;
+
+    PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&reserveCommand,
+                                           NULL);
+    if (NT_SUCCESS(status))
+    {
+        srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+                             sizeof(SCSI_REQUEST_BLOCK) +
+                             (sizeof(ULONG_PTR) * 2),
+                             CDROM_TAG_SRB);
+
+        if (srb == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+        else
+        {
+            cdb = (PCDB)srb->Cdb;
+            RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        size_t dataBufLen = 0;
+
+        // Fill in the CDB.
+        if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
+        {
+            cdb->PERSISTENT_RESERVE_IN.OperationCode    = SCSIOP_PERSISTENT_RESERVE_IN;
+            cdb->PERSISTENT_RESERVE_IN.ServiceAction    = reserveCommand->PR_IN.ServiceAction;
+
+            REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength),
+                                &(reserveCommand->PR_IN.AllocationLength));
+
+            dataBufLen = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+            writeToDevice = FALSE;
+        }
+        else
+        {
+            cdb->PERSISTENT_RESERVE_OUT.OperationCode   = SCSIOP_PERSISTENT_RESERVE_OUT;
+            cdb->PERSISTENT_RESERVE_OUT.ServiceAction   = reserveCommand->PR_OUT.ServiceAction;
+            cdb->PERSISTENT_RESERVE_OUT.Scope           = reserveCommand->PR_OUT.Scope;
+            cdb->PERSISTENT_RESERVE_OUT.Type            = reserveCommand->PR_OUT.Type;
+
+            cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST);
+
+            // Move the parameter list to the beginning of the data buffer (so it is aligned
+            // correctly and that the MDL describes it correctly).
+            RtlMoveMemory(reserveCommand,
+                          reserveCommand->PR_OUT.ParameterList,
+                          sizeof(PRO_PARAMETER_LIST));
+
+            dataBufLen = sizeof(PRO_PARAMETER_LIST);
+            writeToDevice = TRUE;
+        }
+
+        srb->CdbLength = 10;
+        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            srb,
+                                            reserveCommand,
+                                            (ULONG) dataBufLen,
+                                            writeToDevice,
+                                            Request);
+
+        FREE_POOL(srb);
+    }
+
+    return status;
+}
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleAreVolumesReady(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DISK_ARE_VOLUMES_READY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    BOOLEAN                 completeRequest = TRUE;
+    NTSTATUS                status = STATUS_SUCCESS;
+
+    UNREFERENCED_PARAMETER(RequestParameters);
+
+    *DataLength = 0;
+
+    if (DeviceExtension->IsVolumeOnlinePending == FALSE)
+    {
+        status = STATUS_SUCCESS;
+        goto Cleanup;
+    }
+
+    //
+    // Add to the volume ready queue. No worries about request cancellation,
+    // since KMDF will automatically handle that while request is in queue.
+    //
+    status = WdfRequestForwardToIoQueue(Request,
+                                        DeviceExtension->ManualVolumeReadyQueue
+                                        );
+
+    if(!NT_SUCCESS(status))
+    {
+        goto Cleanup;
+    }
+
+    status = STATUS_PENDING;
+    completeRequest = FALSE;
+
+Cleanup:
+
+    if (completeRequest)
+    {
+        RequestCompletion(DeviceExtension, Request, status, 0);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleVolumeOnline(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_VOLUME_ONLINE / IOCTL_VOLUME_POST_ONLINE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    WDFREQUEST              request;
+    PIRP                    irp = NULL;
+    PIO_STACK_LOCATION      nextStack = NULL;
+
+    UNREFERENCED_PARAMETER(RequestParameters);
+
+    *DataLength = 0;
+
+    DeviceExtension->IsVolumeOnlinePending = FALSE;
+
+    // Complete all parked volume ready requests.
+    for (;;)
+    {
+        status = WdfIoQueueRetrieveNextRequest(DeviceExtension->ManualVolumeReadyQueue,
+                                               &request);
+
+        if (!NT_SUCCESS(status))
+        {
+            break;
+        }
+
+        RequestCompletion(DeviceExtension, request, STATUS_SUCCESS, 0);
+    }
+
+    // Change the IOCTL code to IOCTL_DISK_VOLUMES_ARE_READY, and forward the request down,
+    // so that if the underlying port driver will also know that volume is ready.
+    WdfRequestFormatRequestUsingCurrentType(Request);
+    irp = WdfRequestWdmGetIrp(Request);
+    nextStack = IoGetNextIrpStackLocation(irp);
+
+    irp->AssociatedIrp.SystemBuffer = &DeviceExtension->DeviceNumber;
+    nextStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
+    nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof (DeviceExtension->DeviceNumber);
+    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_VOLUMES_ARE_READY;
+
+    // Send the request straight down (synchronously).
+    RequestSend(DeviceExtension,
+                Request,
+                DeviceExtension->IoTarget,
+                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
+                NULL);
+
+    return STATUS_SUCCESS;
+}
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceHandleRawRead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_RAW_READ
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    // Determine whether the drive is currently in raw or cooked mode,
+    // and which command to use to read the data.
+
+    NTSTATUS        status = STATUS_SUCCESS;
+    PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);
+    RAW_READ_INFO   rawReadInfo = {0};
+    PVOID           outputVirtAddr = NULL;
+    ULONG           startingSector;
+
+    PIRP                irp = WdfRequestWdmGetIrp(Request);
+    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(irp);
+
+    VOID*               outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
+        (currentIrpStack->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.
+        (VOID) MediaReadCapacity(DeviceExtension->Device);
+
+        *DataLength = 0;
+        //set the status for volume verification.
+        status = STATUS_VERIFY_REQUIRED;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        // Since this ioctl is METHOD_OUT_DIRECT, we need to copy away
+        // the input buffer before interpreting it.  This prevents a
+        // malicious app from messing with the input buffer while we
+        // are interpreting it. This is done in dispacth.
+        //
+        // Here, we are going to get the input buffer out of saved place.
+        rawReadInfo = *(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+
+        if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength > 0)
+        {
+            // Make sure that any user buffer that we pass down to
+            // the hardware is properly aligned
+            NT_ASSERT(irp->MdlAddress);
+            outputVirtAddr = MmGetMdlVirtualAddress(irp->MdlAddress);
+            if ((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask)
+            {
+                NT_ASSERT(!((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask));
+                *DataLength = 0;
+                status = STATUS_INVALID_PARAMETER;
+            }
+        }
+        else
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        switch (rawReadInfo.TrackMode)
+        {
+        case CDDA:
+        case YellowMode2:
+        case XAForm2:
+            // no check needed.
+            break;
+
+        case RawWithC2AndSubCode:
+            if (!cdData->Mmc.ReadCdC2Pointers || !cdData->Mmc.ReadCdSubCode)
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                          "Request to read C2 & Subcode rejected.  "
+                          "Is C2 supported: %d   Is Subcode supported: %d\n",
+                          cdData->Mmc.ReadCdC2Pointers,
+                          cdData->Mmc.ReadCdSubCode
+                          ));
+                *DataLength = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            break;
+        case RawWithC2:
+            if (!cdData->Mmc.ReadCdC2Pointers)
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                          "Request to read C2 rejected because drive does not "
+                          "report support for C2 pointers\n"
+                          ));
+                *DataLength = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            break;
+        case RawWithSubCode:
+
+            if (!cdData->Mmc.ReadCdSubCode)
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                          "Request to read subcode rejected because drive does "
+                          "not report support for reading the subcode data\n"
+                          ));
+                *DataLength = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            break;
+
+        default:
+            *DataLength = 0;
+            status = STATUS_INVALID_DEVICE_REQUEST;
+            break;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
+        PCDB                cdb = (PCDB)(srb->Cdb);
+
+        size_t              transferByteCount;
+        BOOLEAN             shouldRetry = TRUE;
+        ULONG               timesAlreadyRetried = 0;
+        LONGLONG            retryIn100nsUnits = 0;
+
+        transferByteCount = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        while (shouldRetry)
+        {
+            // Setup cdb depending upon the sector type we want.
+            switch (rawReadInfo.TrackMode)
+            {
+            case CDDA:
+                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
+                cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR;
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 3;
+                cdb->READ_CD.IncludeSyncData = 1;
+                break;
+
+            case YellowMode2:
+                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
+                cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR;
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 1;
+                cdb->READ_CD.IncludeSyncData = 1;
+                break;
+
+            case XAForm2:
+                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
+                cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR;
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 3;
+                cdb->READ_CD.IncludeSyncData = 1;
+                break;
+
+            case RawWithC2AndSubCode:
+                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
+                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
+                cdb->READ_CD.IncludeSyncData = 1;
+                cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
+                cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
+                break;
+
+            case RawWithC2:
+                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
+                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
+                cdb->READ_CD.IncludeSyncData = 1;
+                cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
+                break;
+
+            case RawWithSubCode:
+                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
+                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
+                cdb->READ_CD.IncludeUserData = 1;
+                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
+                cdb->READ_CD.IncludeSyncData = 1;
+                cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
+                break;
+
+            default:
+                // should already checked before coming in loop.
+                NT_ASSERT(FALSE);
+                break;
+            }
+
+            ScratchBuffer_SetupSrb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE);
+            // Restore the buffer, buffer size and MdlAddress. They got changed but we don't want to use the scratch buffer.
+            {
+                PIRP    scratchIrp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
+                scratchIrp->MdlAddress = irp->MdlAddress;
+                srb->DataBuffer = outputVirtAddr;
+                srb->DataTransferLength = (ULONG)transferByteCount;
+            }
+            // Calculate starting offset.
+            startingSector = (ULONG)(rawReadInfo.DiskOffset.QuadPart >> DeviceExtension->SectorShift);
+
+            // Fill in CDB fields.
+            srb->CdbLength = 12;
+
+            cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
+            cdb->READ_CD.TransferBlocks[2]  = (UCHAR) (rawReadInfo.SectorCount & 0xFF);
+            cdb->READ_CD.TransferBlocks[1]  = (UCHAR) (rawReadInfo.SectorCount >> 8 );
+            cdb->READ_CD.TransferBlocks[0]  = (UCHAR) (rawReadInfo.SectorCount >> 16);
+
+            cdb->READ_CD.StartingLBA[3]  = (UCHAR) (startingSector & 0xFF);
+            cdb->READ_CD.StartingLBA[2]  = (UCHAR) ((startingSector >>  8));
+            cdb->READ_CD.StartingLBA[1]  = (UCHAR) ((startingSector >> 16));
+            cdb->READ_CD.StartingLBA[0]  = (UCHAR) ((startingSector >> 24));
+
+            ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
+
+            if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
+                (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
+            {
+                shouldRetry = FALSE;
+                status = STATUS_CANCELLED;
+            }
+            else
+            {
+                shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
+                                                                        timesAlreadyRetried,
+                                                                        &status,
+                                                                        &retryIn100nsUnits);
+                if (shouldRetry)
+                {
+                    LARGE_INTEGER t;
+                    t.QuadPart = -retryIn100nsUnits;
+                    timesAlreadyRetried++;
+                    KeDelayExecutionThread(KernelMode, FALSE, &t);
+                    // keep items clean
+                    ScratchBuffer_ResetItems(DeviceExtension, FALSE);
+                }
+            }
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = srb->DataTransferLength;
+        }
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandlePlayAudioMsf(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_PLAY_AUDIO_MSF
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PCDROM_PLAY_AUDIO_MSF   inputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&inputBuffer,
+                                           NULL);
+
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;
+
+        cdb.PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
+        cdb.PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
+        cdb.PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
+
+        cdb.PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
+        cdb.PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
+        cdb.PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            PLAY_ACTIVE(DeviceExtension) = TRUE;
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadQChannel(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_READ_Q_CHANNEL
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    PVOID       inputBuffer = NULL;
+    PVOID       outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = ReadQChannel(DeviceExtension,
+                              Request,
+                              inputBuffer,
+                              RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                              outputBuffer,
+                              RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                              DataLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ReadQChannel(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   base function to handle request of IOCTL_CDROM_READ_Q_CHANNEL
+
+Arguments:
+
+    DeviceExtension - device context
+    OriginalRequest - original request to be handled
+    InputBuffer - input buffer
+    InputBufferLength - length of input buffer
+    OutputBuffer - output buffer
+    OutputBufferLength - length of output buffer
+
+Return Value:
+
+    NTSTATUS
+    DataLength - returned data length
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    ULONG                       transferByteCount = 0;
+    CDB                         cdb;
+    PCDROM_SUB_Q_DATA_FORMAT    inputBuffer = (PCDROM_SUB_Q_DATA_FORMAT)InputBuffer;
+    PSUB_Q_CHANNEL_DATA         userChannelData = (PSUB_Q_CHANNEL_DATA)OutputBuffer;
+
+    PAGED_CODE ();
+
+    UNREFERENCED_PARAMETER(InputBufferLength);
+
+    *DataLength = 0;
+
+    // Set size of channel data -- however, this is dependent on
+    // what information we are requesting (which Format)
+    switch( inputBuffer->Format )
+    {
+        case IOCTL_CDROM_CURRENT_POSITION:
+            transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
+            break;
+
+        case IOCTL_CDROM_MEDIA_CATALOG:
+            transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
+            break;
+
+        case IOCTL_CDROM_TRACK_ISRC:
+            transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
+            break;
+    }
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    // Always logical unit 0, but only use MSF addressing
+    // for IOCTL_CDROM_CURRENT_POSITION
+    if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
+    {
+        cdb.SUBCHANNEL.Msf = CDB_USE_MSF;
+    }
+
+    // Return subchannel data
+    cdb.SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;
+
+    // Specify format of informatin to return
+    cdb.SUBCHANNEL.Format = inputBuffer->Format;
+
+    // Specify which track to access (only used by Track ISRC reads)
+    if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC)
+    {
+        cdb.SUBCHANNEL.TrackNumber = inputBuffer->Track;
+    }
+
+    cdb.SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
+    cdb.SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
+    cdb.SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;
+
+    status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, transferByteCount, TRUE, &cdb, 10);
+
+    if (NT_SUCCESS(status))
+    {
+        PSUB_Q_CHANNEL_DATA subQPtr = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
+
+#if DBG
+        switch( inputBuffer->Format )
+        {
+        case IOCTL_CDROM_CURRENT_POSITION:
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
+            break;
+
+        case IOCTL_CDROM_MEDIA_CATALOG:
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
+            break;
+
+        case IOCTL_CDROM_TRACK_ISRC:
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
+            break;
+        }
+#endif
+
+        // Update the play active status.
+        if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
+        {
+            PLAY_ACTIVE(DeviceExtension) = TRUE;
+        }
+        else
+        {
+            PLAY_ACTIVE(DeviceExtension) = FALSE;
+        }
+
+        // Check if output buffer is large enough to contain
+        // the data.
+        if (OutputBufferLength < DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength)
+        {
+            DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength = (ULONG)OutputBufferLength;
+        }
+
+        // Copy our buffer into users.
+        RtlMoveMemory(userChannelData,
+                      subQPtr,
+                      DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
+
+        *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+    }
+
+    // nothing to do after the command finishes.
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandlePauseAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_PAUSE_AUDIO
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
+        cdb.PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            PLAY_ACTIVE(DeviceExtension) = FALSE;
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleResumeAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_RESUME_AUDIO
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
+        cdb.PAUSE_RESUME.Action = CDB_AUDIO_RESUME;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            PLAY_ACTIVE(DeviceExtension) = TRUE;  //not in original code. But we should set it.
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSeekAudioMsf(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_SEEK_AUDIO_MSF
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS              status = STATUS_SUCCESS;
+    PCDROM_SEEK_AUDIO_MSF inputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+        ULONG   logicalBlockAddress;
+
+        logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F);
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.SEEK.OperationCode      = SCSIOP_SEEK;
+        cdb.SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
+        cdb.SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
+        cdb.SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
+        cdb.SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleStopAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _Out_ size_t *                 DataLength
+    )
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = 0;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+        cdb.START_STOP.Immediate = 1;
+        cdb.START_STOP.Start = 0;
+        cdb.START_STOP.LoadEject = 0;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 6);
+
+        if (NT_SUCCESS(status))
+        {
+            PLAY_ACTIVE(DeviceExtension) = FALSE;
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleGetSetVolume(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_GET_VOLUME
+                     IOCTL_CDROM_SET_VOLUME
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS              status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG   transferSize = MODE_DATA_SIZE;
+        CDB     cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+        cdb.MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
+        cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
+        cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 10);
+
+        if (NT_SUCCESS(status))
+        {
+            if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)
+            {
+                PAUDIO_OUTPUT   audioOutput;
+                PVOLUME_CONTROL volumeControl;
+                ULONG           bytesTransferred;
+
+                status = WdfRequestRetrieveOutputBuffer(Request,
+                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                        (PVOID*)&volumeControl,
+                                                        NULL);
+                if (NT_SUCCESS(status))
+                {
+                    audioOutput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
+                                                            DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
+                                                            CDROM_AUDIO_CONTROL_PAGE,
+                                                            FALSE);
+
+                    // Verify the page is as big as expected.
+                    bytesTransferred = (ULONG)((PCHAR)audioOutput - (PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer) +
+                                        sizeof(AUDIO_OUTPUT);
+
+                    if ((audioOutput != NULL) &&
+                        (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength >= bytesTransferred))
+                    {
+                        ULONG i;
+                        for (i=0; i<4; i++)
+                        {
+                            volumeControl->PortVolume[i] = audioOutput->PortOutput[i].Volume;
+                        }
+
+                        // Set bytes transferred in IRP.
+                        *DataLength = sizeof(VOLUME_CONTROL);
+
+                    }
+                    else
+                    {
+                        *DataLength = 0;
+                        status = STATUS_INVALID_DEVICE_REQUEST;
+                    }
+                }
+            }
+            else    //IOCTL_CDROM_SET_VOLUME
+            {
+                PAUDIO_OUTPUT   audioInput = NULL;
+                PAUDIO_OUTPUT   audioOutput = NULL;
+                PVOLUME_CONTROL volumeControl = NULL;
+                ULONG           i,bytesTransferred,headerLength;
+
+                status = WdfRequestRetrieveInputBuffer(Request,
+                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                                       (PVOID*)&volumeControl,
+                                                       NULL);
+
+                if (NT_SUCCESS(status))
+                {
+                    audioInput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
+                                                           DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
+                                                           CDROM_AUDIO_CONTROL_PAGE,
+                                                           FALSE);
+
+                    // Check to make sure the mode sense data is valid before we go on
+                    if(audioInput == NULL)
+                    {
+                        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                                    "Mode Sense Page %d not found\n",
+                                    CDROM_AUDIO_CONTROL_PAGE));
+
+                        *DataLength = 0;
+                        status = STATUS_INVALID_DEVICE_REQUEST;
+                    }
+                }
+
+                if (NT_SUCCESS(status))
+                {
+                    // keep items clean; clear the command history
+                    ScratchBuffer_ResetItems(DeviceExtension, TRUE);
+
+                    headerLength = sizeof(MODE_PARAMETER_HEADER10);
+                    bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;
+
+                    // use the scratch buffer as input buffer.
+                    // the content of this buffer will not be changed in the following loop.
+                    audioOutput = (PAUDIO_OUTPUT)((PCHAR)DeviceExtension->ScratchContext.ScratchBuffer + headerLength);
+
+                    for (i=0; i<4; i++)
+                    {
+                        audioOutput->PortOutput[i].Volume = volumeControl->PortVolume[i];
+                        audioOutput->PortOutput[i].ChannelSelection = audioInput->PortOutput[i].ChannelSelection;
+                    }
+
+                    audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
+                    audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
+                    audioOutput->Immediate = MODE_SELECT_IMMEDIATE;
+
+                    RtlZeroMemory(&cdb, sizeof(CDB));
+                    // Set up the CDB
+                    cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
+                    cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8);
+                    cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF);
+                    cdb.MODE_SELECT10.PFBit = 1;
+
+                    status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, bytesTransferred, FALSE, &cdb, 10);
+
+                }
+                *DataLength = 0;
+            }
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadDvdStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DVD_READ_STRUCTURE
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    PVOID       inputBuffer = NULL;
+    PVOID       outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = ReadDvdStructure(DeviceExtension,
+                                  Request,
+                                  inputBuffer,
+                                  RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                  outputBuffer,
+                                  RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                  DataLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ReadDvdStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   base function to handle request of IOCTL_DVD_START_SESSION
+                                      IOCTL_DVD_READ_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    OriginalRequest - original request to be handled
+    InputBuffer - input buffer
+    InputBufferLength - length of input buffer
+    OutputBuffer - output buffer
+    OutputBufferLength - length of output buffer
+
+Return Value:
+
+    NTSTATUS
+    DataLength - returned data length
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PDVD_READ_STRUCTURE     request = (PDVD_READ_STRUCTURE)InputBuffer;
+    PDVD_DESCRIPTOR_HEADER  header = (PDVD_DESCRIPTOR_HEADER)OutputBuffer;
+    CDB                     cdb;
+
+    USHORT                  dataLength;
+    ULONG                   blockNumber;
+    PFOUR_BYTE              fourByte;
+
+    PAGED_CODE ();
+
+    UNREFERENCED_PARAMETER(InputBufferLength);
+
+    if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
+    {
+        *DataLength = 0;
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+    dataLength = (USHORT)OutputBufferLength;
+    blockNumber = (ULONG)(request->BlockByteOffset.QuadPart >> DeviceExtension->SectorShift);
+    fourByte = (PFOUR_BYTE)&blockNumber;
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3;
+    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2;
+    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1;
+    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0;
+    cdb.READ_DVD_STRUCTURE.LayerNumber   = request->LayerNumber;
+    cdb.READ_DVD_STRUCTURE.Format        = (UCHAR)request->Format;
+
+#if DBG
+    {
+        if ((UCHAR)request->Format > DvdMaxDescriptor)
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                        "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
+                        (UCHAR)request->Format,
+                        READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor],
+                        dataLength
+                        ));
+        }
+        else
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
+                        "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
+                        (UCHAR)request->Format,
+                        READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format],
+                        dataLength
+                        ));
+        }
+    }
+#endif // DBG
+
+    if (request->Format == DvdDiskKeyDescriptor)
+    {
+        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)request->SessionId;
+    }
+
+    cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8);
+    cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff);
+
+    status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, dataLength, TRUE, &cdb, 12);
+
+    if (NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                    "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", request->Format));
+
+        RtlMoveMemory(header,
+                      DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
+                      DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
+
+        // Cook the data.  There are a number of fields that really
+        // should be byte-swapped for the caller.
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                  "DvdDCCompletion - READ_STRUCTURE:\n"
+                  "\tHeader at %p\n"
+                  "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n"
+                  "\tDataBuffer was at %p\n"
+                  "\tDataTransferLength was %lx\n",
+                  header,
+                  header->Data,
+                  DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
+                  DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));
+
+        // First the fields in the header
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: header->Length %lx -> ",
+                   header->Length));
+
+        REVERSE_SHORT(&header->Length);
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", header->Length));
+
+        // Now the fields in the descriptor
+        if(request->Format == DvdPhysicalDescriptor)
+        {
+            ULONG tempLength = (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength > (ULONG)FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
+                                ? (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
+                                : 0;
+
+            PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR)&(header->Data[0]);
+
+            // Make sure the buffer size is good for swapping bytes.
+            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, StartingDataSector))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: StartingDataSector %lx -> ",
+                               layer->StartingDataSector));
+                REVERSE_LONG(&(layer->StartingDataSector));
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->StartingDataSector));
+            }
+            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndDataSector))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndDataSector %lx -> ",
+                               layer->EndDataSector));
+                REVERSE_LONG(&(layer->EndDataSector));
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndDataSector));
+            }
+            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndLayerZeroSector))
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndLayerZeroSector %lx -> ",
+                               layer->EndLayerZeroSector));
+                REVERSE_LONG(&(layer->EndLayerZeroSector));
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndLayerZeroSector));
+            }
+        }
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Status is %lx\n", status));
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDeviceControlCompletion - "
+                        "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n",
+                        DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));
+        }
+
+        *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+    }
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DVD_END_SESSION
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS        status = STATUS_SUCCESS;
+    PDVD_SESSION_ID sessionId = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&sessionId,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG           transferSize = 0;
+        CDB             cdb;
+        DVD_SESSION_ID  currentSession = 0;
+        DVD_SESSION_ID  limitSession = 0;
+
+        if(*sessionId == DVD_END_ALL_SESSIONS)
+        {
+            currentSession = 0;
+            limitSession = MAX_COPY_PROTECT_AGID - 1;
+        }
+        else
+        {
+            currentSession = *sessionId;
+            limitSession = *sessionId;
+        }
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        do
+        {
+            RtlZeroMemory(&cdb, sizeof(CDB));
+            // Set up the CDB
+            cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
+            cdb.SEND_KEY.AGID = (UCHAR)(currentSession);
+            cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
+
+            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
+
+            currentSession++;
+        } while ((currentSession <= limitSession) && NT_SUCCESS(status));
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdStartSessionReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DVD_START_SESSION
+                     IOCTL_DVD_READ_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
+    PVOID                   outputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRequestRetrieveOutputBuffer(Request,
+                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                                &outputBuffer,
+                                                NULL);
+    }
+
+    if (NT_SUCCESS(status) && RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY)
+    {
+        status = WdfRequestRetrieveInputBuffer(Request,
+                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                               (PVOID*)&keyParameters,
+                                               NULL);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = DvdStartSessionReadKey(DeviceExtension,
+                                        RequestParameters.Parameters.DeviceIoControl.IoControlCode,
+                                        Request,
+                                        keyParameters,
+                                        RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                        outputBuffer,
+                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
+                                        DataLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DvdStartSessionReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  ULONG                    IoControlCode,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_opt_  PVOID                InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _In_  PVOID                    OutputBuffer,
+    _In_  size_t                   OutputBufferLength,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   base function to handle request of IOCTL_DVD_START_SESSION
+                                      IOCTL_DVD_READ_KEY
+
+Arguments:
+
+    DeviceExtension - device context
+    IoControlCode - IOCTL_DVD_READ_KEY or IOCTL_DVD_START_SESSION
+    OriginalRequest - original request to be handled
+    InputBuffer - input buffer
+    InputBufferLength - length of input buffer
+    OutputBuffer - output buffer
+    OutputBufferLength - length of output buffer
+
+Return Value:
+
+    NTSTATUS
+    DataLength - returned data length
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    ULONG                   keyLength = 0;
+    ULONG                   result = 0;
+    ULONG                   allocationLength;
+    PFOUR_BYTE              fourByte;
+    PDVD_COPY_PROTECT_KEY   keyParameters = (PDVD_COPY_PROTECT_KEY)InputBuffer;
+
+    PAGED_CODE ();
+
+    UNREFERENCED_PARAMETER(InputBufferLength);
+
+    *DataLength = 0;
+
+    fourByte = (PFOUR_BYTE)&allocationLength;
+
+    if (IoControlCode == IOCTL_DVD_READ_KEY)
+    {
+        if (keyParameters == NULL)
+        {
+            status = STATUS_INTERNAL_ERROR;
+        }
+
+        // First of all, initialize the DVD region of the drive, if it has not been set yet
+        if (NT_SUCCESS(status) &&
+            (keyParameters->KeyType == DvdGetRpcKey) &&
+            DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
+        {
+            DevicePickDvdRegion(DeviceExtension->Device);
+        }
+
+        if (NT_SUCCESS(status) &&
+            (keyParameters->KeyType == DvdDiskKey))
+        {
+            // Special case - need to use READ DVD STRUCTURE command to get the disk key.
+            PDVD_COPY_PROTECT_KEY   keyHeader = NULL;
+            PDVD_READ_STRUCTURE     readStructureRequest = (PDVD_READ_STRUCTURE)keyParameters;
+
+            // save the key header so we can restore the interesting parts later
+            keyHeader = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                              sizeof(DVD_COPY_PROTECT_KEY),
+                                              DVD_TAG_READ_KEY);
+
+            if(keyHeader == NULL)
+            {
+                // Can't save the context so return an error
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                            "DvdDeviceControl - READ_KEY: unable to allocate context\n"));
+                status = STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                PREAD_DVD_STRUCTURES_HEADER rawKey = OutputBuffer;
+                PDVD_COPY_PROTECT_KEY       outputKey = OutputBuffer;
+
+                // save input parameters
+                RtlCopyMemory(keyHeader,
+                              InputBuffer,
+                              sizeof(DVD_COPY_PROTECT_KEY));
+
+                readStructureRequest->Format = DvdDiskKeyDescriptor;
+                readStructureRequest->BlockByteOffset.QuadPart = 0;
+                readStructureRequest->LayerNumber = 0;
+                readStructureRequest->SessionId = keyHeader->SessionId;
+
+                status = ReadDvdStructure(DeviceExtension,
+                                          OriginalRequest,
+                                          InputBuffer,
+                                          sizeof(DVD_READ_STRUCTURE),
+                                          OutputBuffer,
+                                          sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR),
+                                          DataLength);
+
+                // fill the output buffer, it's not touched in DeviceHandleReadDvdStructure()
+                // for this specific request type: DvdDiskKeyDescriptor
+                if (NT_SUCCESS(status))
+                {
+                    // Shift the data down to its new position.
+                    RtlMoveMemory(outputKey->KeyData,
+                                  rawKey->Data,
+                                  sizeof(DVD_DISK_KEY_DESCRIPTOR));
+
+                    RtlCopyMemory(outputKey,
+                                  keyHeader,
+                                  sizeof(DVD_COPY_PROTECT_KEY));
+
+                    outputKey->KeyLength = DVD_DISK_KEY_LENGTH;
+
+                    *DataLength = DVD_DISK_KEY_LENGTH;
+                }
+                else
+                {
+                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+                                "StartSessionReadKey Failed with status %x, %xI64 (%x) bytes\n",
+                                status,
+                                (unsigned int)*DataLength,
+                                ((rawKey->Length[0] << 16) | rawKey->Length[1]) ));
+                }
+
+                FREE_POOL(keyHeader);
+            }
+
+            // special process finished. return from here.
+            return status;
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            status = RtlULongSub((ULONG)OutputBufferLength,
+                                 (ULONG)sizeof(DVD_COPY_PROTECT_KEY), &result);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            //The data length field of REPORT KEY Command occupies two bytes
+            keyLength = min(keyLength, MAXUSHORT);
+        }
+    }
+    else    //IOCTL_DVD_START_SESSION
+    {
+        keyParameters = NULL;
+        keyLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA);
+        status = STATUS_SUCCESS;
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        CDB cdb;
+
+        allocationLength = keyLength;
+
+        // Defensive coding. Prefix cannot recognize this usage.
+        UNREFERENCED_PARAMETER(allocationLength);
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+        cdb.REPORT_KEY.AllocationLength[0] = fourByte->Byte1;
+        cdb.REPORT_KEY.AllocationLength[1] = fourByte->Byte0;
+
+        // set the specific parameters....
+        if(IoControlCode == IOCTL_DVD_READ_KEY)
+        {
+            if(keyParameters->KeyType == DvdTitleKey)
+            {
+                ULONG logicalBlockAddress;
+
+                logicalBlockAddress = (ULONG)(keyParameters->Parameters.TitleOffset.QuadPart >>
+                                              DeviceExtension->SectorShift);
+
+                fourByte = (PFOUR_BYTE)&(logicalBlockAddress);
+
+                cdb.REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3;
+                cdb.REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2;
+                cdb.REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1;
+                cdb.REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0;
+            }
+
+            cdb.REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType;
+            cdb.REPORT_KEY.AGID = (UCHAR)keyParameters->SessionId;
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "DvdStartSessionReadKey => sending irp %p (%s)\n",
+                        OriginalRequest, "READ_KEY"));
+        }
+        else
+        {
+            cdb.REPORT_KEY.KeyFormat = DVD_REPORT_AGID;
+            cdb.REPORT_KEY.AGID = 0;
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                        "DvdStartSessionReadKey => sending irp %p (%s)\n",
+                        OriginalRequest, "START_SESSION"));
+        }
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, TRUE, &cdb, 12);
+
+        if (NT_SUCCESS(status))
+        {
+            if(IoControlCode == IOCTL_DVD_READ_KEY)
+            {
+                NTSTATUS                tempStatus;
+                PDVD_COPY_PROTECT_KEY   copyProtectKey = (PDVD_COPY_PROTECT_KEY)OutputBuffer;
+                PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
+                ULONG                   dataLength;
+                ULONG                   transferLength;
+
+                tempStatus = RtlULongSub(DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
+                                         FIELD_OFFSET(CDVD_KEY_HEADER, Data),
+                                         &transferLength);
+
+                dataLength = (keyHeader->DataLength[0] << 8) + keyHeader->DataLength[1];
+
+                if (NT_SUCCESS(tempStatus) && (dataLength >= 2))
+                {
+                    // Adjust the data length to ignore the two reserved bytes in the
+                    // header.
+                    dataLength -= 2;
+
+                    // take the minimum of the transferred length and the
+                    // length as specified in the header.
+                    if(dataLength < transferLength)
+                    {
+                        transferLength = dataLength;
+                    }
+
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+                                "DvdDeviceControlCompletion: [%p] - READ_KEY with "
+                                "transfer length of (%d or %d) bytes\n",
+                                OriginalRequest,
+                                dataLength,
+                                DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - 2));
+
+                    // Copy the key data into the return buffer
+                    if(copyProtectKey->KeyType == DvdTitleKey)
+                    {
+                        RtlMoveMemory(copyProtectKey->KeyData,
+                                      keyHeader->Data + 1,
+                                      transferLength - 1);
+
+                        copyProtectKey->KeyData[transferLength - 1] = 0;
+
+                        // If this is a title key then we need to copy the CGMS flags
+                        // as well.
+                        copyProtectKey->KeyFlags = *(keyHeader->Data);
+
+                    }
+                    else
+                    {
+                        RtlMoveMemory(copyProtectKey->KeyData,
+                                      keyHeader->Data,
+                                      transferLength);
+                    }
+
+                    copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY);
+                    copyProtectKey->KeyLength += transferLength;
+
+                    *DataLength = copyProtectKey->KeyLength;
+                }
+                else
+                {
+                    //There is no valid data from drive.
+                    //This may happen when Key Format = 0x3f that does not require data back from drive.
+                    status = STATUS_SUCCESS;
+                    *DataLength = 0;
+                }
+            }
+            else
+            {
+                PDVD_SESSION_ID         sessionId = (PDVD_SESSION_ID)OutputBuffer;
+                PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
+                PCDVD_REPORT_AGID_DATA  keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data;
+
+                *sessionId = keyData->AGID;
+
+                *DataLength = sizeof(DVD_SESSION_ID);
+            }
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_DVD_SEND_KEY
+                     IOCTL_DVD_SEND_KEY2
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PVOID                   inputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           &inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        status = DvdSendKey(DeviceExtension,
+                            Request,
+                            inputBuffer,
+                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                            DataLength);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_opt_  WDFREQUEST           OriginalRequest,
+    _In_  PVOID                    InputBuffer,
+    _In_  size_t                   InputBufferLength,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   base function to handle request of IOCTL_DVD_SEND_KEY(2)
+   NOTE: cdrom does not process this IOCTL if the input buffer length is bigger than Port transfer length.
+
+Arguments:
+
+    DeviceExtension - device context
+    OriginalRequest - original request to be handled
+    InputBuffer - input buffer
+    InputBufferLength - length of input buffer
+
+Return Value:
+
+    NTSTATUS
+    DataLength - returned data length
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PDVD_COPY_PROTECT_KEY   key = (PDVD_COPY_PROTECT_KEY)InputBuffer;
+
+    ULONG                   keyLength = 0;
+    ULONG                   result = 0;
+    PFOUR_BYTE              fourByte;
+
+    PAGED_CODE ();
+
+    UNREFERENCED_PARAMETER(InputBufferLength);
+
+    *DataLength = 0;
+
+    if (NT_SUCCESS(status))
+    {
+        if ((key->KeyLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
+            ((key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) > DeviceExtension->ScratchContext.ScratchBufferSize))
+        {
+            NT_ASSERT(FALSE);
+            status = STATUS_INTERNAL_ERROR;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = RtlULongSub(key->KeyLength, sizeof(DVD_COPY_PROTECT_KEY), &result);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        keyLength = min(keyLength, DeviceExtension->ScratchContext.ScratchBufferSize);
+
+        if (keyLength < 2)
+        {
+            status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (NT_SUCCESS(status))
+    {
+        PCDVD_KEY_HEADER            keyBuffer = NULL;
+        CDB                         cdb;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        // prepare the input buffer
+        keyBuffer = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer;
+
+        // keylength is decremented here by two because the
+        // datalength does not include the header, which is two
+        // bytes.  keylength is immediately incremented later
+        // by the same amount.
+        keyLength -= 2;
+        fourByte = (PFOUR_BYTE)&keyLength;
+        keyBuffer->DataLength[0] = fourByte->Byte1;
+        keyBuffer->DataLength[1] = fourByte->Byte0;
+        keyLength += 2;
+
+        RtlMoveMemory(keyBuffer->Data,
+                      key->KeyData,
+                      key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY));
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.REPORT_KEY.OperationCode = SCSIOP_SEND_KEY;
+
+        cdb.SEND_KEY.ParameterListLength[0] = fourByte->Byte1;
+        cdb.SEND_KEY.ParameterListLength[1] = fourByte->Byte0;
+        cdb.SEND_KEY.KeyFormat = (UCHAR)key->KeyType;
+        cdb.SEND_KEY.AGID = (UCHAR)key->SessionId;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, FALSE, &cdb, 12);
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSetReadAhead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_STORAGE_SET_READ_AHEAD
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PSTORAGE_SET_READ_AHEAD readAhead = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&readAhead,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG       transferSize = 0;
+        CDB         cdb;
+        ULONG       blockAddress;
+        PFOUR_BYTE  fourByte = (PFOUR_BYTE)&blockAddress;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        cdb.SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD;
+
+        blockAddress = (ULONG)(readAhead->TriggerAddress.QuadPart >>
+                               DeviceExtension->SectorShift);
+
+        // Defensive coding. Prefix cannot recognize this usage.
+        UNREFERENCED_PARAMETER(blockAddress);
+
+        cdb.SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3;
+        cdb.SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2;
+        cdb.SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1;
+        cdb.SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0;
+
+        blockAddress = (ULONG)(readAhead->TargetAddress.QuadPart >>
+                               DeviceExtension->SectorShift);
+
+        cdb.SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3;
+        cdb.SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2;
+        cdb.SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1;
+        cdb.SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0;
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
+
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSetSpeed(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_  WDFREQUEST               Request,
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    )
+/*++
+
+Routine Description:
+
+   Handle request of IOCTL_CDROM_SET_SPEED
+
+Arguments:
+
+    DeviceExtension - device context
+    Request - request to be handled
+    RequestParameters - request parameter
+    DataLength - transferred data length
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
+    PCDROM_SET_SPEED    inputBuffer = NULL;
+
+    PAGED_CODE ();
+
+    *DataLength = 0;
+
+    status = WdfRequestRetrieveInputBuffer(Request,
+                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
+                                           (PVOID*)&inputBuffer,
+                                           NULL);
+
+    if (NT_SUCCESS(status))
+    {
+        ULONG               transferSize = 0;
+        CDB                 cdb;
+        CDROM_SPEED_REQUEST requestType = inputBuffer->RequestType;
+
+        ScratchBuffer_BeginUse(DeviceExtension);
+
+        RtlZeroMemory(&cdb, sizeof(CDB));
+        // Set up the CDB
+        if (requestType == CdromSetSpeed)
+        {
+            PCDROM_SET_SPEED speed = inputBuffer;
+
+            cdb.SET_CD_SPEED.OperationCode = SCSIOP_SET_CD_SPEED;
+            cdb.SET_CD_SPEED.RotationControl = speed->RotationControl;
+            REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.ReadSpeed, &speed->ReadSpeed);
+            REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.WriteSpeed, &speed->WriteSpeed);
+        }
+        else
+        {
+            PCDROM_SET_STREAMING    stream = (PCDROM_SET_STREAMING)inputBuffer;
+            PPERFORMANCE_DESCRIPTOR perfDescriptor;
+
+            transferSize = sizeof(PERFORMANCE_DESCRIPTOR);
+
+            perfDescriptor = DeviceExtension->ScratchContext.ScratchBuffer;
+            RtlZeroMemory(perfDescriptor, transferSize);
+
+            perfDescriptor->RandomAccess = stream->RandomAccess;
+            perfDescriptor->Exact = stream->SetExact;
+            perfDescriptor->RestoreDefaults = stream->RestoreDefaults;
+            perfDescriptor->WriteRotationControl = stream->RotationControl;
+
+            REVERSE_BYTES(&perfDescriptor->StartLba,  &stream->StartLba);
+            REVERSE_BYTES(&perfDescriptor->EndLba,    &stream->EndLba);
+            REVERSE_BYTES(&perfDescriptor->ReadSize,  &stream->ReadSize);
+            REVERSE_BYTES(&perfDescriptor->ReadTime,  &stream->ReadTime);
+            REVERSE_BYTES(&perfDescriptor->WriteSize, &stream->WriteSize);
+            REVERSE_BYTES(&perfDescriptor->WriteTime, &stream->WriteTime);
+
+            cdb.SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
+            REVERSE_BYTES_SHORT(&cdb.SET_STREAMING.ParameterListLength, &transferSize);
+
+            // set value in extension by user inputs.
+            cdData->RestoreDefaults = stream->Persistent ? FALSE : TRUE;
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceHandleSetSpeed: Restore default speed on media change set to %s\n",
+                       cdData->RestoreDefaults ? "true" : "false"));
+        }
+
+        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
+
+        if (NT_SUCCESS(status))
+        {
+            *DataLength = 0;
+        }
+
+        // nothing to do after the command finishes.
+        ScratchBuffer_EndUse(DeviceExtension);
+    }
+
+    return status;
+}
+
+
diff --git a/drivers/storage/class/cdrom_new/ioctl.h b/drivers/storage/class/cdrom_new/ioctl.h
new file mode 100644 (file)
index 0000000..951561a
--- /dev/null
@@ -0,0 +1,847 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    ioctl.h
+
+Abstract:
+
+    Functions to handle IOCTLs.
+
+Author:
+
+Environment:
+
+    kernel mode only 
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __IOCTL_H__
+#define __IOCTL_H__
+
+BOOLEAN
+RequestDispatchProcessDirectly(
+    _In_ WDFDEVICE              Device, 
+    _In_ WDFREQUEST             Request, 
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    );
+
+BOOLEAN
+RequestDispatchToSequentialQueue(
+    _In_ WDFDEVICE              Device, 
+    _In_ WDFREQUEST             Request, 
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    );
+
+BOOLEAN
+RequestDispatchSyncWithSequentialQueue(
+    _In_ WDFDEVICE              Device, 
+    _In_ WDFREQUEST             Request, 
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    );
+
+BOOLEAN
+RequestDispatchSpecialIoctls(
+    _In_ WDFDEVICE              Device, 
+    _In_ WDFREQUEST             Request, 
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    );
+
+BOOLEAN
+RequestDispatchUnknownRequests(
+    _In_ WDFDEVICE              Device, 
+    _In_ WDFREQUEST             Request, 
+    _In_ WDF_REQUEST_PARAMETERS RequestParameters
+    );
+
+//
+//  I/O Request Handlers
+//
+
+// Handlers that are called directly in dispatch routine.
+
+NTSTATUS
+RequestHandleGetInquiryData(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleGetMediaTypeEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleMountQueryUniqueId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleMountQueryDeviceName(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleMountQuerySuggestedLinkName(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleGetDeviceNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleGetHotPlugInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleSetHotPlugInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleEventNotification(
+    _In_      PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_opt_  WDFREQUEST               Request, 
+    _In_opt_  PWDF_REQUEST_PARAMETERS  RequestParameters,
+    _Out_     size_t *                 DataLength
+    );
+
+// Handlers that are called in RequestProcessSerializedIoctl in a work item.
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetDvdRegion(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestHandleQueryPropertyRetrieveCachedData(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+                            
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleReadTOC(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleReadTocEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+                            
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetConfiguration(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleGetDriveGeometry(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleDiskVerify(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleCheckVerify(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleFakePartitionInfo(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleEjectionControl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleLoadEjectMedia(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleMcnControl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandleReserveRelease(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+RequestHandlePersistentReserve(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleAreVolumesReady(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleVolumeOnline(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceHandleRawRead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandlePlayAudioMsf(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadQChannel(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandlePauseAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleResumeAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSeekAudioMsf(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleStopAudio(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleGetSetVolume(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadDvdStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdStartSessionReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleDvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSetReadAhead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSetSpeed(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadMediaKeyBlock(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsSendCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGetCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGetChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleSendChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleReadVolumeId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadSerialNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadMediaId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsReadBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+DeviceHandleAacsGenerateBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleEnableStreaming(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleSendOpcInformation(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleGetPerformance(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleMcnSyncFakeIoctl(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _Out_ size_t *                 DataLength
+    );
+
+// Handlers that will be called by Sync process.
+
+// RequestHandleUnknownIoctl could be called at DISPATCH_LEVEL.
+NTSTATUS
+RequestHandleUnknownIoctl(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessQueryLockState(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessLockDevice(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleExclusiveAccessUnlockDevice(
+    _In_ WDFDEVICE    Device,
+    _In_ WDFREQUEST   Request
+    );
+
+NTSTATUS
+RequestHandleQueryPropertyDeviceUniqueId(
+    _In_ WDFDEVICE    Device, 
+    _In_ WDFREQUEST   Request
+    );
+
+NTSTATUS
+RequestHandleQueryPropertyWriteCache(
+    _In_ WDFDEVICE    Device, 
+    _In_ WDFREQUEST   Request
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+RequestHandleScsiPassThrough(
+    _In_ WDFDEVICE    Device, 
+    _In_ WDFREQUEST   Request
+    );
+
+// Read/write handler called possibly at DISPATCH_LEVEL.
+
+NTSTATUS
+RequestHandleReadWrite(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
+    );
+
+//
+//  I/O Request Validation helpers
+//
+
+NTSTATUS
+RequestValidateRawRead(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateReadTocEx(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateReadToc(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateGetLastSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateReadQChannel(
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateDvdReadStructure(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateDvdStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateDvdSendKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateDvdReadKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateGetConfiguration(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateSetSpeed(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsReadMediaKeyBlock(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsStartSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsSendCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsGetCertificate(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsGetChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsSendChallengeKey(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsReadVolumeId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsReadSerialNumber(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsReadMediaId(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsBindingNonce(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateExclusiveAccess(
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateEnableStreaming(
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateSendOpcInformation(
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateGetPerformance(
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+BOOLEAN
+RequestIsRealtimeStreaming(
+    _In_  WDFREQUEST               Request,
+    _In_  BOOLEAN                  IsReadRequest
+    );
+
+NTSTATUS
+RequestValidateReadWrite(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
+    );
+
+NTSTATUS
+RequestValidatePersistentReserve(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateDvdEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+NTSTATUS
+RequestValidateAacsEndSession(
+    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
+    _In_  WDFREQUEST               Request, 
+    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
+    _Out_ size_t *                 DataLength
+    );
+
+
+//
+// completion routines
+//
+
+
+
+
+#endif // __IOCTL_H__
diff --git a/drivers/storage/class/cdrom_new/license.txt b/drivers/storage/class/cdrom_new/license.txt
new file mode 100644 (file)
index 0000000..6ef3ee3
--- /dev/null
@@ -0,0 +1,23 @@
+The Microsoft Public License (MS-PL)
+Copyright (c) 2015 Microsoft
+
+This license governs use of the accompanying software. If you use the software, you
+ accept this license. If you do not accept the license, do not use the software.
+
+1. Definitions
+ The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
+ same meaning here as under U.S. copyright law.
+ A "contribution" is the original software, or any additions or changes to the software.
+ A "contributor" is any person that distributes its contribution under this license.
+ "Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+ (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+ (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+ (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+ (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+ (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+ (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+ (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
diff --git a/drivers/storage/class/cdrom_new/localwpp.ini b/drivers/storage/class/cdrom_new/localwpp.ini
new file mode 100644 (file)
index 0000000..0e79360
--- /dev/null
@@ -0,0 +1,17 @@
+
+// this defines how to log a len/buffer pair.
+// This function should be in trace.h
+DEFINE_CPLX_TYPE(HEXDUMP, WPP_LOGHEXDUMP, xstr_t, ItemHEXDump,"s", _HEX_, 0,2);
+
+// DEFINE_CPLX_TYPE(
+// name,             // i.e. HEXDUMP         // %!HEXDUMP!
+// macro,            // i.e. WPP_LOGHEXDUMP  // Marshalling macro, defined in trace.h
+// structure,        // i.e. xstr_t          // Argument type (structure to be created by above macro)
+// item type,        // i.e. ItemHEXDump     // MOF type that TracePrt can understand
+// format specifier, // i.e. "s"             // a format specifier that TracePrt can understand
+// ????              // i.e. _HEX_           // Type signature (becomes a part of function name)
+// ????              // i.e. 0               // Weight (0 is variable data length)
+// ????              // i.e. 2               // Slots used by this entry (optional, 1 default)
+// )
+   
+
diff --git a/drivers/storage/class/cdrom_new/mmc.c b/drivers/storage/class/cdrom_new/mmc.c
new file mode 100644 (file)
index 0000000..3887cc2
--- /dev/null
@@ -0,0 +1,1629 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    mmc.c
+
+Abstract:
+
+    Include all funtions relate to MMC 
+
+Environment:
+
+    kernel mode only 
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "mmc.h"
+#include "scratch.h"
+
+#ifdef DEBUG_USE_WPP
+#include "mmc.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceDeallocateMmcResources)
+#pragma alloc_text(PAGE, DeviceAllocateMmcResources)
+#pragma alloc_text(PAGE, DeviceUpdateMmcCapabilities)
+#pragma alloc_text(PAGE, DeviceGetConfigurationWithAlloc)
+#pragma alloc_text(PAGE, DeviceGetConfiguration)
+#pragma alloc_text(PAGE, DeviceUpdateMmcWriteCapability)
+#pragma alloc_text(PAGE, MmcDataFindFeaturePage)
+#pragma alloc_text(PAGE, MmcDataFindProfileInProfiles)
+#pragma alloc_text(PAGE, DeviceRetryTimeGuessBasedOnProfile)
+#pragma alloc_text(PAGE, DeviceRetryTimeDetectionBasedOnModePage2A)
+#pragma alloc_text(PAGE, DeviceRetryTimeDetectionBasedOnGetPerformance)
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceDeallocateMmcResources(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+   release MMC resources
+
+Arguments:
+
+    Device - device object
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA             cddata = &(deviceExtension->DeviceAdditionalData);
+    PCDROM_MMC_EXTENSION    mmcData = &cddata->Mmc;
+
+    PAGED_CODE();
+
+    if (mmcData->CapabilitiesIrp) 
+    {
+        IoFreeIrp(mmcData->CapabilitiesIrp);
+        mmcData->CapabilitiesIrp = NULL;
+    }
+    if (mmcData->CapabilitiesMdl) 
+    {
+        IoFreeMdl(mmcData->CapabilitiesMdl);
+        mmcData->CapabilitiesMdl = NULL;
+    }
+    if (mmcData->CapabilitiesBuffer) 
+    {
+        ExFreePool(mmcData->CapabilitiesBuffer);
+        mmcData->CapabilitiesBuffer = NULL;
+    }
+    if (mmcData->CapabilitiesRequest) 
+    {
+        WdfObjectDelete(mmcData->CapabilitiesRequest);
+        mmcData->CapabilitiesRequest = NULL;
+    }
+    mmcData->CapabilitiesBufferSize = 0;
+    mmcData->IsMmc = FALSE;
+    mmcData->WriteAllowed = FALSE;
+
+    return;
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceAllocateMmcResources(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+   allocate all MMC resources needed
+
+Arguments:
+
+    Device - device object
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS status                         = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA cddata                      = &(deviceExtension->DeviceAdditionalData);
+    PCDROM_MMC_EXTENSION mmcData            = &(cddata->Mmc);
+    WDF_OBJECT_ATTRIBUTES attributes        = {0};
+
+    PAGED_CODE();
+
+    NT_ASSERT(mmcData->CapabilitiesBuffer == NULL);
+    NT_ASSERT(mmcData->CapabilitiesBufferSize == 0);
+
+    // allocate the buffer and set the buffer size. 
+    // retrieve drive configuration information.
+    status = DeviceGetConfigurationWithAlloc(Device,
+                                             &mmcData->CapabilitiesBuffer,
+                                             &mmcData->CapabilitiesBufferSize,
+                                             FeatureProfileList,
+                                             SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+    if (!NT_SUCCESS(status)) 
+    {
+        NT_ASSERT(mmcData->CapabilitiesBuffer     == NULL);
+        NT_ASSERT(mmcData->CapabilitiesBufferSize == 0);
+        return status;
+    }
+
+    NT_ASSERT(mmcData->CapabilitiesBuffer     != NULL);
+    NT_ASSERT(mmcData->CapabilitiesBufferSize != 0);
+    // Create an MDL over the new Buffer (allocated by DeviceGetConfiguration)
+    mmcData->CapabilitiesMdl = IoAllocateMdl(mmcData->CapabilitiesBuffer,
+                                             mmcData->CapabilitiesBufferSize,
+                                             FALSE, FALSE, NULL);
+    if (mmcData->CapabilitiesMdl == NULL) 
+    {
+        ExFreePool(mmcData->CapabilitiesBuffer);
+        mmcData->CapabilitiesBuffer = NULL;
+        mmcData->CapabilitiesBufferSize = 0;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // Create an IRP from which we will create a WDFREQUEST
+    mmcData->CapabilitiesIrp = IoAllocateIrp(deviceExtension->DeviceObject->StackSize + 1, FALSE);
+    if (mmcData->CapabilitiesIrp == NULL) 
+    {
+        IoFreeMdl(mmcData->CapabilitiesMdl);
+        mmcData->CapabilitiesMdl = NULL;
+        ExFreePool(mmcData->CapabilitiesBuffer);
+        mmcData->CapabilitiesBuffer = NULL;
+        mmcData->CapabilitiesBufferSize = 0;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // create WDF request object
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 
+                                            CDROM_REQUEST_CONTEXT);
+    status = WdfRequestCreateFromIrp(&attributes,
+                                     mmcData->CapabilitiesIrp,
+                                     FALSE,
+                                     &mmcData->CapabilitiesRequest);
+    if (!NT_SUCCESS(status))
+    {
+        return status;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceUpdateMmcCapabilities(
+    _In_ WDFDEVICE Device
+    )
+/*++
+
+Routine Description:
+
+   issue get congiguration command ans save result in device extension
+
+Arguments:
+
+    Device - device object
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    PCDROM_DEVICE_EXTENSION  deviceExtension = DeviceGetExtension(Device);
+    PCDROM_DATA              cdData = &(deviceExtension->DeviceAdditionalData);
+    PCDROM_MMC_EXTENSION     mmcData = &(cdData->Mmc);
+    ULONG                    returnedBytes = 0;
+    LONG                     updateState;
+
+    PAGED_CODE();
+
+    // first of all, check if we're still in the CdromMmcUpdateRequired state
+    // and, if yes, change it to CdromMmcUpdateStarted.
+    updateState = InterlockedCompareExchange((PLONG)&(cdData->Mmc.UpdateState),
+                                             CdromMmcUpdateStarted,
+                                             CdromMmcUpdateRequired);
+    if (updateState != CdromMmcUpdateRequired) {
+        // Mmc capabilities have been already updated or are in the process of
+        // being updated - just return STATUS_SUCCESS
+        return STATUS_SUCCESS;
+    }
+
+    // default to read-only, no Streaming, non-blank
+    mmcData->WriteAllowed = FALSE; 
+    mmcData->StreamingReadSupported = FALSE;
+    mmcData->StreamingWriteSupported = FALSE;
+
+    // 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.
+    status = DeviceGetConfiguration(Device,
+                                    mmcData->CapabilitiesBuffer,
+                                    mmcData->CapabilitiesBufferSize,
+                                    &returnedBytes,
+                                    FeatureProfileList,
+                                    SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT);
+
+    if (NT_SUCCESS(status) &&                               // succeeded.
+        (mmcData->CapabilitiesBufferSize >= returnedBytes)) // not overflow.
+    {
+        // update whether or not writes are allowed
+        // this should be the *ONLY* place writes are set to allowed
+        {
+            BOOLEAN         writeAllowed = FALSE;
+            FEATURE_NUMBER  validationSchema = 0;
+            ULONG           blockingFactor = 1;
+
+            DeviceUpdateMmcWriteCapability(mmcData->CapabilitiesBuffer,
+                                           returnedBytes,
+                                           TRUE,
+                                           &writeAllowed,
+                                           &validationSchema,
+                                           &blockingFactor);
+
+            mmcData->WriteAllowed = writeAllowed;
+            mmcData->ValidationSchema = validationSchema;
+            mmcData->Blocking = blockingFactor;
+        }
+
+        // Check if Streaming reads/writes are supported and cache
+        // this information for later use.
+        {
+            PFEATURE_HEADER header;
+            ULONG           minAdditionalLength;
+
+            minAdditionalLength = FIELD_OFFSET(FEATURE_DATA_REAL_TIME_STREAMING, Reserved2) -
+                                  sizeof(FEATURE_HEADER);
+
+            header = MmcDataFindFeaturePage(mmcData->CapabilitiesBuffer,
+                                            returnedBytes,
+                                            FeatureRealTimeStreaming);
+
+            if ((header != NULL) && 
+                (header->Current) &&
+                (header->AdditionalLength >= minAdditionalLength))
+            {
+                PFEATURE_DATA_REAL_TIME_STREAMING feature = (PFEATURE_DATA_REAL_TIME_STREAMING)header;
+
+                // If Real-Time feature is current, then Streaming reads are supported for sure.
+                mmcData->StreamingReadSupported = TRUE;
+
+                // Streaming writes are supported if an appropriate bit is set in the feature page.
+                mmcData->StreamingWriteSupported = (feature->StreamRecording == 1);
+            }
+        }
+
+        // update the flag to reflect that if the media is CSS protected DVD or CPPM-protected DVDAudio
+        {
+            PFEATURE_HEADER header;
+
+            header = DeviceFindFeaturePage(mmcData->CapabilitiesBuffer,
+                                           returnedBytes, 
+                                           FeatureDvdCSS);
+
+            mmcData->IsCssDvd = (header != NULL) && (header->Current);
+        }
+
+        // Update the guesstimate for the drive's write speed
+        // Use the GetConfig profile first as a quick-guess based
+        // on media "type", then continue with media-specific
+        // queries for older media types, and use GET_PERFORMANCE
+        // for all unknown/future media types.
+        {
+            // pseudo-code:
+            // 1) Determine default based on profile (slowest for media)
+            // 2) Determine default based on MODE PAGE 2Ah
+            // 3) Determine default based on GET PERFORMANCE data
+            // 4) Choose fastest reported speed (-1 == none reported)
+            // 5) If all failed (returned -1), go with very safe (slow) default
+            //
+            // This ensures that the retries do not overload the drive's processor.
+            // Sending at highest possible speed for the media is OK, because the
+            // major downside is drive processor usage.  (bus usage too, but most
+            // storage is becoming a point-to-point link.)
+
+            FEATURE_PROFILE_TYPE const profile =
+                                            mmcData->CapabilitiesBuffer->CurrentProfile[0] << (8*1) |
+                                            mmcData->CapabilitiesBuffer->CurrentProfile[1] << (8*0) ;
+            LONGLONG t1 = (LONGLONG)-1;
+            LONGLONG t2 = (LONGLONG)-1;
+            LONGLONG t3 = (LONGLONG)-1;
+            LONGLONG t4 = (LONGLONG)-1;
+            LONGLONG final;
+
+            t1 = DeviceRetryTimeGuessBasedOnProfile(profile);
+            t2 = DeviceRetryTimeDetectionBasedOnModePage2A(deviceExtension);
+            t3 = DeviceRetryTimeDetectionBasedOnGetPerformance(deviceExtension, TRUE);
+            t4 = DeviceRetryTimeDetectionBasedOnGetPerformance(deviceExtension, FALSE);
+
+            // use the "fastest" value returned
+            final = MAXLONGLONG;
+            if (t4 != -1)
+            {
+                final = min(final, t4);
+            }
+            if (t3 != -1)
+            {
+                final = min(final, t3);
+            }
+            if (t2 != -1)
+            {
+                final = min(final, t2);
+            }
+            if (t1 != -1)
+            {
+                final = min(final, t1);
+            }
+            if (final == MAXLONGLONG)
+            {
+                // worst case -- use relatively slow default....
+                final = WRITE_RETRY_DELAY_CD_4x;
+            }
+
+            cdData->ReadWriteRetryDelay100nsUnits = final;
+        }
+
+    }
+    else
+    {
+        // Rediscovery of MMC capabilities has failed - we'll need to retry
+        cdData->Mmc.UpdateState = CdromMmcUpdateRequired;
+    }
+
+    // Change the state to CdromMmcUpdateComplete if it is CdromMmcUpdateStarted.
+    // If it is not, some error must have happened while this function was executed
+    // and the state is CdromMmcUpdateRequired now. In that case, we want to perform
+    // everything again, so we do not set CdromMmcUpdateComplete.
+    InterlockedCompareExchange((PLONG)&(cdData->Mmc.UpdateState),
+                               CdromMmcUpdateComplete,
+                               CdromMmcUpdateStarted);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetConfigurationWithAlloc(
+    _In_ WDFDEVICE               Device,
+    _Outptr_result_bytebuffer_all_(*BytesReturned) 
+     PGET_CONFIGURATION_HEADER*  Buffer,  // this routine allocates this memory
+    _Out_ PULONG                 BytesReturned,
+    FEATURE_NUMBER const         StartingFeature,
+    ULONG const                  RequestedType
+    )
+/*++
+
+Routine Description:
+
+    This function will allocates configuration buffer and set the size.
+
+Arguments:
+
+    Device - device object
+    Buffer - to be allocated by this function
+    BytesReturned - size of the buffer
+    StartingFeature - the starting point of the feature list
+    RequestedType - 
+
+Return Value:
+
+    NTSTATUS
+
+NOTE: does not handle case where more than 65000 bytes are returned,
+      which requires multiple calls with different starting feature
+      numbers.
+
+--*/
+{
+    NTSTATUS                    status = STATUS_SUCCESS;
+    GET_CONFIGURATION_HEADER    header = {0};  // eight bytes, not a lot
+    PGET_CONFIGURATION_HEADER   buffer = NULL;
+    ULONG                       returned = 0;
+    ULONG                       size = 0;
+    ULONG                       i = 0;
+
+    PAGED_CODE();
+
+    *Buffer = NULL;
+    *BytesReturned = 0;
+
+    // send the first request down to just get the header
+    status = DeviceGetConfiguration(Device, 
+                                    &header, 
+                                    sizeof(header),
+                                    &returned, 
+                                    StartingFeature, 
+                                    RequestedType);
+
+    // now send command again, using information returned to allocate just enough memory
+    if (NT_SUCCESS(status)) 
+    {
+        size = header.DataLength[0] << 24 |
+               header.DataLength[1] << 16 |
+               header.DataLength[2] <<  8 |
+               header.DataLength[3] <<  0 ;
+
+        // the loop is in case that the retrieved data length is bigger than last time reported.
+        for (i = 0; (i < 4) && NT_SUCCESS(status); i++) 
+        {
+            // the datalength field is the size *following* itself, so adjust accordingly
+            size += 4*sizeof(UCHAR);
+
+            // make sure the size is reasonable
+            if (size <= sizeof(FEATURE_HEADER)) 
+            {
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                           "DeviceGetConfigurationWithAlloc: drive reports only %x bytes?\n",
+                           size));
+                status = STATUS_UNSUCCESSFUL;
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // allocate the memory
+                buffer = (PGET_CONFIGURATION_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+                                                                          size,
+                                                                          CDROM_TAG_FEATURE);
+
+                if (buffer == NULL) 
+                {
+                    status = STATUS_INSUFFICIENT_RESOURCES;
+                }
+            }
+
+            if (NT_SUCCESS(status))
+            {
+                // send the first request down to just get the header
+                status = DeviceGetConfiguration(Device, 
+                                                buffer, 
+                                                size, 
+                                                &returned,
+                                                StartingFeature, 
+                                                RequestedType);
+
+                if (!NT_SUCCESS(status))
+                {
+                    ExFreePool(buffer);
+                }
+                else if (returned > size)
+                {
+                    ExFreePool(buffer);
+                    status = STATUS_INTERNAL_ERROR;
+                }
+            }
+
+            // command succeeded.
+            if (NT_SUCCESS(status))
+            {
+                returned = buffer->DataLength[0] << 24 |
+                           buffer->DataLength[1] << 16 |
+                           buffer->DataLength[2] <<  8 |
+                           buffer->DataLength[3] <<  0 ;
+                returned += 4*sizeof(UCHAR);
+
+                if (returned <= size) 
+                {
+                    *Buffer = buffer;
+                    *BytesReturned = returned;  // amount of 'safe' memory
+                    // succes, get out of loop.
+                    status = STATUS_SUCCESS;
+                    break;  
+                }
+                else
+                {
+                    // the data size is bigger than the buffer size, retry using new size....
+                    size = returned;
+                    ExFreePool(buffer);
+                    buffer = NULL;
+                }
+            }
+        } // end of for() loop
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        // it failed after a number of attempts, so just fail.
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                   "DeviceGetConfigurationWithAlloc: Failed %d attempts to get all feature "
+                   "information\n", i));
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetConfiguration(
+    _In_  WDFDEVICE                 Device,
+    _Out_writes_bytes_to_(BufferSize, *ValidBytes)
+          PGET_CONFIGURATION_HEADER Buffer,
+    _In_  ULONG const               BufferSize,
+    _Out_ PULONG                    ValidBytes,
+    _In_  FEATURE_NUMBER const      StartingFeature,
+    _In_  ULONG const               RequestedType
+    )
+/*++
+
+Routine Description:
+
+    This function is used to get configuration data.
+
+Arguments:
+
+    Device - device object
+    Buffer - buffer address to hold data.
+    BufferSize - size of the buffer
+    ValidBytes - valid data size in buffer
+    StartingFeature - the starting point of the feature list
+    RequestedType - 
+
+Return Value:
+
+    NTSTATUS
+
+NOTE: does not handle case where more than 64k bytes are returned,
+      which requires multiple calls with different starting feature
+      numbers.
+
+--*/
+{
+    NTSTATUS                status;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    SCSI_REQUEST_BLOCK      srb;
+    PCDB                    cdb = (PCDB)srb.Cdb;
+
+    PAGED_CODE();
+
+    NT_ASSERT(ValidBytes);
+
+    // when system is low resources we can receive empty buffer
+    if (Buffer == NULL || BufferSize < sizeof(GET_CONFIGURATION_HEADER))
+    {
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    *ValidBytes = 0;
+
+    RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+    RtlZeroMemory(Buffer, BufferSize);
+
+    if (TEST_FLAG(deviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT)) 
+    {
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+#pragma warning(push)
+#pragma warning(disable: 6386) // OACR will complain buffer overrun: the writable size is 'BufferSize' bytes, but '65532'
+                               // bytes might be written, which is impossible because BufferSize > 0xFFFC.
+
+    if (BufferSize > 0xFFFC)
+    {
+        // cannot request more than 0xFFFC bytes in one request
+        // Eventually will "stitch" together multiple requests if needed
+        // Today, no drive has anywhere close to 4k.....
+        return DeviceGetConfiguration(Device, 
+                                      Buffer, 
+                                      0xFFFC, 
+                                      ValidBytes, 
+                                      StartingFeature, 
+                                      RequestedType);
+    }
+#pragma warning(pop)
+
+    //Start real work
+    srb.TimeOutValue = CDROM_GET_CONFIGURATION_TIMEOUT;
+    srb.CdbLength = 10;
+
+    cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
+    cdb->GET_CONFIGURATION.RequestType = (UCHAR)RequestedType;
+    cdb->GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(StartingFeature >> 8);
+    cdb->GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(StartingFeature & 0xff);
+    cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(BufferSize >> 8);
+    cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(BufferSize & 0xff);
+
+    status = DeviceSendSrbSynchronously(Device,  
+                                        &srb,  
+                                        Buffer,
+                                        BufferSize,
+                                        FALSE,
+                                        NULL);
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+               "DeviceGetConfiguration: Status was %x\n", status));
+
+    if (NT_SUCCESS(status) || 
+        (status == STATUS_BUFFER_OVERFLOW) || 
+        (status == STATUS_DATA_OVERRUN)) 
+    {
+        ULONG                       returned = srb.DataTransferLength;
+        PGET_CONFIGURATION_HEADER   header = (PGET_CONFIGURATION_HEADER)Buffer;
+        ULONG                       available = (header->DataLength[0] << (8*3)) |
+                                                (header->DataLength[1] << (8*2)) |
+                                                (header->DataLength[2] << (8*1)) |
+                                                (header->DataLength[3] << (8*0)) ;
+
+        available += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+        _Analysis_assume_(srb.DataTransferLength <= BufferSize);
+
+        // The true usable amount of data returned is the lesser of
+        // * the returned data per the srb.DataTransferLength field
+        // * the total size per the GET_CONFIGURATION_HEADER
+        // This is because ATAPI can't tell how many bytes really
+        // were transferred on success when using DMA.
+        if (available < returned)
+        {
+            returned = available;
+        }
+
+        NT_ASSERT(returned <= BufferSize);
+        *ValidBytes = (ULONG)returned;
+
+        //This is succeed case
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
+                   "DeviceGetConfiguration: failed %x\n", status));
+    }
+
+    return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceUpdateMmcWriteCapability(
+    _In_reads_bytes_(BufferSize) 
+        PGET_CONFIGURATION_HEADER   Buffer,
+    ULONG const                     BufferSize,
+    BOOLEAN const                   CurrentOnly, // TRUE == can drive write now, FALSE == can drive ever write
+    _Out_ PBOOLEAN                  Writable,
+    _Out_ PFEATURE_NUMBER           ValidationSchema,
+    _Out_ PULONG                    BlockingFactor
+    )
+/*++
+
+Routine Description:
+
+    This function will allocates configuration buffer and set the size.
+
+Arguments:
+
+    Buffer - 
+    BufferSize - size of the buffer
+    CurrentOnly - valid data size in buffer
+    Writable - the buffer is allocationed in non-paged pool.
+    validationSchema - the starting point of the feature list
+    BlockingFactor - 
+
+Return Value:
+
+    NTSTATUS
+
+NOTE: does not handle case where more than 64k bytes are returned,
+      which requires multiple calls with different starting feature
+      numbers.
+
+--*/
+{
+    //
+    // this routine is used to check if the drive can currently (current==TRUE)
+    // or can ever (current==FALSE) write to media with the current CDROM.SYS
+    // driver.  this check parses the GET_CONFIGURATION response data to search
+    // for the appropriate features and/or if they are current.
+    //
+    // this function should not allocate any resources, and thus may safely
+    // return from any point within the function.
+    //
+    PAGED_CODE();
+
+    *Writable = FALSE;
+    *ValidationSchema = 0;
+    *BlockingFactor = 1;
+
+    //
+    // if the drive supports hardware defect management and random writes, that's
+    // sufficient to allow writes.
+    //
+    {
+        PFEATURE_HEADER defectHeader;
+        PFEATURE_HEADER writableHeader;
+
+        defectHeader   = MmcDataFindFeaturePage(Buffer,
+                                                BufferSize,
+                                                FeatureDefectManagement);
+        writableHeader = MmcDataFindFeaturePage(Buffer,
+                                                BufferSize,
+                                                FeatureRandomWritable);
+
+        if (defectHeader == NULL || writableHeader == NULL)
+        {
+            // cannot write this way
+        }
+        else if (!CurrentOnly)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes supported (defect management)\n"));
+            *Writable = TRUE;
+            return;
+        }
+        else if (defectHeader->Current && writableHeader->Current)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes *allowed* (defect management)\n"));
+            *Writable = TRUE;
+            *ValidationSchema = FeatureDefectManagement;
+            return;
+        }
+    }
+
+    // Certain validation schema require the blocking factor
+    // This is a best-effort attempt to ensure that illegal
+    // requests do not make it to drive
+    {
+        PFEATURE_HEADER header;
+        ULONG           additionalLength;
+
+        // Certain validation schema require the blocking factor
+        // This is a best-effort attempt to ensure  that illegal
+        // requests do not make it to drive
+        additionalLength = RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_RANDOM_READABLE, Blocking) - sizeof(FEATURE_HEADER);
+
+        header = MmcDataFindFeaturePage(Buffer,
+                                        BufferSize,
+                                        FeatureRandomReadable);
+
+        if ((header != NULL) && 
+            (header->Current) &&
+            (header->AdditionalLength >= additionalLength))
+        {
+            PFEATURE_DATA_RANDOM_READABLE feature = (PFEATURE_DATA_RANDOM_READABLE)header;
+            *BlockingFactor = (feature->Blocking[0] << 8) | feature->Blocking[1];
+        }
+    }
+
+    // the majority of features to indicate write capability
+    // indicate this by a single feature existance/current bit.
+    // thus, can use a table-based method for the majority
+    // of the detection....
+    {
+        typedef struct {
+            FEATURE_NUMBER  FeatureToFind;    // the ones allowed
+            FEATURE_NUMBER  ValidationSchema; // and their related schema
+        } FEATURE_TO_WRITE_SCHEMA_MAP;
+
+        static FEATURE_TO_WRITE_SCHEMA_MAP const FeaturesToAllowWritesWith[] = {
+            { FeatureRandomWritable,               FeatureRandomWritable               },
+            { FeatureRigidRestrictedOverwrite,     FeatureRigidRestrictedOverwrite     },
+            { FeatureRestrictedOverwrite,          FeatureRestrictedOverwrite          },
+            { FeatureIncrementalStreamingWritable, FeatureIncrementalStreamingWritable },
+        };
+
+        ULONG count;
+        for (count = 0; count < RTL_NUMBER_OF(FeaturesToAllowWritesWith); count++)
+        {
+            PFEATURE_HEADER header = MmcDataFindFeaturePage(Buffer,
+                                                            BufferSize,
+                                                            FeaturesToAllowWritesWith[count].FeatureToFind);
+            if (header == NULL)
+            {
+                // cannot write using this method
+            }
+            else if (!CurrentOnly)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                            "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
+                            FeaturesToAllowWritesWith[count].FeatureToFind
+                            ));
+                *Writable = TRUE;
+                return;
+            }
+            else if (header->Current)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                            "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
+                            FeaturesToAllowWritesWith[count].FeatureToFind
+                            ));
+                *Writable = TRUE;
+                *ValidationSchema = FeaturesToAllowWritesWith[count].ValidationSchema;
+                return;
+            }
+        } // end count loop
+    }
+
+    // unfortunately, DVD+R media doesn't require IncrementalStreamingWritable feature
+    // to be explicitly set AND it has a seperate bit in the feature to indicate
+    // being able to write to this media type. Thus, use a special case of the above code.
+    {
+        PFEATURE_DATA_DVD_PLUS_R header;
+        ULONG                    additionalLength = FIELD_OFFSET(FEATURE_DATA_DVD_PLUS_R, Reserved2[0]) - sizeof(FEATURE_HEADER);
+        header = MmcDataFindFeaturePage(Buffer,
+                                        BufferSize,
+                                        FeatureDvdPlusR);
+
+        if (header == NULL || (header->Header.AdditionalLength < additionalLength) || (!header->Write))
+        {
+            // cannot write this way
+        }
+        else if (!CurrentOnly)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
+                        FeatureDvdPlusR
+                        ));
+            *Writable = TRUE;
+            return;
+        }
+        else if (header->Header.Current)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
+                        FeatureDvdPlusR
+                        ));
+            *Writable = TRUE;
+            *ValidationSchema = FeatureIncrementalStreamingWritable;
+            return;
+        }
+    }
+
+    // unfortunately, DVD+R DL media doesn't require IncrementalStreamingWritable feature
+    // to be explicitly set AND it has a seperate bit in the feature to indicate
+    // being able to write to this media type. Thus, use a special case of the above code.
+    {
+        PFEATURE_DATA_DVD_PLUS_R_DUAL_LAYER header;
+        ULONG additionalLength = FIELD_OFFSET(FEATURE_DATA_DVD_PLUS_R_DUAL_LAYER, Reserved2[0]) - sizeof(FEATURE_HEADER);
+        header = MmcDataFindFeaturePage(Buffer,
+                                        BufferSize,
+                                        FeatureDvdPlusRDualLayer);
+
+        if (header == NULL || (header->Header.AdditionalLength < additionalLength) || (!header->Write))
+        {
+            // cannot write this way
+        }
+        else if (!CurrentOnly)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes supported (feature %04x)\n",
+                        FeatureDvdPlusRDualLayer
+                        ));
+            *Writable = TRUE;
+            return;
+        }
+        else if (header->Header.Current)
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                        "DeviceUpdateMmcWriteCapability => Writes *allowed* (feature %04x)\n",
+                        FeatureDvdPlusRDualLayer
+                        ));
+            *Writable = TRUE;
+            *ValidationSchema = FeatureIncrementalStreamingWritable;
+            return;
+        }
+    }
+
+    // There are currently a number of drives on the market
+    // that fail to report:
+    // (a) FeatureIncrementalStreamingWritable as current
+    //     for CD-R / DVD-R profile.
+    // (b) FeatureRestrictedOverwrite as current for CD-RW
+    //     profile
+    // (c) FeatureRigidRestrictedOverwrite as current for
+    //     DVD-RW profile
+    //
+    // Thus, use the profiles also.
+    {
+        PFEATURE_HEADER header;
+        header = MmcDataFindFeaturePage(Buffer,
+                                        BufferSize,
+                                        FeatureProfileList);
+
+        if (header != NULL && header->Current)
+        {
+            // verify buffer bounds -- the below routine presumes full profile list provided
+            PUCHAR bufferEnd = ((PUCHAR)Buffer) + BufferSize;
+            PUCHAR headerEnd = ((PUCHAR)header) + header->AdditionalLength + RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER, AdditionalLength);
+            if (bufferEnd >= headerEnd) // this _should_ never occurr, but....
+            {
+                // Profiles don't contain any data other than current/not current.
+                // thus, can generically loop through them to see if any of the
+                // below (in order of preference) are current.
+                typedef struct {
+                    FEATURE_PROFILE_TYPE ProfileToFind;    // the ones allowed
+                    FEATURE_NUMBER       ValidationSchema; // and their related schema
+                } PROFILE_TO_WRITE_SCHEMA_MAP;
+
+                static PROFILE_TO_WRITE_SCHEMA_MAP const ProfilesToAllowWritesWith[] = {
+                    { ProfileDvdRewritable,  FeatureRigidRestrictedOverwrite     },
+                    { ProfileCdRewritable,   FeatureRestrictedOverwrite },
+                    { ProfileDvdRecordable,  FeatureIncrementalStreamingWritable },
+                    { ProfileCdRecordable,   FeatureIncrementalStreamingWritable },
+                };
+
+                ULONG count;
+                for (count = 0; count < RTL_NUMBER_OF(ProfilesToAllowWritesWith); count++)
+                {
+                    BOOLEAN exists = FALSE;
+                    MmcDataFindProfileInProfiles((PFEATURE_DATA_PROFILE_LIST)header,
+                                                 ProfilesToAllowWritesWith[count].ProfileToFind,
+                                                 CurrentOnly,
+                                                 &exists);
+                    if (exists)
+                    {
+                        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+                                    "DeviceUpdateMmcWriteCapability => Writes %s (profile %04x)\n",
+                                    (CurrentOnly ? "*allowed*" : "supported"),
+                                    FeatureDvdPlusR
+                                    ));
+
+                        *Writable = TRUE;
+                        *ValidationSchema = ProfilesToAllowWritesWith[count].ValidationSchema;
+                        return;
+                    }
+                } // end count loop
+            } // end if (bufferEnd >= headerEnd)
+
+        } // end if (header != NULL && header->Current)
+    }
+
+    // nothing matched to say it's writable.....
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+MmcDataFindFeaturePage(
+    _In_reads_bytes_(Length) 
+        PGET_CONFIGURATION_HEADER   FeatureBuffer,
+    ULONG const                     Length,
+    FEATURE_NUMBER const            Feature
+    )
+/*++
+
+Routine Description:
+
+    search the specific feature from feature list buffer
+
+Arguments:
+
+    FeatureBuffer - buffer of feature list
+    Length - size of the buffer
+    Feature - feature wanted to find
+
+Return Value:
+
+    PVOID - if found, pointer of starting address of the specific feature.
+            otherwise, NULL.
+
+--*/
+{
+    PUCHAR buffer;
+    PUCHAR limit;
+    ULONG  validLength;
+
+    PAGED_CODE();
+
+    if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)) {
+        return NULL;
+    }
+
+    // Calculate the length of valid data available in the
+    // capabilities buffer from the DataLength field
+    REVERSE_BYTES(&validLength, FeatureBuffer->DataLength);
+    validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
+
+    // set limit to point to first illegal address
+    limit  = (PUCHAR)FeatureBuffer;
+    limit += min(Length, validLength);
+
+    // set buffer to point to first page
+    buffer = FeatureBuffer->Data;
+
+    // loop through each page until we find the requested one, or
+    // until it's not safe to access the entire feature header
+    // (if equal, have exactly enough for the feature header)
+    while (buffer + sizeof(FEATURE_HEADER) <= limit) 
+    {
+        PFEATURE_HEADER header = (PFEATURE_HEADER)buffer;
+        FEATURE_NUMBER  thisFeature;
+
+        thisFeature  = (header->FeatureCode[0] << 8) |
+                       (header->FeatureCode[1]);
+
+        if (thisFeature == Feature) 
+        {
+            PUCHAR temp;
+
+            // if don't have enough memory to safely access all the feature
+            // information, return NULL
+            temp = buffer;
+            temp += sizeof(FEATURE_HEADER);
+            temp += header->AdditionalLength;
+
+            if (temp > limit) 
+            {
+                // this means the transfer was cut-off, an insufficiently
+                // small buffer was given, or other arbitrary error.  since
+                // it's not safe to view the amount of data (even though
+                // the header is safe) in this feature, pretend it wasn't
+                // transferred at all...
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                           "Feature %x exists, but not safe to access all its "
+                           "data.  returning NULL\n", Feature));
+                return NULL;
+            } 
+            else 
+            {
+                return buffer;
+            }
+        }
+
+        if ((header->AdditionalLength % 4) &&
+            !(Feature >= 0xff00 && Feature <= 0xffff))
+        {
+            return NULL;
+        }
+
+        buffer += sizeof(FEATURE_HEADER);
+        buffer += header->AdditionalLength;
+
+    }
+    return NULL;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+MmcDataFindProfileInProfiles(
+    _In_ FEATURE_DATA_PROFILE_LIST const* ProfileHeader,
+    _In_ FEATURE_PROFILE_TYPE const       ProfileToFind,
+    _In_ BOOLEAN const                    CurrentOnly,
+    _Out_ PBOOLEAN                        Found
+    )
+/*++
+
+Routine Description:
+
+    search the specific feature from feature list buffer
+
+Arguments:
+
+    ProfileHeader - buffer of profile list
+    ProfileToFind - profile to be found
+    CurrentOnly - 
+
+Return Value:
+
+    Found - found or not
+
+--*/
+{
+    FEATURE_DATA_PROFILE_LIST_EX const * profile;
+    ULONG numberOfProfiles;
+    ULONG i;
+
+    PAGED_CODE();
+
+    // initialize output
+    *Found = FALSE;
+
+    // sanity check
+    if (ProfileHeader->Header.AdditionalLength % 2 != 0) 
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                   "Profile total length %x is not integral multiple of 4\n",
+                   ProfileHeader->Header.AdditionalLength));
+        NT_ASSERT(FALSE);
+        return;
+    }
+
+    // calculate number of profiles
+    numberOfProfiles = ProfileHeader->Header.AdditionalLength / 4;
+    profile = ProfileHeader->Profiles; // zero-sized array
+
+    // loop through profiles
+    for (i = 0; i < numberOfProfiles; i++) 
+    {
+        FEATURE_PROFILE_TYPE currentProfile;
+
+        currentProfile = (profile->ProfileNumber[0] << 8) |
+                         (profile->ProfileNumber[1] & 0xff);
+
+        if (currentProfile == ProfileToFind)
+        {
+            if (profile->Current || (!CurrentOnly))
+            {
+                *Found = TRUE;
+            }
+        }
+
+        profile++;
+    }
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeGuessBasedOnProfile(
+    FEATURE_PROFILE_TYPE const Profile
+    )
+/*++
+
+Routine Description:
+
+    determine the retry time based on profile
+
+Arguments:
+
+    Profile - 
+
+Return Value:
+
+    LONGLONG - retry time
+
+--*/
+{
+    LONGLONG result = -1; // this means we have no idea
+
+    PAGED_CODE();
+
+    switch (Profile)
+    {
+    case ProfileInvalid:               // = 0x0000,
+    case ProfileNonRemovableDisk:      // = 0x0001,
+    case ProfileRemovableDisk:         // = 0x0002,
+    case ProfileMOErasable:            // = 0x0003,
+    case ProfileMOWriteOnce:           // = 0x0004,
+    case ProfileAS_MO:                 // = 0x0005,
+    // Reserved                             0x0006 - 0x0007,
+    // Reserved                             0x000b - 0x000f,
+    // Reserved                             0x0017 - 0x0019
+    // Reserved                             0x001C - 001F
+    // Reserved                             0x0023 - 0x0029
+    // Reserved                             0x002C - 0x003F
+    // Reserved                             0x0044 - 0x004F
+    // Reserved                             0x0053 - 0xfffe
+    case ProfileNonStandard:          //  = 0xffff
+    default:
+    {
+        NOTHING; // no default
+        break;
+    }
+
+    case ProfileCdrom:                 // = 0x0008,
+    case ProfileCdRecordable:          // = 0x0009,
+    case ProfileCdRewritable:          // = 0x000a,
+    case ProfileDDCdrom:               // = 0x0020, // obsolete
+    case ProfileDDCdRecordable:        // = 0x0021, // obsolete
+    case ProfileDDCdRewritable:        // = 0x0022, // obsolete
+    {
+        // 4x is ok as all CD drives have
+        // at least 64k*4 (256k) buffer
+        // and this is just a first-pass
+        // guess based only on profile
+        result = WRITE_RETRY_DELAY_CD_4x;
+        break;
+    }
+    case ProfileDvdRom:                // = 0x0010,
+    case ProfileDvdRecordable:         // = 0x0011,
+    case ProfileDvdRam:                // = 0x0012,
+    case ProfileDvdRewritable:         // = 0x0013,  // restricted overwrite
+    case ProfileDvdRWSequential:       // = 0x0014,
+    case ProfileDvdDashRLayerJump:     // = 0x0016,
+    case ProfileDvdPlusRW:             // = 0x001A,
+    case ProfileDvdPlusR:              // = 0x001B,
+    {
+        result = WRITE_RETRY_DELAY_DVD_1x;
+        break;
+    }
+    case ProfileDvdDashRDualLayer:     // = 0x0015,
+    case ProfileDvdPlusRWDualLayer:    // = 0x002A,
+    case ProfileDvdPlusRDualLayer:     // = 0x002B,
+    {
+        result = WRITE_RETRY_DELAY_DVD_1x;
+        break;
+    }
+
+    case ProfileBDRom:                 // = 0x0040,
+    case ProfileBDRSequentialWritable: // = 0x0041,  // BD-R 'SRM'
+    case ProfileBDRRandomWritable:     // = 0x0042,  // BD-R 'RRM'
+    case ProfileBDRewritable:          // = 0x0043,
+    {
+        // I could not find specifications for the
+        // minimal 1x data rate for BD media.  Use
+        // HDDVD values for now, since they are
+        // likely to be similar.  Also, all media
+        // except for CD, DVD, and AS-MO should
+        // already fully support GET_CONFIG, so
+        // this guess is only used if we fail to
+        // get a performance descriptor....
+        result = WRITE_RETRY_DELAY_HDDVD_1x;
+        break;
+    }
+
+    case ProfileHDDVDRom:              // = 0x0050,
+    case ProfileHDDVDRecordable:       // = 0x0051,
+    case ProfileHDDVDRam:              // = 0x0052,
+    {
+        // All HDDVD drives support GET_PERFORMANCE
+        // so this guess is fine at 1x....
+        result = WRITE_RETRY_DELAY_HDDVD_1x;
+        break;
+    }
+
+    // addition of any further profile types is not
+    // technically required as GET PERFORMANCE
+    // should succeed for all future drives.  However,
+    // it is useful in case GET PERFORMANCE does
+    // fail for other reasons (i.e. bus resets, etc)
+
+    } // end switch(Profile)
+
+    return result;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeDetectionBasedOnModePage2A(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    determine the retry time based on mode sense data
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    LONGLONG - retry time
+
+--*/
+{
+    NTSTATUS    status;
+    ULONG       transferSize = min(0xFFF0, DeviceExtension->ScratchContext.ScratchBufferSize);
+    CDB         cdb;
+    LONGLONG    result = -1;
+
+    PAGED_CODE();
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+    cdb.MODE_SENSE10.Dbd           = 1;
+    cdb.MODE_SENSE10.PageCode      = MODE_PAGE_CAPABILITIES;
+    cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(transferSize >> 8);
+    cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);
+
+    status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 10);
+
+    // analyze the data on success....
+    if (NT_SUCCESS(status))
+    {
+        MODE_PARAMETER_HEADER10 const* header = DeviceExtension->ScratchContext.ScratchBuffer;
+        CDVD_CAPABILITIES_PAGE const*  page = NULL;
+        ULONG dataLength = (header->ModeDataLength[0] << (8*1)) |
+                           (header->ModeDataLength[1] << (8*0)) ;
+
+        // no possible overflow
+        if (dataLength != 0)
+        {
+            dataLength += RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER10, ModeDataLength);
+        }
+
+        // If it's not abundantly clear, we really don't trust the drive
+        // to be returning valid data.  Get the page pointer and usable
+        // size of the page here...
+        if (dataLength < sizeof(MODE_PARAMETER_HEADER10))
+        {
+            dataLength = 0;
+        }
+        else if (dataLength > DeviceExtension->ScratchContext.ScratchBufferSize)
+        {
+            dataLength = 0;
+        }
+        else if ((header->BlockDescriptorLength[1] == 0) &&
+                 (header->BlockDescriptorLength[0] == 0))
+        {
+            dataLength -= sizeof(MODE_PARAMETER_HEADER10);
+            page = (CDVD_CAPABILITIES_PAGE const *)(header + 1);
+        }
+        else if ((header->BlockDescriptorLength[1] == 0) &&
+                 (header->BlockDescriptorLength[0] == sizeof(MODE_PARAMETER_BLOCK)))
+        {
+            dataLength -= sizeof(MODE_PARAMETER_HEADER10);
+            dataLength -= min(dataLength, sizeof(MODE_PARAMETER_BLOCK));
+            page = (CDVD_CAPABILITIES_PAGE const *)
+                                            ( ((PUCHAR)header) +
+                                              sizeof(MODE_PARAMETER_HEADER10) +
+                                              sizeof(MODE_PARAMETER_BLOCK)
+                                              );
+        }
+
+        // Change dataLength from the size available per the header to
+        // the size available per the page itself.
+        if ((page != NULL) &&
+            (dataLength >= RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE, PageLength))
+            )
+        {
+            dataLength = min(dataLength, ((ULONG)(page->PageLength) + 2));
+        }
+
+        // Ignore the page if the fastest write speed field isn't available.
+        if ((page != NULL) &&
+            (dataLength < RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE, WriteSpeedMaximum))
+            )
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "ModePage 2Ah was requested, but drive reported "
+                        "only %x bytes (%x needed). Ignoring.\n",
+                        dataLength,
+                        RTL_SIZEOF_THROUGH_FIELD(CDVD_CAPABILITIES_PAGE, WriteSpeedMaximum)
+                        ));
+            page = NULL;
+        }
+
+        // Verify the page we requested is the one the drive actually provided
+        if ((page != NULL) && (page->PageCode != MODE_PAGE_CAPABILITIES))
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "ModePage 2Ah was requested, but drive reported "
+                        "page %x\n",
+                        page->PageCode
+                        ));
+            page = NULL;
+        }
+
+        // If _everything_ succeeded, then use the speed value in the page!
+        if (page != NULL)
+        {
+            ULONG temp =
+                (page->WriteSpeedMaximum[0] << (8*1)) |
+                (page->WriteSpeedMaximum[1] << (8*0)) ;
+            // stored as 1,000 byte increments...
+            temp *= 1000;
+            // typically stored at 2448 bytes/sector due to CD media
+            // error up to 20% high by presuming it returned 2048 data
+            // and convert to sectors/second
+            temp /= 2048;
+            // currently: sectors/sec
+            // ignore too-small or zero values
+            if (temp != 0)
+            {
+                result = ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(temp);
+            }
+        }
+    }
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return result;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeDetectionBasedOnGetPerformance(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 UseLegacyNominalPerformance
+    )
+/*++
+
+Routine Description:
+
+    determine the retry time based on get performance data
+
+Arguments:
+
+    DeviceExtension - device context
+    UseLegacyNominalPerformance - 
+
+Return Value:
+
+    LONGLONG - retry time
+
+--*/
+{
+    typedef struct _GET_PERFORMANCE_HEADER {
+        UCHAR TotalDataLength[4]; // not including this field
+        UCHAR Except : 1;
+        UCHAR Write  : 1;
+        UCHAR Reserved0 : 6;
+        UCHAR Reserved1[3];
+    } GET_PERFORMANCE_HEADER, *PGET_PERFORMANCE_HEADER;
+    C_ASSERT( sizeof(GET_PERFORMANCE_HEADER) == 8);
+
+    typedef struct _GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR {
+        UCHAR StartLba[4];
+        UCHAR StartPerformance[4];
+        UCHAR EndLba[4];
+        UCHAR EndPerformance[4];
+    } GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR, *PGET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR;
+    C_ASSERT( sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR) == 16);
+
+
+    typedef struct _GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR {
+        UCHAR MixedReadWrite          : 1;
+        UCHAR GuaranteedForWholeMedia : 1;
+        UCHAR Reserved0_RDD           : 1;
+        UCHAR WriteRotationControl    : 2;
+        UCHAR Reserved1               : 3;
+        UCHAR Reserved2[3];
+
+        UCHAR MediaCapacity[4];
+        UCHAR ReadSpeedKilobytesPerSecond[4];
+        UCHAR WriteSpeedKilobytesPerSecond[4];
+    } GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR, *PGET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR;
+    C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) == 16);
+
+    //////
+
+    NTSTATUS    status;
+    LONGLONG    result = -1;
+
+    // transfer size -- descriptors + 8 byte header
+    // Note: this size is identical for both descriptor types
+    C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR));
+
+    ULONG const maxDescriptors = min(200, (DeviceExtension->ScratchContext.ScratchBufferSize-sizeof(GET_PERFORMANCE_HEADER))/sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR));
+    ULONG       validDescriptors = 0;
+    ULONG       transferSize = sizeof(GET_PERFORMANCE_HEADER) + (maxDescriptors*sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR));
+    CDB         cdb;
+
+    PAGED_CODE();
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    if (UseLegacyNominalPerformance)
+    {
+        cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;
+        cdb.GET_PERFORMANCE.Except = 0;
+        cdb.GET_PERFORMANCE.Write = 1;
+        cdb.GET_PERFORMANCE.Tolerance = 2; // only defined option
+        cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors[1] = (UCHAR)maxDescriptors;
+        cdb.GET_PERFORMANCE.Type = 0; // legacy nominal descriptors
+    }
+    else
+    {
+        cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;
+        cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors[1] = (UCHAR)maxDescriptors;
+        cdb.GET_PERFORMANCE.Type = 3; // write speed
+    }
+
+    status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, NULL, transferSize, TRUE, &cdb, 12, CDROM_GET_PERFORMANCE_TIMEOUT);
+
+    // determine how many valid descriptors there actually are
+    if (NT_SUCCESS(status))
+    {
+        GET_PERFORMANCE_HEADER const* header = (GET_PERFORMANCE_HEADER const*)DeviceExtension->ScratchContext.ScratchBuffer;
+        ULONG temp1 = (header->TotalDataLength[0] << (8*3)) |
+                      (header->TotalDataLength[1] << (8*2)) |
+                      (header->TotalDataLength[2] << (8*1)) |
+                      (header->TotalDataLength[3] << (8*0)) ;
+
+        // adjust data size for header
+        if (temp1 + (ULONG)RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER, TotalDataLength) < temp1)
+        {
+            temp1 = 0;
+        }
+        else if (temp1 != 0)
+        {
+            temp1 += RTL_SIZEOF_THROUGH_FIELD(GET_PERFORMANCE_HEADER, TotalDataLength);
+        }
+
+        if (temp1 == 0)
+        {
+            // no data returned
+        }
+        else if (temp1 <= sizeof(GET_PERFORMANCE_HEADER))
+        {
+            // only the header returned, no descriptors
+        }
+        else if (UseLegacyNominalPerformance &&
+                 ((header->Except != 0) || (header->Write == 0))
+                 )
+        {
+            // bad data being returned -- ignore it
+        }
+        else if (!UseLegacyNominalPerformance &&
+                 ((header->Except != 0) || (header->Write != 0))
+                 )
+        {
+            // returning Performance (Type 0) data, not requested Write Speed (Type 3) data
+        }
+        else if ( (temp1 - sizeof(GET_PERFORMANCE_HEADER)) % sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) != 0)
+        {
+            // Note: this size is identical for both descriptor types
+            C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR));
+
+            // not returning valid data....
+        }
+        else // save how many are usable
+        {
+            // Note: this size is identical for both descriptor types
+            C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR));
+
+            // take the smaller usable value
+            temp1 = min(temp1, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
+            // then determine the usable descriptors
+            validDescriptors = (temp1 - sizeof(GET_PERFORMANCE_HEADER)) / sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR);
+        }
+    }
+
+    // The drive likely supports this command.
+    // Verify the data makes sense.
+    if (NT_SUCCESS(status))
+    {
+        ULONG       i;
+        GET_PERFORMANCE_HEADER const*                 header = (GET_PERFORMANCE_HEADER const*)DeviceExtension->ScratchContext.ScratchBuffer;
+        GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR const* descriptor = (GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR const*)(header+1); // pointer math
+
+        // NOTE: We could write this loop twice, once for each write descriptor type
+        //       However, the only fields of interest are the writeKBps field (Type 3) and
+        //       the EndPerformance field (Type 0), which both exist in the same exact
+        //       location and have essentially the same meaning.  So, just use the same
+        //       loop/structure pointers for both of the to simplify the readability of
+        //       this code.  The C_ASSERT()s here verify this at compile-time.
+
+        C_ASSERT( sizeof(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR) == sizeof(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR));
+        C_ASSERT( FIELD_OFFSET(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR, WriteSpeedKilobytesPerSecond) ==
+                  FIELD_OFFSET(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR, EndPerformance)
+                  );
+        C_ASSERT( RTL_FIELD_SIZE(GET_PERFORMANCE_WRITE_SPEED_DESCRIPTOR, WriteSpeedKilobytesPerSecond) ==
+                  RTL_FIELD_SIZE(GET_PERFORMANCE_NOMINAL_PERFORMANCE_DESCRIPTOR, EndPerformance)
+                  );
+
+        // loop through them all, and find the fastest listed write speed
+        for (i = 0; NT_SUCCESS(status) && (i <validDescriptors); descriptor++, i++)
+        {
+            ULONG const writeKBps =
+                (descriptor->WriteSpeedKilobytesPerSecond[0] << (8*3)) |
+                (descriptor->WriteSpeedKilobytesPerSecond[1] << (8*2)) |
+                (descriptor->WriteSpeedKilobytesPerSecond[2] << (8*1)) |
+                (descriptor->WriteSpeedKilobytesPerSecond[3] << (8*0)) ;
+
+            // Avoid overflow and still have good estimates
+            // 0x1 0000 0000 / 1000  == 0x00418937 == maximum writeKBps to multiple first
+            ULONG const sectorsPerSecond =
+                (writeKBps > 0x00418937) ? // would overflow occur by multiplying by 1000?
+                ((writeKBps / 2048) * 1000) : // must divide first, minimal loss of accuracy
+                ((writeKBps * 1000) / 2048) ; // must multiply first, avoid loss of accuracy
+
+            if (sectorsPerSecond <= 0)
+            {
+                break; // out of the loop -- no longer valid data (very defensive programming)
+            }
+
+            // we have at least one valid result, so prevent returning -1 as our result
+            if (result == -1) { result = MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS; }
+
+            // take the fastest speed (smallest wait time) we've found thus far
+            result = min(result, ConvertSectorsPerSecondTo100nsUnitsFor64kWrite(sectorsPerSecond));
+        }
+    }
+
+    ScratchBuffer_EndUse(DeviceExtension);
+    
+    return result;
+}
+
+#pragma warning(pop) // un-sets any local warning changes
diff --git a/drivers/storage/class/cdrom_new/mmc.h b/drivers/storage/class/cdrom_new/mmc.h
new file mode 100644 (file)
index 0000000..51c0330
--- /dev/null
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    mmc.h
+
+Abstract:
+
+    Functions for MMC area.
+
+Author:
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __MMC_H__
+#define __MMC_H__
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceDeallocateMmcResources(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceAllocateMmcResources(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceUpdateMmcCapabilities(
+    _In_ WDFDEVICE Device
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetConfigurationWithAlloc(
+    _In_ WDFDEVICE                  Device,
+    _Outptr_result_bytebuffer_all_(*BytesReturned) 
+    PGET_CONFIGURATION_HEADER *     Buffer,
+    _Out_ PULONG                    BytesReturned,
+    FEATURE_NUMBER const            StartingFeature,
+    ULONG const                     RequestedType
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceGetConfiguration(
+    _In_  WDFDEVICE                 Device,
+    _Out_writes_bytes_to_(BufferSize, *ValidBytes)
+          PGET_CONFIGURATION_HEADER Buffer,
+    _In_  ULONG const               BufferSize,
+    _Out_ PULONG                    ValidBytes,
+    _In_  FEATURE_NUMBER const      StartingFeature,
+    _In_  ULONG const               RequestedType
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceUpdateMmcWriteCapability(
+    _In_reads_bytes_(BufferSize) 
+        PGET_CONFIGURATION_HEADER   Buffer,
+    ULONG const                     BufferSize,
+    BOOLEAN const                   CurrentOnly, // TRUE == can drive write now, FALSE == can drive ever write
+    _Out_ PBOOLEAN                  Writable,
+    _Out_ PFEATURE_NUMBER           ValidationSchema,
+    _Out_ PULONG                    BlockingFactor
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+PVOID
+MmcDataFindFeaturePage(
+    _In_reads_bytes_(Length) 
+       PGET_CONFIGURATION_HEADER    FeatureBuffer,
+    ULONG const                     Length,
+    FEATURE_NUMBER const            Feature
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+MmcDataFindProfileInProfiles(
+     _In_   FEATURE_DATA_PROFILE_LIST const* ProfileHeader,
+     _In_   FEATURE_PROFILE_TYPE const       ProfileToFind,
+     _In_   BOOLEAN const                    CurrentOnly,
+     _Out_  PBOOLEAN                         Found
+     );
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeGuessBasedOnProfile(
+    FEATURE_PROFILE_TYPE const Profile
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeDetectionBasedOnModePage2A(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+_Ret_range_(-1,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+LONGLONG
+DeviceRetryTimeDetectionBasedOnGetPerformance(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 UseLegacyNominalPerformance
+    );
+
+#endif // __MMC_H__
diff --git a/drivers/storage/class/cdrom_new/pnppower.c b/drivers/storage/class/cdrom_new/pnppower.c
new file mode 100644 (file)
index 0000000..2b4d354
--- /dev/null
@@ -0,0 +1,689 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    pnppower.c
+
+Abstract:
+
+    Functions to handle PnP and Power IRPs.
+
+Environment:
+
+    kernel mode only
+
+Notes:
+    optical devices do not need to issue SPIN UP when power up.
+    The device itself should SPIN UP to process commands.
+
+
+Revision History:
+
+--*/
+
+
+#include "ntddk.h"
+#include "wdfcore.h"
+
+#include "cdrom.h"
+#include "ioctl.h"
+#include "scratch.h"
+#include "mmc.h"
+
+#ifdef DEBUG_USE_WPP
+#include "pnppower.tmh"
+#endif
+
+
+NTSTATUS
+DeviceScratchSyncCache(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+NTSTATUS
+DeviceScratchPreventMediaRemoval(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 Prevent
+    );
+
+NTSTATUS
+RequestIssueShutdownFlush(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ PIRP                    Irp
+    );
+
+IO_COMPLETION_ROUTINE RequestProcessPowerIrpCompletion;
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE RequestUnlockQueueCompletion;
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DevicePowerSettingCallback)
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+
+#pragma warning(disable:28118)  // Dispatch routines for IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS occur at PASSIVE_LEVEL.
+                                // WDF defines EVT_WDFDEVICE_WDM_IRP_PREPROCESS with _IRQL_requires_max_(DISPATCH_LEVEL),
+                                // triggering a false positive for this warning.
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestProcessShutdownFlush(
+    WDFDEVICE  Device,
+    PIRP       Irp
+    )
+/*++
+
+Routine Description:
+
+    process IRP: IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS
+
+Arguments:
+
+    Device - device object
+    Irp - the irp
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PIO_STACK_LOCATION      currentStack = NULL;
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+
+    //add trace info
+
+    // acquire the shutdown/flush lock
+    WdfWaitLockAcquire(deviceExtension->ShutdownFlushWaitLock, NULL);
+
+    currentStack = IoGetCurrentIrpStackLocation(Irp);
+
+    // finish all current requests
+    WdfIoQueueStopSynchronously(deviceExtension->SerialIOQueue);
+
+    // sync cache
+    if (NT_SUCCESS(status))
+    {
+        // safe to use scratch srb to send the request.
+        status = DeviceScratchSyncCache(deviceExtension);
+    }
+
+    // For SHUTDOWN, allow media removal.
+    if (NT_SUCCESS(status))
+    {
+        if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN)
+        {
+            // safe to use scratch srb to send the request.
+            status = DeviceScratchPreventMediaRemoval(deviceExtension, FALSE);
+        }
+    }
+
+    // Use original IRP, send SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH (no retry)
+    if (NT_SUCCESS(status))
+    {
+        status = RequestIssueShutdownFlush(deviceExtension, Irp);
+    }
+
+    // restart queue to allow processing further requests.
+    WdfIoQueueStart(deviceExtension->SerialIOQueue);
+
+    // release the shutdown/flush lock
+    WdfWaitLockRelease(deviceExtension->ShutdownFlushWaitLock);
+
+    // 6. complete the irp
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, 0);
+
+    return status;
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestProcessSetPower(
+    WDFDEVICE  Device,
+    PIRP       Irp
+    )
+/*++
+
+Routine Description:
+
+    process IRP: IRP_MJ_POWER
+
+Arguments:
+
+    Device - device object
+    Irp - the irp
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
+    PIO_STACK_LOCATION      currentStack;
+    NTSTATUS                status;
+    BOOLEAN                 IrpMarkedPending = FALSE;
+
+    currentStack = IoGetCurrentIrpStackLocation(Irp);
+
+    if ((currentStack->Parameters.Power.Type == DevicePowerState) &&
+        (currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0))
+    {
+        // We need to unlock the device queue in D3 postprocessing.
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               RequestProcessPowerIrpCompletion,
+                               deviceExtension,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        // Mark the Irp pending as we'll defer the I/O completion.
+        IoMarkIrpPending(Irp);
+        IrpMarkedPending = TRUE;
+    }
+    else {
+
+        IoSkipCurrentIrpStackLocation(Irp);
+    }
+
+#pragma warning(push)
+#pragma warning(disable: 28193) // OACR will complain that the status variable is not examined.
+
+    //
+    // Deliver the IRP back to the framework.
+    //
+
+    status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp);
+
+    if (IrpMarkedPending)
+    {
+        UNREFERENCED_PARAMETER(status);
+        return STATUS_PENDING;
+    }
+
+#pragma warning(pop)
+
+    return status;
+}
+
+// use scratch SRB to issue SYNC CACHE command.
+NTSTATUS
+DeviceScratchSyncCache(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    use scratch buffer to send SYNC CACHE command
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    ULONG       transferSize = 0;
+    CDB         cdb;
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+    //srb->QueueTag = SP_UNTAGGED;
+    //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+
+    status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, NULL, transferSize, FALSE, &cdb, 10, TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4));
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return status;
+}
+
+NTSTATUS
+DeviceScratchPreventMediaRemoval(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 Prevent
+    )
+/*++
+
+Routine Description:
+
+    use scratch SRB to issue ALLOW/PREVENT MEDIA REMOVAL command.
+
+Arguments:
+
+    DeviceExtension - device context
+    Prevent - TRUE (prevent media removal); FALSE (allow media removal)
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    ULONG       transferSize = 0;
+    CDB         cdb;
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    RtlZeroMemory(&cdb, sizeof(CDB));
+    // Set up the CDB
+    cdb.MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+    cdb.MEDIA_REMOVAL.Prevent = Prevent;
+    //srb->QueueTag = SP_UNTAGGED;
+    //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+
+    status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, FALSE, &cdb, 6);
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+    return status;
+}
+
+NTSTATUS
+RequestIssueShutdownFlush(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ PIRP                    Irp
+    )
+/*++
+
+Routine Description:
+
+    issue SRB function Flush/Shutdown command.
+
+Arguments:
+
+    DeviceExtension - device context
+    Irp - the irp
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
+    PIO_STACK_LOCATION  currentStack = NULL;
+
+    ULONG       transferSize = 0;
+    BOOLEAN     shouldRetry = TRUE;
+    ULONG       timesAlreadyRetried = 0;
+    LONGLONG    retryIn100nsUnits = 0;
+
+
+    currentStack = IoGetCurrentIrpStackLocation(Irp);
+
+
+    ScratchBuffer_BeginUse(DeviceExtension);
+
+    // no retry needed.
+    {
+        ScratchBuffer_SetupSrb(DeviceExtension, NULL, transferSize, FALSE);
+
+        // Set up the SRB/CDB
+        srb->QueueTag = SP_UNTAGGED;
+        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+        srb->TimeOutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4);
+        srb->CdbLength = 0;
+
+        if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN)
+        {
+            srb->Function = SRB_FUNCTION_SHUTDOWN;
+        }
+        else
+        {
+            srb->Function = SRB_FUNCTION_FLUSH;
+        }
+
+        ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
+
+        shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
+                                                                timesAlreadyRetried,
+                                                                &status,
+                                                                &retryIn100nsUnits);
+        UNREFERENCED_PARAMETER(shouldRetry); //defensive coding, avoid PREFAST warning.
+        UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning.
+
+        // retrieve the real status from the request.
+        status = WdfRequestGetStatus(DeviceExtension->ScratchContext.ScratchRequest);
+    }
+
+    ScratchBuffer_EndUse(DeviceExtension);
+
+
+    return status;
+}
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestUnlockQueueCompletion (
+    _In_ WDFREQUEST Request,
+    _In_ WDFIOTARGET Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+    _In_ WDFCONTEXT Context
+    )
+{
+    PIRP                    Irp = Context;
+    WDFDEVICE               device = WdfIoTargetGetDevice(Target);
+    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
+
+    UNREFERENCED_PARAMETER(Request);
+    UNREFERENCED_PARAMETER(Params);
+
+    deviceExtension->PowerContext.Options.LockQueue = FALSE;
+
+    PowerContextEndUse(deviceExtension);
+
+    // Complete the original power irp
+    IoCompleteRequest( Irp, IO_NO_INCREMENT );
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+RequestProcessPowerIrpCompletion(
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PIRP Irp,
+    _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
+    )
+/*++
+
+Routine Description:
+
+    Free the Irp.
+
+Arguments:
+
+    DeviceObject - device that the completion routine fires on.
+
+    Irp - The irp to be completed.
+
+    Context - IRP context
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION deviceExtension = Context;
+    PIO_STACK_LOCATION      currentStack;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+    {
+        IoMarkIrpPending( Irp );
+    }
+
+    currentStack = IoGetCurrentIrpStackLocation(Irp);
+
+    NT_ASSERT(currentStack->Parameters.Power.Type == DevicePowerState);
+    NT_ASSERT(currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0);
+
+    _Analysis_assume_(deviceExtension != NULL);
+
+    deviceExtension->PowerContext.PowerChangeState.PowerDown++;
+
+    // Step 5. UNLOCK QUEUE
+    if (deviceExtension->PowerContext.Options.LockQueue)
+    {
+        (VOID)DeviceSendPowerDownProcessRequest(deviceExtension,
+                                                   RequestUnlockQueueCompletion,
+                                                   Irp);
+
+        // Let the completion routine complete the Irp
+        return STATUS_MORE_PROCESSING_REQUIRED;
+    }
+
+    // Release the power context if it wasn't already done as part of D0Exit handling
+    if (deviceExtension->PowerContext.InUse)
+    {
+        PowerContextEndUse(deviceExtension);
+    }
+
+    return STATUS_CONTINUE_COMPLETION;
+}
+
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextReuseRequest(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    reset fields for the request.
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    WDF_REQUEST_REUSE_PARAMS reuseParams;
+    PIRP                     irp = NULL;
+
+    RtlZeroMemory(&(DeviceExtension->PowerContext.SenseData), sizeof(DeviceExtension->PowerContext.SenseData));
+    RtlZeroMemory(&(DeviceExtension->PowerContext.Srb), sizeof(DeviceExtension->PowerContext.Srb));
+
+    irp = WdfRequestWdmGetIrp(DeviceExtension->PowerContext.PowerRequest);
+
+    // Re-use the previously created PowerRequest object and format it
+    WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
+    status = WdfRequestReuse(DeviceExtension->PowerContext.PowerRequest, &reuseParams);
+    if (NT_SUCCESS(status))
+    {
+        // This request was preformated during initialization so this call should never fail.
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                                DeviceExtension->PowerContext.PowerRequest,
+                                                                IOCTL_SCSI_EXECUTE_IN,
+                                                                NULL, NULL,
+                                                                NULL, NULL,
+                                                                NULL, NULL);
+
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "PowerContextReuseRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
+                       status));
+        }
+    }
+
+    // Do some basic initialization of the PowerRequest, the rest will be done by the caller
+    // of this function
+    if (NT_SUCCESS(status))
+    {
+        PIO_STACK_LOCATION  nextStack = NULL;
+
+        nextStack = IoGetNextIrpStackLocation(irp);
+
+        nextStack->MajorFunction = IRP_MJ_SCSI;
+        nextStack->Parameters.Scsi.Srb = &(DeviceExtension->PowerContext.Srb);
+
+        DeviceExtension->PowerContext.Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+        DeviceExtension->PowerContext.Srb.OriginalRequest = irp;
+
+        DeviceExtension->PowerContext.Srb.SenseInfoBuffer = &(DeviceExtension->PowerContext.SenseData);
+        DeviceExtension->PowerContext.Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextBeginUse(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    initialize fields in power context
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    NT_ASSERT(!DeviceExtension->PowerContext.InUse);
+
+    DeviceExtension->PowerContext.InUse = TRUE;
+    DeviceExtension->PowerContext.RetryCount = MAXIMUM_RETRIES;
+    DeviceExtension->PowerContext.RetryIntervalIn100ns = 0;
+
+    KeQueryTickCount(&DeviceExtension->PowerContext.StartTime);
+
+    RtlZeroMemory(&(DeviceExtension->PowerContext.Options), sizeof(DeviceExtension->PowerContext.Options));
+    RtlZeroMemory(&(DeviceExtension->PowerContext.PowerChangeState), sizeof(DeviceExtension->PowerContext.PowerChangeState));
+
+    status = PowerContextReuseRequest(DeviceExtension);
+
+    RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest);
+
+    return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PowerContextEndUse(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    inidate that power context using is finished.
+
+Arguments:
+
+    DeviceExtension - device context
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NT_ASSERT(DeviceExtension->PowerContext.InUse);
+
+    DeviceExtension->PowerContext.InUse = FALSE;
+
+    KeQueryTickCount(&DeviceExtension->PowerContext.CompleteTime);
+
+    return STATUS_SUCCESS;
+}
+
+
+_Function_class_(POWER_SETTING_CALLBACK)
+_IRQL_requires_same_
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DevicePowerSettingCallback(
+    _In_ LPCGUID SettingGuid,
+    _In_reads_bytes_(ValueLength) PVOID Value,
+    _In_ ULONG ValueLength,
+    _Inout_opt_ PVOID Context
+    )
+/*++
+Description:
+
+    This function is the callback for power setting notifications (registered
+    when ClasspGetD3IdleTimeout() is called for the first time).
+
+    Currently, this function is used to get the disk idle timeout value from
+    the system power settings.
+
+    This function is guaranteed to be called at PASSIVE_LEVEL.
+
+Arguments:
+
+    SettingGuid - The power setting GUID.
+    Value - Pointer to the power setting value.
+    ValueLength - Size of the Value buffer.
+    Context - The FDO's device extension.
+
+Return Value:
+
+    STATUS_SUCCESS
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION DeviceExtension = Context;
+    MONITOR_DISPLAY_STATE DisplayState;
+
+    PAGED_CODE();
+
+    if (IsEqualGUID(SettingGuid, &GUID_CONSOLE_DISPLAY_STATE)) {
+
+        if ((ValueLength == sizeof(ULONG)) && (Value != NULL)) {
+
+            DisplayState = *((PULONG)Value);
+
+            _Analysis_assume_(DeviceExtension != NULL);
+
+            //
+            // Power setting callbacks are asynchronous so make sure the device
+            // is completely initialized before taking any actions.
+            //
+            if (DeviceExtension->IsInitialized) {
+
+                //
+                // If monitor is off, change media change requests to not keep device active.
+                // This allows the devices to go to sleep if there are no other active requests.
+                //
+
+                if (DisplayState == PowerMonitorOff) {
+
+                    //
+                    // Mark the device inactive so that it can enter a low power state.
+                    //
+
+                    DeviceMarkActive(DeviceExtension, FALSE, TRUE);
+                    SET_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+                }
+                else
+                {
+                    CLEAR_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+                    DeviceMarkActive(DeviceExtension, TRUE, TRUE);
+                }
+            }
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+#pragma warning(pop) // un-sets any local warning changes
+
diff --git a/drivers/storage/class/cdrom_new/scratch.c b/drivers/storage/class/cdrom_new/scratch.c
new file mode 100644 (file)
index 0000000..945b9f7
--- /dev/null
@@ -0,0 +1,1467 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    scratch.c
+
+Abstract:
+
+    Functions for using common scratch buffer
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "ioctl.h"
+#include "scratch.h"
+#include "mmc.h"
+
+#ifdef DEBUG_USE_WPP
+#include "scratch.tmh"
+#endif
+
+// Forward declarations
+EVT_WDF_REQUEST_COMPLETION_ROUTINE  ScratchBuffer_ReadWriteCompletionRoutine;
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, ScratchBuffer_Deallocate)
+#pragma alloc_text(PAGE, ScratchBuffer_Allocate)
+#pragma alloc_text(PAGE, ScratchBuffer_SetupSrb)
+#pragma alloc_text(PAGE, ScratchBuffer_ExecuteCdbEx)
+
+#endif
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScratchBuffer_Deallocate(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    release all resources allocated for scratch.
+
+Arguments:
+
+    DeviceExtension - device extension
+
+Return Value:
+
+    none
+
+--*/
+{
+    PAGED_CODE ();
+
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
+
+    if (DeviceExtension->ScratchContext.ScratchHistory != NULL)
+    {
+        ExFreePool(DeviceExtension->ScratchContext.ScratchHistory);
+        DeviceExtension->ScratchContext.ScratchHistory = NULL;
+    }
+    if (DeviceExtension->ScratchContext.ScratchSense != NULL)
+    {
+        ExFreePool(DeviceExtension->ScratchContext.ScratchSense);
+        DeviceExtension->ScratchContext.ScratchSense = NULL;
+    }
+    if (DeviceExtension->ScratchContext.ScratchSrb != NULL)
+    {
+        ExFreePool(DeviceExtension->ScratchContext.ScratchSrb);
+        DeviceExtension->ScratchContext.ScratchSrb = NULL;
+    }
+    if (DeviceExtension->ScratchContext.ScratchBufferSize != 0)
+    {
+        DeviceExtension->ScratchContext.ScratchBufferSize = 0;
+    }
+    if (DeviceExtension->ScratchContext.ScratchBufferMdl != NULL)
+    {
+        IoFreeMdl(DeviceExtension->ScratchContext.ScratchBufferMdl);
+        DeviceExtension->ScratchContext.ScratchBufferMdl = NULL;
+    }
+    if (DeviceExtension->ScratchContext.ScratchBuffer != NULL)
+    {
+        ExFreePool(DeviceExtension->ScratchContext.ScratchBuffer);
+        DeviceExtension->ScratchContext.ScratchBuffer = NULL;
+    }
+
+    if (DeviceExtension->ScratchContext.PartialMdl != NULL)
+    {
+        IoFreeMdl(DeviceExtension->ScratchContext.PartialMdl);
+        DeviceExtension->ScratchContext.PartialMdl = NULL;
+    }
+
+    if (DeviceExtension->ScratchContext.ScratchRequest != NULL)
+    {
+        PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
+        if (irp->MdlAddress)
+        {
+            irp->MdlAddress = NULL;
+        }
+        WdfObjectDelete(DeviceExtension->ScratchContext.ScratchRequest);
+        DeviceExtension->ScratchContext.ScratchRequest = NULL;
+    }
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+ScratchBuffer_Allocate(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    allocate resources allocated for scratch.
+
+Arguments:
+
+    DeviceExtension - device extension
+
+Return Value:
+
+    none
+
+--*/
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    PAGED_CODE ();
+
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
+
+    // quick-exit if already allocated
+    if ((DeviceExtension->ScratchContext.ScratchBuffer     != NULL) &&
+        (DeviceExtension->ScratchContext.ScratchBufferMdl  != NULL) &&
+        (DeviceExtension->ScratchContext.ScratchBufferSize != 0)    &&
+        (DeviceExtension->ScratchContext.ScratchRequest    != NULL) &&
+        (DeviceExtension->ScratchContext.ScratchSrb        != NULL) &&
+        (DeviceExtension->ScratchContext.ScratchHistory    != NULL) &&
+        (DeviceExtension->ScratchContext.PartialMdl  != NULL)
+        )
+    {
+        return TRUE;
+    }
+
+    // validate max transfer already determined
+    NT_ASSERT(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes != 0);
+
+    // validate no partially-saved state
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer     == NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl  == NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize == 0);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest    == NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl == NULL);
+
+    // limit the scratch buffer to between 4k and 64k (so data length fits into USHORT -- req'd for many commands)
+    DeviceExtension->ScratchContext.ScratchBufferSize = min(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, (64*1024));
+
+    // allocate the buffer
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->ScratchContext.ScratchBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                                              DeviceExtension->ScratchContext.ScratchBufferSize,
+                                                                              CDROM_TAG_SCRATCH);
+        if (DeviceExtension->ScratchContext.ScratchBuffer == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate scratch buffer of %x bytes\n",
+                        DeviceExtension->ScratchContext.ScratchBufferSize
+                        ));
+        }
+        else if (BYTE_OFFSET(DeviceExtension->ScratchContext.ScratchBuffer) != 0)
+        {
+            status = STATUS_INTERNAL_ERROR;
+            TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
+                        "Allocation of %x bytes non-paged pool was not "
+                        "allocated on page boundary?  STATUS_INTERNAL_ERROR\n",
+                        DeviceExtension->ScratchContext.ScratchBufferSize
+                        ));
+        }
+    }
+
+    // allocate the MDL
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->ScratchContext.ScratchBufferMdl = IoAllocateMdl(DeviceExtension->ScratchContext.ScratchBuffer,
+                                                                         DeviceExtension->ScratchContext.ScratchBufferSize,
+                                                                         FALSE, FALSE, NULL);
+        if (DeviceExtension->ScratchContext.ScratchBufferMdl == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate MDL for %x byte buffer\n",
+                        DeviceExtension->ScratchContext.ScratchBufferSize
+                        ));
+        }
+        else
+        {
+            MmBuildMdlForNonPagedPool(DeviceExtension->ScratchContext.ScratchBufferMdl);
+        }
+    }
+
+    // create the request
+    if (NT_SUCCESS(status))
+    {
+        WDF_OBJECT_ATTRIBUTES attributes;
+        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
+                                                CDROM_REQUEST_CONTEXT);
+
+        status =  WdfRequestCreate(&attributes,
+                                   DeviceExtension->IoTarget,
+                                   &DeviceExtension->ScratchContext.ScratchRequest);
+
+        if ((!NT_SUCCESS(status)) ||
+            (DeviceExtension->ScratchContext.ScratchRequest == NULL))
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate scratch MDL \n"));
+        }
+    }
+
+    // allocate the srb
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->ScratchContext.ScratchSrb = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                                           sizeof(SCSI_REQUEST_BLOCK),
+                                                                           CDROM_TAG_SCRATCH);
+
+        if (DeviceExtension->ScratchContext.ScratchSrb == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate scratch SRB\n"));
+        }
+    }
+
+    // allocate the sense buffer
+    if (NT_SUCCESS(status))
+    {
+        DeviceExtension->ScratchContext.ScratchSense = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                                             sizeof(SENSE_DATA),
+                                                                             CDROM_TAG_SCRATCH);
+
+        if (DeviceExtension->ScratchContext.ScratchSense == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate scratch sense data\n"
+                        ));
+        }
+    }
+
+    // allocate the SRB history data
+    if (NT_SUCCESS(status))
+    {
+        size_t allocationSize = sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
+        allocationSize += 20 * sizeof(SRB_HISTORY_ITEM);
+
+        DeviceExtension->ScratchContext.ScratchHistory = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                                                               allocationSize,
+                                                                               CDROM_TAG_SCRATCH);
+        if (DeviceExtension->ScratchContext.ScratchHistory == NULL)
+        {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                        "Failed to allocate scratch history buffer\n"
+                        ));
+        }
+        else
+        {
+            // must be initialized here...
+            RtlZeroMemory(DeviceExtension->ScratchContext.ScratchHistory, allocationSize);
+            DeviceExtension->ScratchContext.ScratchHistory->TotalHistoryCount = 20;
+        }
+    }
+
+    // allocate the MDL
+    if (NT_SUCCESS(status))
+    {
+        ULONG transferLength = 0;
+
+        status = RtlULongAdd(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, PAGE_SIZE, &transferLength);
+        if (NT_SUCCESS(status))
+        {
+            DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE;
+            DeviceExtension->ScratchContext.PartialMdl = IoAllocateMdl(NULL,
+                                                                       transferLength,
+                                                                       FALSE,
+                                                                       FALSE,
+                                                                       NULL);
+            if (DeviceExtension->ScratchContext.PartialMdl == NULL)
+            {
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                            "Failed to allocate MDL for %x byte buffer\n",
+                            DeviceExtension->ScratchContext.ScratchBufferSize
+                            ));
+            }
+            else
+            {
+                NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl->Size >=
+                       (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes) * sizeof(PFN_NUMBER)));
+            }
+        }
+        else
+        {
+            status = STATUS_INTEGER_OVERFLOW;
+        }
+    }
+
+    // cleanup on failure
+    if (!NT_SUCCESS(status))
+    {
+        ScratchBuffer_Deallocate(DeviceExtension);
+    }
+
+    return NT_SUCCESS(status);
+}
+
+
+VOID
+ScratchBuffer_ResetItems(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                    ResetRequestHistory
+    )
+/*++
+
+Routine Description:
+
+    reset scratch items for reuse.
+
+Arguments:
+
+    DeviceExtension - device extension
+    ResetRequestHistory - reset history fields or not
+
+Return Value:
+
+    none
+
+--*/
+{
+    NTSTATUS                 status = STATUS_SUCCESS;
+    WDF_REQUEST_REUSE_PARAMS reuseParams;
+    PIRP                     irp = NULL;
+
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchHistory    != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchSense      != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchSrb        != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest    != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize != 0);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer     != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl  != NULL);
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse      != 0);
+
+    irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
+
+    if (ResetRequestHistory)
+    {
+        PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory;
+        RtlZeroMemory(history->History, sizeof(SRB_HISTORY_ITEM) * history->TotalHistoryCount);
+        history->ClassDriverUse[0] = 0;
+        history->ClassDriverUse[1] = 0;
+        history->ClassDriverUse[2] = 0;
+        history->ClassDriverUse[3] = 0;
+        history->UsedHistoryCount  = 0;
+    }
+
+    // re-use the KMDF request object
+
+    // deassign the MdlAddress, this is the value we assign explicitly.
+    // this is to prevent WdfRequestReuse to release the Mdl unexpectly.
+    if (irp->MdlAddress)
+    {
+        irp->MdlAddress = NULL;
+    }
+
+    WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
+    status = WdfRequestReuse(DeviceExtension->ScratchContext.ScratchRequest, &reuseParams);
+    // WDF request to format the request befor sending it
+    if (NT_SUCCESS(status))
+    {
+        // clean up completion routine.
+        WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest, NULL, NULL);
+
+        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
+                                                                DeviceExtension->ScratchContext.ScratchRequest,
+                                                                IOCTL_SCSI_EXECUTE_IN,
+                                                                NULL, NULL,
+                                                                NULL, NULL,
+                                                                NULL, NULL);
+        if (!NT_SUCCESS(status))
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                       "ScratchBuffer_ResetItems: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
+                       status));
+        }
+    }
+
+    RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSense, sizeof(SENSE_DATA));
+    RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSrb, sizeof(SCSI_REQUEST_BLOCK));
+
+    return;
+}
+
+
+NTSTATUS
+ScratchBuffer_PerformNextReadWrite(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ BOOLEAN                  FirstTry
+    )
+/*++
+
+Routine Description:
+
+    This function asynchronously sends the next read/write SRB down the stack.
+
+Arguments:
+
+    DeviceExtension - Device extension
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
+    PCDROM_REQUEST_CONTEXT              requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
+    WDFREQUEST                          originalRequest = requestContext->OriginalRequest;
+    NTSTATUS                            status = STATUS_SUCCESS;
+
+    ULONG       transferSize;
+    BOOLEAN     usePartialMdl;
+
+    transferSize = min((readWriteContext->EntireXferLen - readWriteContext->TransferedBytes), readWriteContext->MaxLength);
+
+    if (FirstTry)
+    {
+        DeviceExtension->ScratchContext.NumRetries = 0;
+    }
+
+    ScratchBuffer_ResetItems(DeviceExtension, FALSE);
+
+    usePartialMdl = (readWriteContext->PacketsCount > 1 || readWriteContext->TransferedBytes > 0);
+
+    ScratchBuffer_SetupReadWriteSrb(DeviceExtension,
+                                    originalRequest,
+                                    readWriteContext->StartingOffset,
+                                    transferSize,
+                                    readWriteContext->DataBuffer,
+                                    readWriteContext->IsRead,
+                                    usePartialMdl
+                                    );
+
+    WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest,
+            ScratchBuffer_ReadWriteCompletionRoutine, DeviceExtension);
+
+    status = ScratchBuffer_SendSrb(DeviceExtension, FALSE, (FirstTry ? &readWriteContext->SrbHistoryItem : NULL));
+
+    return status;
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+ScratchBuffer_ReadWriteTimerRoutine(
+    struct _KDPC *Dpc,
+    PVOID         DeferredContext,
+    PVOID         SystemArgument1,
+    PVOID         SystemArgument2
+    )
+/*++
+
+Routine Description:
+
+    Timer routine for retrying read and write requests.
+
+Arguments:
+
+    Timer - WDF timer
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION             deviceExtension = NULL;
+    PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = NULL;
+    WDFREQUEST                          originalRequest = NULL;
+    PCDROM_REQUEST_CONTEXT              requestContext = NULL;
+    NTSTATUS                            status = STATUS_SUCCESS;
+    KIRQL                               oldIrql;
+
+    UNREFERENCED_PARAMETER(Dpc);
+    UNREFERENCED_PARAMETER(SystemArgument1);
+    UNREFERENCED_PARAMETER(SystemArgument2);
+
+    if (DeferredContext == NULL)
+    {
+        // This is impossible, but definition of KDEFERRED_ROUTINE allows optional argument,
+        // and thus OACR will complain.
+
+        return;
+    }
+
+    originalRequest = (WDFREQUEST) DeferredContext;
+    requestContext = RequestGetContext(originalRequest);
+
+    KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
+
+    if (!requestContext->ReadWriteIsCompleted)
+    {
+        // As the first step, unregister the cancellation routine
+        status = WdfRequestUnmarkCancelable(originalRequest);
+    }
+    else
+    {
+        status = STATUS_CANCELLED;
+    }
+
+    KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
+
+    if (status != STATUS_CANCELLED)
+    {
+        deviceExtension = requestContext->DeviceExtension;
+        readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
+
+        // We use timer only for retries, that's why the second parameter is always FALSE
+        status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
+
+        if (!NT_SUCCESS(status))
+        {
+            ScratchBuffer_EndUse(deviceExtension);
+            RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
+        }
+    }
+
+    //
+    // Drop the extra reference
+    //
+    WdfObjectDereference(originalRequest);
+}
+
+
+EVT_WDF_REQUEST_CANCEL  ScratchBuffer_ReadWriteEvtRequestCancel;
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+ScratchBuffer_ReadWriteEvtRequestCancel(
+    _In_ WDFREQUEST  Request
+    )
+/*++
+
+Routine Description:
+
+    Cancels a request waiting for the read/write timer to expire. This function does not
+    support cancellation of requests that have already been sent down.
+
+Arguments:
+
+    Request - WDF request
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_REQUEST_CONTEXT             requestContext = RequestGetContext(Request);
+    PCDROM_DEVICE_EXTENSION            deviceExtension = requestContext->DeviceExtension;
+    PCDROM_SCRATCH_READ_WRITE_CONTEXT  readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
+    KIRQL                              oldIrql;
+
+    KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
+
+    if (KeCancelTimer(&requestContext->ReadWriteTimer))
+    {
+       //
+       // Timer is canceled, we own the request.  Drop the reference we took before
+       // queueing the timer.
+       //
+       WdfObjectDereference(Request);
+    }
+    else
+    {
+        //
+        // Timer will run and drop the reference but it won't complete the request
+        // because we set IsCompleted to TRUE
+        //
+    }
+
+    requestContext->ReadWriteIsCompleted = TRUE;
+
+    KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
+
+    ScratchBuffer_EndUse(deviceExtension);
+
+    // If WdfTimerStop returned TRUE, it means this request was scheduled for a retry
+    // and the retry has not happened yet. We just need to cancel it and release the scratch buffer.
+    RequestCompletion(deviceExtension, Request, STATUS_CANCELLED, readWriteContext->TransferedBytes);
+}
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+ScratchBuffer_ReadWriteCompletionRoutine(
+    _In_ WDFREQUEST  Request,
+    _In_ WDFIOTARGET  Target,
+    _In_ PWDF_REQUEST_COMPLETION_PARAMS  Params,
+    _In_ WDFCONTEXT  Context
+    )
+/*++
+
+Routine Description:
+
+    Read/write request completion routine.
+
+Arguments:
+    Request - WDF request
+    Target - The IO target the request was completed by.
+    Params - the request completion parameters
+    Context - context
+
+Return Value:
+
+    none
+
+--*/
+{
+    PCDROM_DEVICE_EXTENSION             deviceExtension = (PCDROM_DEVICE_EXTENSION) Context;
+    PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
+    NTSTATUS                            status = STATUS_SUCCESS;
+    PCDROM_REQUEST_CONTEXT              requestContext = RequestGetContext(deviceExtension->ScratchContext.ScratchRequest);
+    WDFREQUEST                          originalRequest = requestContext->OriginalRequest;
+
+    if (!NT_SUCCESS(WdfRequestGetStatus(Request)))
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+                   "WdfRequestSend: %lx\n",
+                    WdfRequestGetStatus(Request)
+                    ));
+    }
+
+    UNREFERENCED_PARAMETER(Params);
+    UNREFERENCED_PARAMETER(Target);
+
+    // We are not calling ScratchBuffer_BeginUse / ScratchBuffer_EndUse in this function, because we already own
+    // the scratch buffer if this function is being called.
+
+    if ((deviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
+        (deviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
+    {
+        // The request has been cancelled, just need to complete it
+    }
+    else if (SRB_STATUS(deviceExtension->ScratchContext.ScratchSrb->SrbStatus) != SRB_STATUS_SUCCESS)
+    {
+        // The SCSI command that we sent down has failed, retry it if necessary
+        BOOLEAN shouldRetry = TRUE;
+        LONGLONG retryIn100nsUnits = 0;
+
+        shouldRetry = RequestSenseInfoInterpretForScratchBuffer(deviceExtension,
+                                                                deviceExtension->ScratchContext.NumRetries,
+                                                                &status,
+                                                                &retryIn100nsUnits);
+
+        if (shouldRetry)
+        {
+            deviceExtension->ScratchContext.NumRetries++;
+
+            if (retryIn100nsUnits == 0)
+            {
+                // We take a shortcut here by calling ScratchBuffer_PerformNextReadWrite directly:
+                // this helps to avoid unnecessary context switch.
+                status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
+
+                if (NT_SUCCESS(status))
+                {
+                    // We're not done with the request yet, no need to complete it now
+                    return;
+                }
+            }
+            else
+            {
+                PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(originalRequest);
+                KIRQL                  oldIrql;
+
+                //
+                // Initialize the spin lock and timer local to the original request.
+                //
+                if (!originalRequestContext->ReadWriteRetryInitialized)
+                {
+                    KeInitializeSpinLock(&originalRequestContext->ReadWriteCancelSpinLock);
+                    KeInitializeTimer(&originalRequestContext->ReadWriteTimer);
+                    KeInitializeDpc(&originalRequestContext->ReadWriteDpc, ScratchBuffer_ReadWriteTimerRoutine, originalRequest);
+                    originalRequestContext->ReadWriteRetryInitialized = TRUE;
+                }
+
+                KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
+
+                status = WdfRequestMarkCancelableEx(originalRequest, ScratchBuffer_ReadWriteEvtRequestCancel);
+
+                if (status == STATUS_CANCELLED)
+                {
+                    requestContext->ReadWriteIsCompleted = TRUE;
+
+                    KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
+                }
+                else
+                {
+                    LARGE_INTEGER t;
+
+                    t.QuadPart = -retryIn100nsUnits;
+
+                    WdfObjectReference(originalRequest);
+
+                    // Use negative time to indicate that we want a relative delay
+                    KeSetTimer(&originalRequestContext->ReadWriteTimer,
+                               t,
+                               &originalRequestContext->ReadWriteDpc
+                               );
+
+                    KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
+
+                    return;
+                }
+            }
+        }
+    }
+    else
+    {
+        // The SCSI command has succeeded
+        readWriteContext->DataBuffer += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+        readWriteContext->StartingOffset.QuadPart += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+        readWriteContext->TransferedBytes += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
+        readWriteContext->PacketsCount--;
+
+        // Update the SRB history item
+        if (readWriteContext->SrbHistoryItem)
+        {
+            ULONG senseSize;
+
+            // Query the tick count and store in the history
+            KeQueryTickCount(&readWriteContext->SrbHistoryItem->TickCountCompleted);
+
+            // Copy the SRB Status...
+            readWriteContext->SrbHistoryItem->SrbStatus = deviceExtension->ScratchContext.ScratchSrb->SrbStatus;
+
+            // Determine the amount of valid sense data
+            if (deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength >=
+                    RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
+            {
+                PSENSE_DATA sense = (PSENSE_DATA)deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer;
+                senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
+                            sense->AdditionalSenseLength;
+                senseSize = min(senseSize, sizeof(SENSE_DATA));
+            }
+            else
+            {
+                senseSize = deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength;
+            }
+
+            // Normalize the sense data copy in the history
+            RtlZeroMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData), sizeof(SENSE_DATA));
+            RtlCopyMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData),
+                    deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer, senseSize);
+        }
+
+        // Check whether we need to send more SCSI commands to complete the request
+        if (readWriteContext->PacketsCount > 0)
+        {
+            status = ScratchBuffer_PerformNextReadWrite(deviceExtension, TRUE);
+
+            if (NT_SUCCESS(status))
+            {
+                // We're not done with the request yet, no need to complete it now
+                return;
+            }
+        }
+    }
+
+    ScratchBuffer_EndUse(deviceExtension);
+
+
+    RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScratchBuffer_SetupSrb(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ WDFREQUEST             OriginalRequest,
+    _In_ ULONG                      MaximumTransferLength,
+    _In_ BOOLEAN                    GetDataFromDevice
+    )
+/*++
+
+Routine Description:
+
+    setup scratch SRB for sending out.
+
+Arguments:
+
+    DeviceExtension - device extension
+    OriginalRequest - original request delivered by WDF
+    MaximumTransferLength - transfer length
+    GetDataFromDevice - TRUE (get data from device); FALSE (send data to device)
+
+Return Value:
+
+    none
+
+--*/
+{
+    WDFREQUEST              request = DeviceExtension->ScratchContext.ScratchRequest;
+    PIRP                    irp = WdfRequestWdmGetIrp(request);
+    PSCSI_REQUEST_BLOCK     srb = DeviceExtension->ScratchContext.ScratchSrb;
+    PIO_STACK_LOCATION      irpStack = NULL;
+    PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(request);
+
+    PAGED_CODE ();
+
+    requestContext->OriginalRequest = OriginalRequest;
+
+    // set to use the full scratch buffer via the scratch SRB
+    irpStack = IoGetNextIrpStackLocation(irp);
+    irpStack->MajorFunction = IRP_MJ_SCSI;
+    if (MaximumTransferLength == 0)
+    {
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
+    }
+    else if (GetDataFromDevice)
+    {
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+    }
+    else
+    {
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
+    }
+    irpStack->Parameters.Scsi.Srb = srb;
+
+    if (MaximumTransferLength > 0)
+    {
+        // the Irp must show the MDL's address for the transfer
+        irp->MdlAddress = DeviceExtension->ScratchContext.ScratchBufferMdl;
+
+        srb->DataBuffer = DeviceExtension->ScratchContext.ScratchBuffer;
+    }
+
+    // prepare the SRB with default values
+    srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+    srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+    srb->SrbStatus = 0;
+    srb->ScsiStatus = 0;
+    srb->NextSrb = NULL;
+    srb->OriginalRequest = irp;
+    srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+    srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
+
+    srb->CdbLength = 16; // to cause failures if not set correctly -- CD devices limited to 12 bytes for now...
+
+    srb->DataTransferLength = min(DeviceExtension->ScratchContext.ScratchBufferSize, MaximumTransferLength);
+    srb->TimeOutValue = DeviceExtension->TimeOutValue;
+    srb->SrbFlags = DeviceExtension->SrbFlags;
+    SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+    SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+    if (MaximumTransferLength == 0)
+    {
+        SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+    }
+    else if (GetDataFromDevice)
+    {
+        SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+    }
+    else
+    {
+        SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+    }
+}
+
+
+NTSTATUS
+ScratchBuffer_SendSrb(
+    _Inout_     PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_        BOOLEAN                 SynchronousSrb,
+    _When_(SynchronousSrb, _Pre_null_)
+    _When_(!SynchronousSrb, _In_opt_)
+                PSRB_HISTORY_ITEM       *SrbHistoryItem
+    )
+/*++
+
+Routine Description:
+
+    Send the command from the scratch SRB to lower driver and retry if necessary.
+
+Arguments:
+
+    DeviceExtension - device extension
+    SynchronousSrb - indicates whether the SRB needs to be sent synchronously or nor
+    SrbHistoryItem - storage for SRB history item, if this is an asynchronous request
+
+Return Value:
+
+    none
+
+--*/
+{
+    NTSTATUS                 status  = STATUS_SUCCESS;
+    PSCSI_REQUEST_BLOCK      srb = DeviceExtension->ScratchContext.ScratchSrb;
+    PSRB_HISTORY             history = DeviceExtension->ScratchContext.ScratchHistory;
+    PSRB_HISTORY_ITEM        item = NULL;
+    BOOLEAN                  requestCancelled = FALSE;
+
+    srb->InternalStatus = 0;
+    srb->SrbStatus = 0;
+
+    // allocate/update history pre-command, if it is a synchronous request or we were supplied
+    // with a storage for the history item
+    if (SynchronousSrb || SrbHistoryItem != NULL)
+    {
+        // sending a packet implies a new history unit is to be used.
+        NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
+
+        // if already all used up, remove at least one history unit
+        if (history->UsedHistoryCount == history->TotalHistoryCount )
+        {
+            CompressSrbHistoryData(history);
+            NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
+        }
+
+        // thus, since we are about to increment the count, it must now be less...
+        NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
+
+        // increment the number of history units in use
+        history->UsedHistoryCount++;
+
+        // determine index to use
+        item = &( history->History[ history->UsedHistoryCount-1 ] );
+
+        if (SrbHistoryItem != NULL)
+        {
+            *SrbHistoryItem = item;
+        }
+
+        // zero out the history item
+        RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM));
+
+        // Query the tick count and store in the history
+        KeQueryTickCount(&item->TickCountSent);
+    }
+
+    // get cancellation status;
+    {
+        PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
+
+        if (requestContext->OriginalRequest != NULL)
+        {
+            requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
+        }
+    }
+
+    if (!requestCancelled)
+    {
+        status = RequestSend(DeviceExtension,
+                             DeviceExtension->ScratchContext.ScratchRequest,
+                             DeviceExtension->IoTarget,
+                             SynchronousSrb ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0,
+                             NULL);
+
+        // If this is a synchronous request, update the history item immediately, including "normalized" sense data
+        if (SynchronousSrb)
+        {
+            ULONG senseSize;
+
+            // Query the tick count and store in the history
+            KeQueryTickCount(&item->TickCountCompleted);
+
+            // Copy the SRB Status
+            item->SrbStatus = srb->SrbStatus;
+
+            // Determine the amount of valid sense data
+            if (srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
+            {
+                PSENSE_DATA sense = (PSENSE_DATA)srb->SenseInfoBuffer;
+                senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
+                            sense->AdditionalSenseLength;
+                senseSize = min(senseSize, sizeof(SENSE_DATA));
+            }
+            else
+            {
+                senseSize = srb->SenseInfoBufferLength;
+            }
+
+            // Normalize the sense data copy in the history
+            RtlZeroMemory(&(item->NormalizedSenseData), sizeof(SENSE_DATA));
+            RtlCopyMemory(&(item->NormalizedSenseData), srb->SenseInfoBuffer, senseSize);
+        }
+    }
+    else
+    {
+        DeviceExtension->ScratchContext.ScratchSrb->SrbStatus = SRB_STATUS_ABORTED;
+        DeviceExtension->ScratchContext.ScratchSrb->InternalStatus = (ULONG)STATUS_CANCELLED;
+        status = STATUS_CANCELLED;
+    }
+
+    return status;
+}
+
+VOID
+CompressSrbHistoryData(
+    _Inout_  PSRB_HISTORY   RequestHistory
+    )
+/*++
+
+Routine Description:
+
+    compress the SRB history data.
+
+Arguments:
+
+    RequestHistory - SRB history data
+
+Return Value:
+
+    RequestHistory - compressed history data
+
+--*/
+{
+    ULONG i;
+    NT_ASSERT( RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount );
+    ValidateSrbHistoryDataPresumptions(RequestHistory);
+
+    for (i=0; i < RequestHistory->UsedHistoryCount; i++)
+    {
+        // for each item...
+        PSRB_HISTORY_ITEM toMatch = &( RequestHistory->History[i] );
+        // hint: read const qualifiers backwards.  i.e. srbstatus is a const UCHAR
+        // so, "UCHAR const * const x" is read "x is a const pointer to a const UCHAR"
+        // unfortunately, "const UCHAR" is equivalent to "UCHAR const", which causes
+        // people no end of confusion due to its widespread use.
+        UCHAR const srbStatus = toMatch->SrbStatus;
+        UCHAR const sense     = toMatch->NormalizedSenseData.SenseKey;
+        UCHAR const asc       = toMatch->NormalizedSenseData.AdditionalSenseCode;
+        UCHAR const ascq      = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
+        ULONG j;
+
+        // see if there are any at higher indices with identical Sense/ASC/ASCQ
+        for (j = i+1; (toMatch->ClassDriverUse != 0xFF) && (j < RequestHistory->UsedHistoryCount); j++)
+        {
+            PSRB_HISTORY_ITEM found = &( RequestHistory->History[j] );
+            // close enough match?
+            if ((srbStatus == found->SrbStatus) &&
+                (sense     == found->NormalizedSenseData.SenseKey) &&
+                (asc       == found->NormalizedSenseData.AdditionalSenseCode) &&
+                (ascq      == found->NormalizedSenseData.AdditionalSenseCodeQualifier)) {
+
+                // add the fields to keep reasonable track of delay times.
+                if (toMatch->MillisecondsDelayOnRetry + found->MillisecondsDelayOnRetry < toMatch->MillisecondsDelayOnRetry) {
+                    toMatch->MillisecondsDelayOnRetry = MAXULONG;
+                } else {
+                    toMatch->MillisecondsDelayOnRetry += found->MillisecondsDelayOnRetry;
+                }
+
+                // this found item cannot contain any compressed entries because
+                // the first entry with a given set of sense/asc/ascq will always
+                // either be full (0xFF) or be the only partially-full entry with
+                // that sense/asc/ascq.
+                NT_ASSERT(found->ClassDriverUse == 0);
+                // add the counts so we still know how many retries total
+                toMatch->ClassDriverUse++;
+
+
+                // if not the last entry, need to move later entries earlier in the array
+                if (j != RequestHistory->UsedHistoryCount-1) {
+                    // how many entries remain?
+                    SIZE_T remainingBytes = RequestHistory->UsedHistoryCount - 1 - j;
+                    remainingBytes *= sizeof(SRB_HISTORY_ITEM);
+
+                    // note that MOVE is required due to overlapping entries
+                    RtlMoveMemory(found, found+1, remainingBytes);
+
+                    // Finally, decrement the number of used history count and
+                    // decrement j to rescan the current location again
+                    --RequestHistory->UsedHistoryCount;
+                    --j;
+                } // end moving of array elements around
+            } // end of close enough match
+        } // end j loop
+    } // end i loop
+
+    // unable to compress duplicate sense/asc/ascq, so just lose the most recent data
+    if (RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount)
+    {
+        PSRB_HISTORY_ITEM item = &( RequestHistory->History[ RequestHistory->TotalHistoryCount-1 ] );
+        RequestHistory->ClassDriverUse[0] += item->ClassDriverUse; // how many did we "lose"?
+        RequestHistory->UsedHistoryCount--;
+    }
+
+    // finally, zero any that are no longer in use
+    NT_ASSERT( RequestHistory->UsedHistoryCount != RequestHistory->TotalHistoryCount);
+    {
+        SIZE_T bytesToZero = RequestHistory->TotalHistoryCount - RequestHistory->UsedHistoryCount;
+        bytesToZero *= sizeof(SRB_HISTORY_ITEM);
+        RtlZeroMemory(&(RequestHistory->History[RequestHistory->UsedHistoryCount]), bytesToZero);
+    }
+
+    ValidateSrbHistoryDataPresumptions(RequestHistory);
+    return;
+}
+
+VOID
+ValidateSrbHistoryDataPresumptions(
+    _In_     SRB_HISTORY const * RequestHistory
+    )
+{
+#if DBG
+    // validate that all fully-compressed items are before any non-fully-compressed items of any particular sense/asc/ascq
+    // validate that there is at most one partially-compressed item of any particular sense/asc/ascq
+    // validate that all items of any particular sense/asc/ascq that are uncompressed are at the end
+    // THUS: A(255) A(255) A( 40) A(  0) A(  0) is legal for all types with A as sense/asc/ascq
+    //       A(0)   B(255) A(  0) B( 17) B(  0) is also legal because A/B are different types of error
+
+    ULONG i;
+    for (i = 0; i < RequestHistory->UsedHistoryCount; i++)
+    {
+        SRB_HISTORY_ITEM const * toMatch = &( RequestHistory->History[i] );
+        UCHAR const srbStatus = toMatch->SrbStatus;
+        UCHAR const sense     = toMatch->NormalizedSenseData.SenseKey;
+        UCHAR const asc       = toMatch->NormalizedSenseData.AdditionalSenseCode;
+        UCHAR const ascq      = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
+        ULONG j;
+
+        BOOLEAN foundPartiallyCompressedItem =
+            (toMatch->ClassDriverUse !=    0) &&
+            (toMatch->ClassDriverUse != 0xFF) ;
+        BOOLEAN foundUncompressedItem =
+            (toMatch->ClassDriverUse ==    0) ;
+
+        for (j = i+1; j < RequestHistory->UsedHistoryCount; j++)
+        {
+            SRB_HISTORY_ITEM const * found = &( RequestHistory->History[j] );
+            if ((srbStatus == found->SrbStatus) &&
+                (sense     == found->NormalizedSenseData.SenseKey) &&
+                (asc       == found->NormalizedSenseData.AdditionalSenseCode) &&
+                (ascq      == found->NormalizedSenseData.AdditionalSenseCodeQualifier)
+                )
+            {
+                // found a matching type, so validate ordering rules
+                if (foundUncompressedItem && (found->ClassDriverUse != 0))
+                {
+                    DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
+                               "History data has compressed history following uncompressed history "
+                               "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
+                               srbStatus, sense, asc, ascq,
+                               i,i, j,j
+                               );
+                    NT_ASSERT(FALSE);
+                }
+                else if (foundPartiallyCompressedItem && (found->ClassDriverUse == 0xFF))
+                {
+                    DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
+                               "History data has fully compressed history following partially compressed history "
+                               "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
+                               srbStatus, sense, asc, ascq,
+                               i,i, j,j
+                               );
+                    NT_ASSERT(FALSE);
+                }
+
+                // update if we have now found partially compressed and/or uncompressed items
+                if (found->ClassDriverUse == 0)
+                {
+                    foundUncompressedItem = TRUE;
+                }
+                else if (found->ClassDriverUse != 0xFF)
+                {
+                    foundPartiallyCompressedItem = TRUE;
+                }
+            } // end match of (toMatch,found)
+        } // end loop j
+    } // end loop i
+#else
+    UNREFERENCED_PARAMETER(RequestHistory);
+#endif
+    return;
+}
+
+VOID
+ScratchBuffer_SetupReadWriteSrb(
+    _Inout_ PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _In_    WDFREQUEST                  OriginalRequest,
+    _In_    LARGE_INTEGER               StartingOffset,
+    _In_    ULONG                       RequiredLength,
+    _Inout_updates_bytes_(RequiredLength) UCHAR* DataBuffer,
+    _In_    BOOLEAN                     IsReadRequest,
+    _In_    BOOLEAN                     UsePartialMdl
+    )
+/*++
+
+Routine Description:
+
+    setup SRB for read/write request.
+
+Arguments:
+
+    DeviceExtension - device extension
+    OriginalRequest - read/write request
+    StartingOffset - read/write starting offset
+    DataBuffer - buffer for read/write
+    IsReadRequest - TRUE (read); FALSE (write)
+
+Return Value:
+
+    none
+
+--*/
+{
+    //NOTE: R/W request not use the ScratchBuffer, instead, it uses the buffer associated with IRP.
+
+    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
+    PCDB                cdb = (PCDB)srb->Cdb;
+    LARGE_INTEGER       logicalBlockAddr;
+    ULONG               numTransferBlocks;
+
+    PIRP                originalIrp = WdfRequestWdmGetIrp(OriginalRequest);
+
+    PIRP                irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
+    PIO_STACK_LOCATION  irpStack = NULL;
+
+    PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
+
+    requestContext->OriginalRequest = OriginalRequest;
+
+
+    logicalBlockAddr.QuadPart = Int64ShrlMod32(StartingOffset.QuadPart, DeviceExtension->SectorShift);
+    numTransferBlocks = RequiredLength >> DeviceExtension->SectorShift;
+
+    // set to use the full scratch buffer via the scratch SRB
+    irpStack = IoGetNextIrpStackLocation(irp);
+    irpStack->MajorFunction = IRP_MJ_SCSI;
+    if (IsReadRequest)
+    {
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+    }
+    else
+    {
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
+    }
+    irpStack->Parameters.Scsi.Srb = srb;
+
+    // prepare the SRB with default values
+    srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+    srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+    srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+    srb->SrbStatus = 0;
+    srb->ScsiStatus = 0;
+    srb->NextSrb = NULL;
+    srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+    srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
+
+    srb->DataBuffer = DataBuffer;
+    srb->DataTransferLength = RequiredLength;
+
+    srb->QueueSortKey = logicalBlockAddr.LowPart;
+    if (logicalBlockAddr.QuadPart > 0xFFFFFFFF)
+    {
+        //
+        // If the requested LBA is more than max ULONG set the
+        // QueueSortKey to the maximum value, so that these
+        // requests can be added towards the end of the queue.
+        //
+        srb->QueueSortKey = 0xFFFFFFFF;
+    }
+
+    srb->OriginalRequest = irp;
+    srb->TimeOutValue = DeviceExtension->TimeOutValue;
+
+    if (RequestIsRealtimeStreaming(OriginalRequest, IsReadRequest) &&
+        !TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING))
+    {
+        if (IsReadRequest)
+        {
+            RtlZeroMemory(&cdb->READ12, sizeof(cdb->READ12));
+            REVERSE_BYTES(&cdb->READ12.LogicalBlock, &logicalBlockAddr.LowPart);
+            REVERSE_BYTES(&cdb->READ12.TransferLength, &numTransferBlocks);
+            cdb->READ12.Streaming = 1;
+            cdb->READ12.OperationCode = SCSIOP_READ12;
+            srb->CdbLength = sizeof(cdb->READ12);
+        }
+        else
+        {
+            RtlZeroMemory(&cdb->WRITE12, sizeof(cdb->WRITE12));
+            REVERSE_BYTES(&cdb->WRITE12.LogicalBlock, &logicalBlockAddr.LowPart);
+            REVERSE_BYTES(&cdb->WRITE12.TransferLength, &numTransferBlocks);
+            cdb->WRITE12.Streaming = 1;
+            cdb->WRITE12.OperationCode = SCSIOP_WRITE12;
+            srb->CdbLength = sizeof(cdb->WRITE12);
+        }
+    }
+    else
+    {
+        RtlZeroMemory(&cdb->CDB10, sizeof(cdb->CDB10));
+        cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3;
+        cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2;
+        cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1;
+        cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0;
+        cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
+        cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
+        cdb->CDB10.OperationCode = (IsReadRequest) ? SCSIOP_READ : SCSIOP_WRITE;
+        srb->CdbLength = sizeof(cdb->CDB10);
+    }
+
+    //  Set SRB and IRP flags
+    srb->SrbFlags = DeviceExtension->SrbFlags;
+    if (TEST_FLAG(originalIrp->Flags, IRP_PAGING_IO) ||
+        TEST_FLAG(originalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO))
+    {
+        SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
+    }
+
+    SET_FLAG(srb->SrbFlags, (IsReadRequest) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
+    SET_FLAG(srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
+
+    //
+    // If the request is not split, we can use the original IRP MDL.  If the
+    // request needs to be split, we need to use a partial MDL.  The partial MDL
+    // is needed because more than one driver might be mapping the same MDL
+    // and this causes problems.
+    //
+    if (UsePartialMdl == FALSE)
+    {
+        irp->MdlAddress = originalIrp->MdlAddress;
+    }
+    else
+    {
+        if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE)
+        {
+            MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl);
+        }
+
+        IoBuildPartialMdl(originalIrp->MdlAddress, DeviceExtension->ScratchContext.PartialMdl, srb->DataBuffer, srb->DataTransferLength);
+        DeviceExtension->ScratchContext.PartialMdlIsBuilt = TRUE;
+        irp->MdlAddress = DeviceExtension->ScratchContext.PartialMdl;
+    }
+
+    //DBGLOGSENDPACKET(Pkt);
+    //HISTORYLOGSENDPACKET(Pkt);
+
+    //
+    // Set the original irp here for SFIO.
+    //
+    srb->SrbExtension = (PVOID)(originalIrp);
+
+    return;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ScratchBuffer_ExecuteCdbEx(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ WDFREQUEST             OriginalRequest,
+    _In_ ULONG                      TransferSize,
+    _In_ BOOLEAN                    GetDataFromDevice,
+    _In_ PCDB                       Cdb,
+    _In_ UCHAR                      OprationLength,
+    _In_ ULONG                      TimeoutValue
+    )
+/*++
+
+Routine Description:
+
+    Use Scratch buffer to send the Cdb, check error and retry if necessary.
+
+Arguments:
+
+    DeviceExtension - device context
+    OriginalRequest - original request that requires this CDB operation
+    TransferSize - Data transfer size required
+    GetFromDevice - TRUE if getting data from device.
+    Cdb - SCSI command
+    OprationLength - SCSI command length: 6, 10 or 12
+    TimeoutValue - if > 0, use it as timeout value for command
+                   if 0, use the default device timeout value
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
+    PCDB                cdb = (PCDB)(srb->Cdb);
+
+    BOOLEAN             shouldRetry = TRUE;
+    ULONG               timesAlreadyRetried = 0;
+    LONGLONG            retryIn100nsUnits = 0;
+
+    PAGED_CODE ();
+
+    while (shouldRetry)
+    {
+        ScratchBuffer_SetupSrb(DeviceExtension, OriginalRequest, TransferSize, GetDataFromDevice);
+
+        // Set up the SRB/CDB
+        RtlCopyMemory(cdb, Cdb, sizeof(CDB));
+
+        srb->CdbLength = OprationLength;
+
+        if (TimeoutValue > 0)
+        {
+            srb->TimeOutValue = TimeoutValue;
+        }
+
+        ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
+
+        if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
+            (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
+        {
+            shouldRetry = FALSE;
+            status = STATUS_CANCELLED;
+        }
+        else
+        {
+            shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
+                                                                    timesAlreadyRetried,
+                                                                    &status,
+                                                                    &retryIn100nsUnits);
+            if (shouldRetry)
+            {
+                LARGE_INTEGER t;
+                t.QuadPart = -retryIn100nsUnits;
+                timesAlreadyRetried++;
+                KeDelayExecutionThread(KernelMode, FALSE, &t);
+                // keep items clean
+                ScratchBuffer_ResetItems(DeviceExtension, FALSE);
+            }
+        }
+    }
+
+    return status;
+}
+
+
diff --git a/drivers/storage/class/cdrom_new/scratch.h b/drivers/storage/class/cdrom_new/scratch.h
new file mode 100644 (file)
index 0000000..92d7532
--- /dev/null
@@ -0,0 +1,187 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    mmc.h
+
+Abstract:
+
+    Functions/macros for using the scratch buffer and scratch request/SRB
+    in the serial I/O queue context
+
+Author:
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __SCRATCH_H__
+#define __SCRATCH_H__
+
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScratchBuffer_Deallocate(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+BOOLEAN
+ScratchBuffer_Allocate(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    );
+
+VOID
+ScratchBuffer_ResetItems(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 ResetRequestHistory
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+ScratchBuffer_SetupSrb(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ WDFREQUEST             OriginalRequest,
+    _In_ ULONG                      MaximumTransferLength,
+    _In_ BOOLEAN                    GetDataFromDevice
+    );
+
+VOID
+ScratchBuffer_SetupReadWriteSrb(
+    _Inout_ PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _In_    WDFREQUEST                  OriginalRequest,
+    _In_    LARGE_INTEGER               StartingOffset,
+    _In_    ULONG                       RequiredLength,
+    _Inout_updates_bytes_(RequiredLength) UCHAR* DataBuffer,
+    _In_    BOOLEAN                     IsReadRequest,
+    _In_    BOOLEAN                     UsePartialMdl
+    );
+
+NTSTATUS
+ScratchBuffer_SendSrb(
+    _Inout_     PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_        BOOLEAN                 SynchronousSrb,
+    _When_(SynchronousSrb, _Pre_null_)
+    _When_(!SynchronousSrb, _In_opt_)
+                PSRB_HISTORY_ITEM       *SrbHistoryItem
+    );
+
+NTSTATUS
+ScratchBuffer_PerformNextReadWrite(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ BOOLEAN                  FirstTry
+    );
+
+#if DBG
+    #define ScratchBuffer_BeginUse(context) ScratchBuffer_BeginUseX((context), __FILE__, __LINE__)
+#else
+    #define ScratchBuffer_BeginUse(context) ScratchBuffer_BeginUseX((context), NULL, (ULONG)-1)
+#endif
+
+__inline VOID ScratchBuffer_BeginUseX(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ LPCSTR File, ULONG Line)
+{
+    // NOTE: these are not "real" locks.  They are simply to help
+    //       avoid multiple uses of the scratch buffer. Thus, it
+    //       is not critical to have atomic operations here.
+    PVOID tmp = InterlockedCompareExchangePointer((PVOID)&(DeviceExtension->ScratchContext.ScratchInUse), (PVOID)-1, NULL);
+    NT_ASSERT(tmp == NULL);
+    UNREFERENCED_PARAMETER(tmp); //defensive coding, avoid PREFAST warning.
+    DeviceExtension->ScratchContext.ScratchInUseFileName = File;
+    DeviceExtension->ScratchContext.ScratchInUseLineNumber = Line;
+    ScratchBuffer_ResetItems(DeviceExtension, TRUE);
+    RequestClearSendTime(DeviceExtension->ScratchContext.ScratchRequest);
+    return;
+}
+__inline VOID ScratchBuffer_EndUse(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension)
+{
+    // NOTE: these are not "real" locks.  They are simply to help
+    //       avoid multiple uses of the scratch buffer.  Thus, it
+    //       is not critical to have atomic operations here.
+
+    // On lock release, we erase ScratchInUseFileName and ScratchInUseLineNumber _before_ releasing ScratchInUse,
+    // because otherwise we may erase these after the lock has been acquired again by another thread. We store the
+    // old values of ScratchInUseFileName and ScratchInUseLineNumber in local variables to facilitate debugging,
+    // if the ASSERT at the end of the function is hit.
+    PCSTR  scratchInUseFileName;
+    ULONG  scratchInUseLineNumber;
+    PVOID  tmp;
+
+    scratchInUseFileName = DeviceExtension->ScratchContext.ScratchInUseFileName;
+    scratchInUseLineNumber = DeviceExtension->ScratchContext.ScratchInUseLineNumber;
+    UNREFERENCED_PARAMETER(scratchInUseFileName);
+    UNREFERENCED_PARAMETER(scratchInUseLineNumber);
+    DeviceExtension->ScratchContext.ScratchInUseFileName = NULL;
+    DeviceExtension->ScratchContext.ScratchInUseLineNumber = 0;
+
+    //
+    // If we have used the PartialMdl in the scratch context we should notify MM that we will be reusing it
+    // otherwise it may leak System VA if some one below us has mapped the same.
+    //
+
+    if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE)
+    {
+        MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl);
+        DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE;
+    }
+
+    tmp = InterlockedCompareExchangePointer((PVOID)&(DeviceExtension->ScratchContext.ScratchInUse), NULL, (PVOID)-1);
+    NT_ASSERT(tmp == ((PVOID)-1));
+    UNREFERENCED_PARAMETER(tmp); //defensive coding, avoid PREFAST warning.
+    return;
+}
+
+VOID
+CompressSrbHistoryData(
+    _Inout_  PSRB_HISTORY   RequestHistory
+    );
+
+VOID
+ValidateSrbHistoryDataPresumptions(
+    _In_     SRB_HISTORY const* RequestHistory
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+ScratchBuffer_ExecuteCdbEx(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ WDFREQUEST             OriginalRequest,
+    _In_ ULONG                      TransferSize,
+    _In_ BOOLEAN                    GetDataFromDevice,
+    _In_ PCDB                       Cdb,
+    _In_ UCHAR                      OprationLength,
+    _In_ ULONG                      TimeoutValue
+    );
+
+_IRQL_requires_max_(APC_LEVEL)
+__inline
+NTSTATUS
+ScratchBuffer_ExecuteCdb(
+    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_opt_ WDFREQUEST             OriginalRequest,
+    _In_ ULONG                      TransferSize,
+    _In_ BOOLEAN                    GetDataFromDevice,
+    _In_ PCDB                       Cdb,
+    _In_ UCHAR                      OprationLength
+    )
+{
+    return ScratchBuffer_ExecuteCdbEx(DeviceExtension,
+                                      OriginalRequest,
+                                      TransferSize,
+                                      GetDataFromDevice,
+                                      Cdb,
+                                      OprationLength,
+                                      0);
+}
+
+KDEFERRED_ROUTINE ScratchBuffer_ReadWriteTimerRoutine;
+
+#endif //__SCRATCH_H__
diff --git a/drivers/storage/class/cdrom_new/sense.c b/drivers/storage/class/cdrom_new/sense.c
new file mode 100644 (file)
index 0000000..7033c6c
--- /dev/null
@@ -0,0 +1,2600 @@
+/*--
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    sense.c
+
+Abstract:
+
+    This file contains the methods needed to accurately
+    determine how to retry requests on CDROM device types.
+
+Environment:
+
+    kernel mode only
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "string.h"
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "cdrom.h"
+#include "ntstrsafe.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "sense.tmh"
+#endif
+
+// Forward declarations
+VOID
+SenseInfoInterpretRefineByIoControl(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      ULONG                     IoControlCode,
+    _In_      BOOLEAN                   OverrideVerifyVolume,
+    _Inout_   BOOLEAN*                  Retry,
+    _Inout_   NTSTATUS*                 Status
+    );
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, SenseInfoInterpretRefineByIoControl)
+
+#endif
+
+
+//
+// FROM CLASSPNP\CLASSP.H
+//  Lots of retries of synchronized SCSI commands that devices may not
+//  even support really slows down the system (especially while booting).
+//  (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
+//  If a disk cannot return a small initialization buffer at startup
+//  in two attempts (with delay interval) then we cannot expect it to return
+//  data consistently with four retries.
+//  So don't set the retry counts as high here as for data SRBs.
+//
+//  If we find that these requests are failing consecutively,
+//  despite the retry interval, on otherwise reliable media,
+//  then we should either increase the retry interval for
+//  that failure or (by all means) increase these retry counts as appropriate.
+//
+
+#define TOTAL_COUNT_RETRY_DEFAULT       4
+#define TOTAL_COUNT_RETRY_LOCK_MEDIA    1
+#define TOTAL_COUNT_RETRY_MODESENSE     1
+#define TOTAL_COUNT_RETRY_READ_CAPACITY 1
+
+
+#define TOTAL_SECONDS_RETRY_TIME_WRITE          160
+#define TOTAL_SECONDS_RETRY_TIME_MEDIUM_REMOVAL 120
+
+typedef struct _ERROR_LOG_CONTEXT {
+    BOOLEAN     LogError;
+    BOOLEAN     ErrorUnhandled;
+    NTSTATUS    ErrorCode;
+    ULONG       UniqueErrorValue;
+    ULONG       BadSector;
+} ERROR_LOG_CONTEXT, *PERROR_LOG_CONTEXT;
+
+NTSTATUS
+DeviceErrorHandlerForMmc(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb,
+    _Inout_ PNTSTATUS             Status,
+    _Inout_ PBOOLEAN              Retry
+    )
+/*++
+
+Routine Description:
+
+    this routine will be used for error handler for all MMC devices.
+    it's invoked by DeviceErrorHandler()that invoked by SenseInfoInterpret() or GESN
+
+    This routine just checks for media change sense/asc/ascq and
+    also for other events, such as bus resets.  this is used to
+    determine if the device behaviour has changed, to allow for
+    read and write operations to be allowed and/or disallowed.
+
+Arguments:
+
+    DeviceExtension - device context
+    Srb - SRB structure for analyze
+
+Return Value:
+
+    NTSTATUS
+    Status - 
+    Retry - 
+
+--*/
+{
+    BOOLEAN mediaChange = FALSE;
+    PCDB    cdb = (PCDB)Srb->Cdb;
+
+    if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) 
+    {
+        PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
+
+        // the following sense keys could indicate a change in capabilities.
+
+        // we used to expect this to be serialized, and only hit from our
+        // own routine. we now allow some requests to continue during our
+        // processing of the capabilities update in order to allow
+        // IoReadPartitionTable() to succeed.
+        switch (senseBuffer->SenseKey & 0xf) 
+        {
+
+        case SCSI_SENSE_NOT_READY: 
+        {
+            if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) 
+            {
+                if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed) 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: media removed, writes will be "
+                               "failed until new media detected\n"));
+                }
+
+                // NOTE - REF #0002
+                DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+            } 
+            else if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) 
+            {
+                if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_BECOMING_READY) 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: media becoming ready, "
+                               "SHOULD notify shell of change time by sending "
+                               "GESN request immediately!\n"));
+                } 
+                else if (((senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_OPERATION_IN_PROGRESS) ||
+                            (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
+                            ) &&
+                           ((Srb->Cdb[0] == SCSIOP_READ) ||
+                            (Srb->Cdb[0] == SCSIOP_READ6) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_CAPACITY) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_CD) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_CD_MSF) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_TOC) ||
+                            (Srb->Cdb[0] == SCSIOP_WRITE) ||
+                            (Srb->Cdb[0] == SCSIOP_WRITE6) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_TRACK_INFORMATION) ||
+                            (Srb->Cdb[0] == SCSIOP_READ_DISK_INFORMATION)
+                            )
+                           ) 
+                {
+                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: LONG_WRITE or "
+                               "OP_IN_PROGRESS for limited subset of cmds -- "
+                               "setting retry to TRUE\n"));
+                    *Retry = TRUE;
+                    *Status = STATUS_DEVICE_BUSY;
+                }
+            }
+            break;
+        } // end SCSI_SENSE_NOT_READY
+
+        case SCSI_SENSE_UNIT_ATTENTION: 
+        {
+            switch (senseBuffer->AdditionalSenseCode) 
+            {
+            case SCSI_ADSENSE_MEDIUM_CHANGED: 
+            {
+                // always update if the medium may have changed
+
+                // NOTE - REF #0002
+                DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+                DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
+
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                           "DeviceErrorHandlerForMmc: media change detected, need to "
+                           "update drive capabilities\n"));
+                mediaChange = TRUE;
+                break;
+
+            } // end SCSI_ADSENSE_MEDIUM_CHANGED
+
+            case SCSI_ADSENSE_BUS_RESET:
+            {
+                // NOTE - REF #0002
+                DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+                DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
+
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                           "DeviceErrorHandlerForMmc: bus reset detected, need to "
+                           "update drive capabilities\n"));
+                break;
+
+            } // end SCSI_ADSENSE_BUS_RESET
+
+            case SCSI_ADSENSE_OPERATOR_REQUEST: 
+            {
+
+                BOOLEAN b = FALSE;
+
+                switch (senseBuffer->AdditionalSenseCodeQualifier)
+                {
+                case SCSI_SENSEQ_MEDIUM_REMOVAL: 
+                {
+                    // eject notification currently handled by classpnp
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: Eject requested by user\n"));
+                    *Retry = TRUE;
+                    *Status = STATUS_DEVICE_BUSY;
+                    break;
+                }
+
+                case SCSI_SENSEQ_WRITE_PROTECT_DISABLE:
+                    b = TRUE;
+                case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: Write protect %s requested "
+                               "by user\n",
+                               (b ? "disable" : "enable")));
+                    *Retry = TRUE;
+                    *Status = STATUS_DEVICE_BUSY;
+                    // NOTE - REF #0002
+                    DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+                    DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
+
+                    break;
+                }
+
+                } // end of AdditionalSenseCodeQualifier switch
+
+
+                break;
+
+            } // end SCSI_ADSENSE_OPERATOR_REQUEST
+
+            default: 
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                           "DeviceErrorHandlerForMmc: Unit attention %02x/%02x\n",
+                           senseBuffer->AdditionalSenseCode,
+                           senseBuffer->AdditionalSenseCodeQualifier));
+                break;
+            }
+
+            } // end of AdditionSenseCode switch
+            break;
+
+        } // end SCSI_SENSE_UNIT_ATTENTION
+
+        case SCSI_SENSE_ILLEGAL_REQUEST: 
+        {
+            if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_WRITE_PROTECT) 
+            {
+                if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
+                {
+                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                               "DeviceErrorHandlerForMmc: media was writable, but "
+                               "failed request with WRITE_PROTECT error...\n"));
+                }
+                // NOTE - REF #0002
+                // do not update all the capabilities just because
+                // we can't write to the disc.
+                DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
+            }
+            break;
+        } // end SCSI_SENSE_ILLEGAL_REQUEST
+
+        } // end of SenseKey switch
+
+        // Check if we failed to set the DVD region key and send appropriate error
+        if (cdb->CDB16.OperationCode == SCSIOP_SEND_KEY) 
+        {
+            if (cdb->SEND_KEY.KeyFormat == DvdSetRpcKey) 
+            {
+                if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) 
+                {
+                    // media of appropriate region required
+                    *Status = STATUS_NO_MEDIA_IN_DEVICE;
+                    *Retry = FALSE;
+                } 
+                else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
+                           (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_COPY_PROTECTION_FAILURE) &&
+                           (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT)) 
+                {
+                    // media of appropriate region required
+                    *Status = STATUS_CSS_REGION_MISMATCH;
+                    *Retry = FALSE;
+                } 
+                else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
+                           (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA) &&
+                           (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INCOMPATIBLE_FORMAT)) 
+                {
+                    // media of appropriate region required
+                    *Status = STATUS_CSS_REGION_MISMATCH;
+                    *Retry = FALSE;
+                }
+            }
+        }
+    } // end of SRB_STATUS_AUTOSENSE_VALID
+
+    // On media change, if device speed should be reset to default then
+    // queue a workitem to send the commands to the device. Do this on
+    // media arrival as some device will fail this command if no media
+    // is present. Ignore the fake media change from classpnp driver.
+    if ((mediaChange == TRUE) && (*Status != STATUS_MEDIA_CHANGED)) 
+    {
+        if (DeviceExtension->DeviceAdditionalData.RestoreDefaults == TRUE) 
+        {
+            NTSTATUS                status = STATUS_SUCCESS;
+            WDF_OBJECT_ATTRIBUTES   attributes;
+            WDF_WORKITEM_CONFIG     workitemConfig;
+            WDFWORKITEM             workItem;
+
+            WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+            attributes.ParentObject = DeviceExtension->Device;
+
+            WDF_WORKITEM_CONFIG_INIT(&workitemConfig,
+                                     DeviceRestoreDefaultSpeed);
+
+            status = WdfWorkItemCreate(&workitemConfig,
+                                       &attributes,
+                                       &workItem);
+            if (!NT_SUCCESS(status))
+            {
+                return STATUS_SUCCESS;
+            }
+
+            WdfWorkItemEnqueue(workItem);
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                        "DeviceErrorHandlerForMmc: Restore device default speed for %p\n",
+                        DeviceExtension->DeviceObject));
+        }
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DeviceErrorHandlerForHitachiGD2000(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
+    _In_ PSCSI_REQUEST_BLOCK      Srb,
+    _Inout_ PNTSTATUS             Status,
+    _Inout_ PBOOLEAN              Retry
+    )
+/*++
+
+Routine Description:
+
+   error handler for HITACHI CDR-1750S, CDR-3650/1650S
+
+   This routine checks the type of error.  If the error suggests that the
+   drive has spun down and cannot reinitialize itself, send a
+   START_UNIT or READ to the device.  This will force the drive to spin
+   up.  This drive also loses the AGIDs it has granted when it spins down,
+   which may result in playback failure the first time around.
+
+Arguments:
+
+    DeviceExtension - the device object.
+
+    Srb - Supplies a pointer to the failing Srb.
+
+    Status - return the final status for this command?
+
+    Retry - return if the command should be retried.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
+
+    if (!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) 
+    {
+        return STATUS_SUCCESS; //nobody cares about this return value yet.
+    }
+
+    if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_HARDWARE_ERROR) &&
+        (senseBuffer->AdditionalSenseCode == 0x44)) 
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                    "DeviceErrorHandlerForHitachiGD2000 (%p) => Internal Target "
+                    "Failure Detected -- spinning up drive\n", DeviceExtension->Device));
+
+        // the request should be retried because the device isn't ready
+        *Retry = TRUE;
+        *Status = STATUS_DEVICE_NOT_READY;
+
+        // send a START_STOP unit to spin up the drive
+        // NOTE: this temporarily violates the StartIo serialization
+        //       mechanism, but the completion routine on this will NOT
+        //       call StartNextPacket(), so it's a temporary disruption
+        //       of the serialization only.
+        DeviceSendStartUnit(DeviceExtension->Device);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+VOID
+SenseInfoRequestGetInformation(
+    _In_  WDFREQUEST  Request,
+    _Out_ UCHAR*      MajorFunctionCode,
+    _Out_ ULONG*      IoControlCode,
+    _Out_ BOOLEAN*    OverrideVerifyVolume,
+    _Out_ ULONGLONG*  Total100nsSinceFirstSend
+    )
+{
+    PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+
+    *MajorFunctionCode = 0;
+    *IoControlCode = 0;
+    *OverrideVerifyVolume = FALSE;
+    *Total100nsSinceFirstSend = 0;
+
+    if (requestContext->OriginalRequest != NULL)
+    {
+        PIO_STACK_LOCATION originalIrpStack = NULL;
+        
+        PIRP originalIrp = WdfRequestWdmGetIrp(requestContext->OriginalRequest);
+
+        if (originalIrp != NULL)
+        {
+            originalIrpStack = IoGetCurrentIrpStackLocation(originalIrp);
+        }
+
+        if (originalIrpStack != NULL)
+        {
+            *MajorFunctionCode = originalIrpStack->MajorFunction;
+
+            if (*MajorFunctionCode == IRP_MJ_DEVICE_CONTROL)
+            {
+                *IoControlCode = originalIrpStack->Parameters.DeviceIoControl.IoControlCode;
+            }
+
+            *OverrideVerifyVolume = TEST_FLAG(originalIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
+        }
+    }
+
+    // Calculate time past since the request was first time sent.
+    if (requestContext->TimeSentDownFirstTime.QuadPart > 0)
+    {
+        LARGE_INTEGER tmp;
+        KeQueryTickCount(&tmp);
+        tmp.QuadPart -= requestContext->TimeSentDownFirstTime.QuadPart;
+        tmp.QuadPart *= KeQueryTimeIncrement();
+        *Total100nsSinceFirstSend = tmp.QuadPart;
+    }
+    else
+    {
+        // set to -1 if field TimeSentDownFirstTime not set.
+        *Total100nsSinceFirstSend = (ULONGLONG) -1;
+    }
+
+    return;
+}
+
+BOOLEAN
+SenseInfoInterpretByAdditionalSenseCode(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      PSCSI_REQUEST_BLOCK       Srb,
+    _In_      UCHAR                     AdditionalSenseCode,
+    _In_      UCHAR                     AdditionalSenseCodeQual,
+    _Inout_   NTSTATUS*                 Status,
+    _Inout_   BOOLEAN*                  Retry,
+    _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,  
+    _Inout_   PERROR_LOG_CONTEXT        LogContext
+    )
+/*
+    This function will interpret error based on ASC/ASCQ. 
+
+    If the error code is not processed in this function, e.g. return value is TRUE, 
+    caller needs to call SenseInfoInterpretBySenseKey() for further interpret.
+*/
+{
+    BOOLEAN     needFurtherInterpret = TRUE;
+    PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+
+    // set default values for retry fields.
+    *Status = STATUS_IO_DEVICE_ERROR;
+    *Retry = TRUE;
+    *RetryIntervalInSeconds = 0;
+
+    switch (AdditionalSenseCode) 
+    {
+    case SCSI_ADSENSE_LUN_NOT_READY: 
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, 
+                        "SenseInfoInterpretByAdditionalSenseCode: Lun not ready\n"));
+
+            //
+            //  Many non-WHQL certified drives (mostly CD-RW) return
+            //  2/4/0 when they have no media instead of the obvious choice of:
+            //
+            //      SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
+            //
+            //  These drives should not pass WHQL certification due to this discrepency.
+            //
+            //  However, we have to retry on 2/4/0 (Not ready, LUN not ready, no info) 
+            //  and also 3/2/0 (no seek complete).
+            //
+            //  These conditions occur when the shell tries to examine an
+            //  injected CD (e.g. for autoplay) before the CD is spun up.
+            //
+            //  The drive should be returning an ASCQ of SCSI_SENSEQ_BECOMING_READY
+            //  (0x01) in order to comply with WHQL standards.
+            //
+            //  The default retry timeout of one second is acceptable to balance
+            //  these discrepencies.  don't modify the status, though....
+            //
+            
+            switch (AdditionalSenseCodeQual) 
+            {
+            case SCSI_SENSEQ_OPERATION_IN_PROGRESS: 
+                {
+                    DEVICE_EVENT_BECOMING_READY notReady = {0};
+
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                                "SenseInfoInterpretByAdditionalSenseCode: Operation In Progress\n"));
+
+                    needFurtherInterpret = FALSE;
+            
+                    *Retry = TRUE;
+                    *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
+                    *Status = STATUS_DEVICE_NOT_READY;
+
+                    notReady.Version = 1;
+                    notReady.Reason = 2;
+                    notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
+                    DeviceSendNotification(DeviceExtension,
+                                           &GUID_IO_DEVICE_BECOMING_READY,
+                                           sizeof(DEVICE_EVENT_BECOMING_READY),
+                                           &notReady);
+
+                    break;
+                }
+
+            case SCSI_SENSEQ_BECOMING_READY: 
+                {
+                    DEVICE_EVENT_BECOMING_READY notReady = {0};
+
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                                "SenseInfoInterpretByAdditionalSenseCode: In process of becoming ready\n"));
+
+                    needFurtherInterpret = FALSE;
+            
+                    *Retry = TRUE;
+                    *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
+                    *Status = STATUS_DEVICE_NOT_READY;
+
+                    notReady.Version = 1;
+                    notReady.Reason = 1;
+                    notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
+                    DeviceSendNotification(DeviceExtension,
+                                           &GUID_IO_DEVICE_BECOMING_READY,
+                                           sizeof(DEVICE_EVENT_BECOMING_READY),
+                                           &notReady);
+
+                    break;
+                }
+
+            case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                                "SenseInfoInterpretByAdditionalSenseCode: Long write in progress\n"));
+
+                    needFurtherInterpret = FALSE;
+            
+                    // This has been seen as a transcient failure on some drives
+                    *Status = STATUS_DEVICE_NOT_READY;
+                    *Retry = TRUE;
+                    // Set retry interval to be 0 as the drive can be ready at anytime.
+                    *RetryIntervalInSeconds = 0;
+
+                    break;
+                }
+
+            case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                                "SenseInfoInterpretByAdditionalSenseCode: Manual intervention required\n"));
+
+                    needFurtherInterpret = FALSE;
+
+                    *Status = STATUS_NO_MEDIA_IN_DEVICE;
+                    *Retry = FALSE;
+                    *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
+
+                    break;
+                }
+
+            case SCSI_SENSEQ_FORMAT_IN_PROGRESS: 
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                                "SenseInfoInterpretByAdditionalSenseCode: Format in progress\n"));
+            
+                    needFurtherInterpret = FALSE;
+
+                    *Status = STATUS_DEVICE_NOT_READY;
+                    *Retry = FALSE;
+                    *RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
+
+                    break;
+                }
+
+            case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: 
+            case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
+            default:
+                {
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                                "SenseInfoInterpretByAdditionalSenseCode: Initializing command required\n"));
+
+                    needFurtherInterpret = FALSE;
+
+                    *Status = STATUS_DEVICE_NOT_READY;
+                    *Retry = TRUE;
+                    *RetryIntervalInSeconds = 0;
+
+                    // This sense code/additional sense code combination may indicate 
+                    // that the device needs to be started. 
+                    if (TEST_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
+                        !TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY))
+                    {
+                        DeviceSendStartUnit(DeviceExtension->Device);
+                    }
+
+                    break;
+                }
+            } // end switch (AdditionalSenseCodeQual)
+            break;
+
+        } // end case (SCSI_ADSENSE_LUN_NOT_READY)
+
+    case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: 
+        {
+            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, 
+                        "SenseInfoInterpretByAdditionalSenseCode: No Media in device.\n"));
+
+            needFurtherInterpret = FALSE;
+
+            *Status = STATUS_NO_MEDIA_IN_DEVICE;
+            *Retry = FALSE;
+
+            if (AdditionalSenseCodeQual == 0xCC)
+            {
+                //  The IMAPIv1 filter returns this ASCQ value while it is burning CD media, and we want
+                //  to preserve this functionality for compatibility reasons.
+                //  We want to indicate that the media is not present to most applications;
+                //  but RSM has to know that the media is still in the drive (i.e. the drive is not free).
+                DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, NULL);
+            }
+            else 
+            {
+                DeviceSetMediaChangeStateEx(DeviceExtension, MediaNotPresent, NULL);
+            }
+
+            break;
+        } // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
+
+    case SCSI_ADSENSE_INVALID_MEDIA: 
+        {
+        switch (AdditionalSenseCodeQual) 
+        {
+
+        case SCSI_SENSEQ_UNKNOWN_FORMAT: 
+            {
+                needFurtherInterpret = FALSE;
+            
+                // Log error only if this is a paging request
+                *Status = STATUS_UNRECOGNIZED_MEDIA;
+                *Retry = FALSE;
+
+                LogContext->LogError = TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
+                LogContext->UniqueErrorValue = 256;
+                LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
+
+                break;
+            }
+
+        case SCSI_SENSEQ_INCOMPATIBLE_FORMAT: 
+            {
+                needFurtherInterpret = FALSE;
+            
+                *Status = STATUS_UNRECOGNIZED_MEDIA;
+                *Retry = FALSE;
+
+                LogContext->LogError = FALSE;
+
+                break;
+            }   
+
+        case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED:
+            {
+                needFurtherInterpret = FALSE;
+            
+                *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
+                *Retry = FALSE;
+
+                LogContext->LogError = FALSE;
+                LogContext->UniqueErrorValue = 256;
+                LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
+                break;
+            }
+
+        default:
+            {
+                needFurtherInterpret = TRUE;
+                break;
+            }
+        } // end case AdditionalSenseCodeQual
+
+        break;
+        } // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
+
+    case SCSI_ADSENSE_NO_SEEK_COMPLETE: 
+        {
+        switch (AdditionalSenseCodeQual) 
+        {
+
+        case 0x00: 
+            {
+                needFurtherInterpret = FALSE;
+            
+                *Status = STATUS_DEVICE_DATA_ERROR;
+                *Retry = TRUE;
+                *RetryIntervalInSeconds = 0;
+                LogContext->LogError = TRUE;
+                LogContext->UniqueErrorValue = 256;
+                LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
+                break;
+            }
+
+        default:
+            {
+                needFurtherInterpret = TRUE;
+                break;
+            }
+        }
+
+        break;
+        } // end case SCSI_ADSENSE_NO_SEEK_COMPLETE
+
+    case SCSI_ADSENSE_LUN_COMMUNICATION: 
+        {
+        switch (AdditionalSenseCodeQual) 
+        {
+
+        case SCSI_SESNEQ_COMM_CRC_ERROR: 
+            {
+                needFurtherInterpret = FALSE;
+            
+                *Status = STATUS_IO_DEVICE_ERROR;
+                *Retry = TRUE;
+                *RetryIntervalInSeconds = 1;
+                LogContext->LogError = TRUE;
+                LogContext->UniqueErrorValue = 257;
+                LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
+                break;
+            }
+
+        default:
+            {
+                needFurtherInterpret = TRUE;
+                break;
+            }
+        }
+
+        break;
+        } // end case SCSI_ADSENSE_LUN_COMMUNICATION
+
+    case SCSI_ADSENSE_ILLEGAL_BLOCK: 
+        {
+            needFurtherInterpret = FALSE;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 
+                        "SenseInfoInterpretByAdditionalSenseCode: Illegal block address\n"));
+            *Status = STATUS_NONEXISTENT_SECTOR;
+            *Retry = FALSE;
+            break;
+        }
+
+    case SCSI_ADSENSE_INVALID_LUN: 
+        {
+            needFurtherInterpret = FALSE;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  
+                        "SenseInfoInterpretByAdditionalSenseCode: Invalid LUN\n"));
+            *Status = STATUS_NO_SUCH_DEVICE;
+            *Retry = FALSE;
+            break;
+        }
+
+    case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: 
+        {
+            needFurtherInterpret = FALSE;
+
+            *Retry = FALSE;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 
+                        "SenseInfoInterpretByAdditionalSenseCode: Key - Copy protection failure\n"));
+
+            switch (AdditionalSenseCodeQual) 
+            {
+            case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Authentication failure\n"));
+                *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
+                break;
+            case SCSI_SENSEQ_KEY_NOT_PRESENT:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Key not present\n"));
+                *Status = STATUS_CSS_KEY_NOT_PRESENT;
+                break;
+            case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Key not established\n"));
+                *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
+                break;
+            case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Read of scrambled sector w/o authentication\n"));
+                *Status = STATUS_CSS_SCRAMBLED_SECTOR;
+                break;
+            case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Media region does not logical unit region\n"));
+                *Status = STATUS_CSS_REGION_MISMATCH;
+                break;
+            case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
+                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretByAdditionalSenseCode: Region set error -- region may be permanent\n"));
+                *Status = STATUS_CSS_RESETS_EXHAUSTED;
+                break;
+
+            default:
+                *Status = STATUS_COPY_PROTECTION_FAILURE;
+                break;
+            } // end switch of ASCQ for COPY_PROTECTION_FAILURE
+
+            break;
+        }
+
+    case SCSI_ADSENSE_INVALID_CDB: 
+        {
+            needFurtherInterpret = FALSE;
+
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  
+                        "SenseInfoInterpretByAdditionalSenseCode: Key - Invalid CDB\n"));
+
+            *Status = STATUS_INVALID_DEVICE_REQUEST;
+            *Retry = FALSE;
+
+            // Note: the retry interval is not typically used.
+            // it is set here only because a ClassErrorHandler
+            // cannot set the RetryIntervalInSeconds, and the error may
+            // require a few commands to be sent to clear whatever
+            // caused this condition (i.e. disk clears the write
+            // cache, requiring at least two commands)
+            //
+            // hopefully, this shortcoming can be changed for blackcomb.
+            *RetryIntervalInSeconds = 3;
+
+            break;
+        }
+
+    case SCSI_ADSENSE_MEDIUM_CHANGED: 
+        {
+            needFurtherInterpret = FALSE;
+            *RetryIntervalInSeconds = 0;
+
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  
+                        "SenseInfoInterpretByAdditionalSenseCode: Media changed\n"));
+
+            DeviceSetMediaChangeStateEx(DeviceExtension, MediaPresent, NULL);
+
+            // special process for Media Change
+            if (IsVolumeMounted(DeviceExtension->DeviceObject))
+            {
+                // Set bit to indicate that media may have changed and volume needs verification.
+                SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+                *Status = STATUS_VERIFY_REQUIRED;
+                *Retry = FALSE;
+            }
+            else 
+            {
+                *Status = STATUS_IO_DEVICE_ERROR;
+                *Retry = TRUE;
+            }
+            break;
+        }
+
+    case SCSI_ADSENSE_OPERATOR_REQUEST: 
+        {
+            switch (AdditionalSenseCodeQual) 
+            {
+            case SCSI_SENSEQ_MEDIUM_REMOVAL: 
+                {
+                    needFurtherInterpret = FALSE;
+                    *RetryIntervalInSeconds = 0;
+            
+                    InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
+                    
+                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  
+                        "SenseInfoInterpretByAdditionalSenseCode: Ejection request received!\n"));
+                    //Send eject notification.
+                    DeviceSendNotification(DeviceExtension,
+                                           &GUID_IO_MEDIA_EJECT_REQUEST,
+                                           0,
+                                           NULL);
+                    // special process for Media Change
+                    if (IsVolumeMounted(DeviceExtension->DeviceObject))
+                    {
+                        // Set bit to indicate that media may have changed and volume needs verification.
+                        SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+                        *Status = STATUS_VERIFY_REQUIRED;
+                        *Retry = FALSE;
+                    }
+                    else 
+                    {
+                        *Status = STATUS_IO_DEVICE_ERROR;
+                        *Retry = TRUE;
+                    }
+                    break;
+                }
+            default:
+                {
+                    needFurtherInterpret = TRUE;
+                    break;
+                }
+            }
+            break;
+        }
+
+    case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: 
+        {
+            needFurtherInterpret = FALSE;
+            *RetryIntervalInSeconds = 5;
+
+            InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
+                    
+            // Device information has changed, we need to rescan the
+            // bus for changed information such as the capacity.
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  
+                        "SenseInfoInterpretByAdditionalSenseCode: Device information changed. Invalidate the bus\n"));
+
+            IoInvalidateDeviceRelations(DeviceExtension->LowerPdo, BusRelations);
+
+            // special process for Media Change
+            if (IsVolumeMounted(DeviceExtension->DeviceObject))
+            {
+                // Set bit to indicate that media may have changed and volume needs verification.
+                SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+                *Status = STATUS_VERIFY_REQUIRED;
+                *Retry = FALSE;
+            }
+            else 
+            {
+                *Status = STATUS_IO_DEVICE_ERROR;
+                *Retry = TRUE;
+            }
+            break;
+        } //end Case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED
+
+
+    case SCSI_ADSENSE_REC_DATA_NOECC:
+    case SCSI_ADSENSE_REC_DATA_ECC: 
+        {
+            needFurtherInterpret = FALSE;
+
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+            LogContext->LogError = TRUE;
+            LogContext->UniqueErrorValue = 258;
+            LogContext->ErrorCode = IO_RECOVERED_VIA_ECC;
+
+            if (senseBuffer->IncorrectLength) 
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                            "SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
+                *Status = STATUS_INVALID_BLOCK_LENGTH ;
+            }
+            break;
+        }
+
+    case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: 
+        {
+            UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
+
+            *((PULONG)wmiEventData) = sizeof(UCHAR);
+            wmiEventData[sizeof(ULONG)] = AdditionalSenseCodeQual;
+
+            needFurtherInterpret = FALSE;
+
+            // Don't log another eventlog if we have already logged once
+            // NOTE: this should have been interlocked, but the structure
+            //       was publicly defined to use a BOOLEAN (char).  Since
+            //       media only reports these errors once per X minutes,
+            //       the potential race condition is nearly non-existant.
+            //       the worst case is duplicate log entries, so ignore.
+
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+            LogContext->UniqueErrorValue = 258;
+            LogContext->LogError = TRUE;
+            LogContext->ErrorCode = IO_WRN_FAILURE_PREDICTED;
+
+            if (senseBuffer->IncorrectLength) 
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 
+                            "SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
+                *Status = STATUS_INVALID_BLOCK_LENGTH ;
+            }
+            break;
+        }
+
+    case 0x57: 
+        {
+            // UNABLE_TO_RECOVER_TABLE_OF_CONTENTS
+            // the Matshita CR-585 returns this for all read commands
+            // on blank CD-R and CD-RW media, and we need to handle
+            // this for READ_CD detection ability.
+            switch (AdditionalSenseCodeQual) 
+            {
+            case 0x00: 
+                {
+                    needFurtherInterpret = FALSE;
+
+                    *Status = STATUS_UNRECOGNIZED_MEDIA;
+                    *Retry = FALSE;
+                    break;
+                }
+            default:
+                {
+                    needFurtherInterpret = TRUE;
+                    break;
+                }
+            }
+            break;
+        }   //end case Matshita specific error 0x57
+
+    default:
+        {
+            needFurtherInterpret = TRUE;
+            break;
+        }
+    }
+
+    return needFurtherInterpret;
+}
+
+VOID
+SenseInfoInterpretBySenseKey(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      PSENSE_DATA               SenseData,
+    _In_      UCHAR                     SenseKey,
+    _Inout_   NTSTATUS*                 Status,
+    _Inout_   BOOLEAN*                  Retry,
+    _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,
+    _Inout_   PERROR_LOG_CONTEXT        LogContext
+    )
+{
+    // set default values for retry fields.
+    *Status = STATUS_IO_DEVICE_ERROR;
+    *Retry = TRUE;
+    *RetryIntervalInSeconds = 0;
+
+    switch (SenseKey)
+    {
+    case SCSI_SENSE_NOT_READY:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Not Ready (bad block)\n"));
+
+            *Status = STATUS_DEVICE_NOT_READY;
+            *Retry = TRUE;
+
+            // for unprocessed "not ready" codes, retry the command immediately.
+            *RetryIntervalInSeconds = 0;
+            break;
+        }
+
+    case SCSI_SENSE_DATA_PROTECT:
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Media write protected\n"));
+            *Status = STATUS_MEDIA_WRITE_PROTECTED;
+            *Retry = FALSE;
+            break;
+        } 
+
+    case SCSI_SENSE_MEDIUM_ERROR:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Medium Error (bad block)\n"));
+
+            *Status = STATUS_DEVICE_DATA_ERROR;
+            *Retry = FALSE;
+            LogContext->LogError = TRUE;
+            LogContext->UniqueErrorValue = 256;
+            LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
+
+            break;
+        } // end SCSI_SENSE_MEDIUM_ERROR
+
+    case SCSI_SENSE_HARDWARE_ERROR:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Hardware error\n"));
+
+            *Status = STATUS_IO_DEVICE_ERROR;
+            *Retry = TRUE;
+            LogContext->LogError = TRUE;
+            LogContext->UniqueErrorValue = 257;
+            LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
+
+            break;
+        } // end SCSI_SENSE_HARDWARE_ERROR
+
+    case SCSI_SENSE_ILLEGAL_REQUEST:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Illegal SCSI request\n"));
+            *Status = STATUS_INVALID_DEVICE_REQUEST;
+            *Retry = FALSE;
+
+            break;
+        } // end SCSI_SENSE_ILLEGAL_REQUEST
+
+    case SCSI_SENSE_UNIT_ATTENTION:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Key - Unit Attention\n"));
+
+            // A media change may have occured so increment the change
+            // count for the physical device
+            InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
+
+            if (IsVolumeMounted(DeviceExtension->DeviceObject))
+            {
+                // Set bit to indicate that media may have changed
+                // and volume needs verification.
+                SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+                *Status = STATUS_VERIFY_REQUIRED;
+                *Retry = FALSE;
+            }
+            else
+            {
+                *Status = STATUS_IO_DEVICE_ERROR;
+                *Retry = TRUE;
+            }
+
+            break;
+
+        } // end SCSI_SENSE_UNIT_ATTENTION
+
+    case SCSI_SENSE_ABORTED_COMMAND:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Command aborted\n"));
+            *Status = STATUS_IO_DEVICE_ERROR;
+            *Retry = TRUE;
+            *RetryIntervalInSeconds = 1;
+            break;
+        } // end SCSI_SENSE_ABORTED_COMMAND
+
+    case SCSI_SENSE_BLANK_CHECK:
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Media blank check\n"));
+            *Retry = FALSE;
+            *Status = STATUS_NO_DATA_DETECTED;
+            break;
+        } // end SCSI_SENSE_BLANK_CHECK
+
+    case SCSI_SENSE_RECOVERED_ERROR:
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Recovered error\n"));
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+            LogContext->LogError = TRUE;
+            LogContext->UniqueErrorValue = 258;
+            LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
+
+            if (SenseData->IncorrectLength)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
+                *Status = STATUS_INVALID_BLOCK_LENGTH ;
+            }
+
+            break;
+        } // end SCSI_SENSE_RECOVERED_ERROR
+
+    case SCSI_SENSE_NO_SENSE:
+        {
+            // Check other indicators.
+            if (SenseData->IncorrectLength)
+            {
+                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
+                *Status = STATUS_INVALID_BLOCK_LENGTH ;
+                *Retry   = FALSE;
+            }
+            else
+            {
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                            "SenseInfoInterpretBySenseKey: No specific sense key\n"));
+                *Status = STATUS_IO_DEVICE_ERROR;
+                *Retry = TRUE;
+            }
+
+            break;
+        } // end SCSI_SENSE_NO_SENSE
+
+    default:
+        {
+            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretBySenseKey: Unrecognized sense code\n"));
+            *Status = STATUS_IO_DEVICE_ERROR;
+            *Retry = TRUE;
+            *RetryIntervalInSeconds = 0;
+
+            break;
+        }
+
+    } // end switch (SenseKey)
+
+    return;
+}
+
+VOID
+SenseInfoInterpretBySrbStatus(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      PSCSI_REQUEST_BLOCK       Srb,
+    _In_ ULONG                          RetriedCount,
+    _Inout_   NTSTATUS*                 Status,
+    _Inout_   BOOLEAN*                  Retry,
+    _Out_ _Deref_out_range_(0,100) ULONG*         RetryIntervalInSeconds,
+    _Inout_   PERROR_LOG_CONTEXT        LogContext
+    )
+{
+    BOOLEAN incrementErrorCount = FALSE;
+
+    // set default values for retry fields.
+    *Status = STATUS_IO_DEVICE_ERROR;
+    *Retry = TRUE;
+    *RetryIntervalInSeconds = 0;
+
+    switch (SRB_STATUS(Srb->SrbStatus)) 
+    {
+    case SRB_STATUS_INVALID_LUN:
+    case SRB_STATUS_INVALID_TARGET_ID:
+    case SRB_STATUS_NO_DEVICE:
+    case SRB_STATUS_NO_HBA:
+    case SRB_STATUS_INVALID_PATH_ID: 
+    {
+        *Status = STATUS_NO_SUCH_DEVICE;
+        *Retry = FALSE;
+        break;
+    }
+
+    case SRB_STATUS_COMMAND_TIMEOUT:
+    case SRB_STATUS_TIMEOUT: 
+    {
+        // Update the error count for the device.
+        *Status = STATUS_IO_TIMEOUT;
+        *Retry = TRUE;
+        *RetryIntervalInSeconds = 0;
+        incrementErrorCount = TRUE;
+        break;
+    }
+
+    case SRB_STATUS_ABORTED:
+    {
+        // Update the error count for the device.
+        *Status = STATUS_IO_TIMEOUT;
+        *Retry = TRUE;
+        *RetryIntervalInSeconds = 1;
+        incrementErrorCount = TRUE;
+        break;
+    }
+
+    case SRB_STATUS_SELECTION_TIMEOUT: 
+    {
+        *Status = STATUS_DEVICE_NOT_CONNECTED;
+        *Retry = FALSE;
+        *RetryIntervalInSeconds = 2;
+        LogContext->LogError = TRUE;
+        LogContext->ErrorCode = IO_ERR_NOT_READY;
+        LogContext->UniqueErrorValue = 260;
+        break;
+    }
+
+    case SRB_STATUS_DATA_OVERRUN:
+    {
+        *Status = STATUS_DATA_OVERRUN;
+        *Retry = FALSE;
+        break;
+    }
+
+    case SRB_STATUS_PHASE_SEQUENCE_FAILURE: 
+    {
+        // Update the error count for the device.
+        incrementErrorCount = TRUE;
+        *Status = STATUS_IO_DEVICE_ERROR;
+
+        // If there was phase sequence error then limit the number of retries.
+        *Retry = (RetriedCount <= 1);
+
+        break;
+    }
+
+    case SRB_STATUS_REQUEST_FLUSHED: 
+    {
+        // If the status needs verification bit is set.  Then set
+        // the status to need verification and no retry; otherwise,
+        // just retry the request.
+        if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME)) 
+        {
+            *Status = STATUS_VERIFY_REQUIRED;
+            *Retry = FALSE;
+        } 
+        else 
+        {
+            *Status = STATUS_IO_DEVICE_ERROR;
+            *Retry = TRUE;
+        }
+
+        break;
+    }
+
+    case SRB_STATUS_INVALID_REQUEST: 
+    {
+        *Status = STATUS_INVALID_DEVICE_REQUEST;
+        *Retry = FALSE;
+        break;
+    }
+
+    case SRB_STATUS_UNEXPECTED_BUS_FREE:
+    case SRB_STATUS_PARITY_ERROR:
+        // Update the error count for the device and fall through to below
+        incrementErrorCount = TRUE;
+
+    case SRB_STATUS_BUS_RESET:
+    {
+        *Status = STATUS_IO_DEVICE_ERROR;
+        *Retry = TRUE;
+        break;
+    }
+
+    case SRB_STATUS_ERROR: 
+    {
+        *Status = STATUS_IO_DEVICE_ERROR;
+        *Retry = TRUE;
+
+        if (Srb->ScsiStatus == 0) 
+        {
+            // This is some strange return code.  Update the error
+            // count for the device.
+            incrementErrorCount = TRUE;
+        } 
+
+        if (Srb->ScsiStatus == SCSISTAT_BUSY) 
+        {
+            *Status = STATUS_DEVICE_NOT_READY;
+        }
+
+        break;
+    }
+
+    default: 
+    {
+        *Status = STATUS_IO_DEVICE_ERROR;
+        *Retry = TRUE;
+        LogContext->LogError = TRUE;
+        LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
+        LogContext->UniqueErrorValue = 259;
+        LogContext->ErrorUnhandled = TRUE;
+        break;
+    }
+
+    } //end of (SRB_STATUS(Srb->SrbStatus))  
+    
+    if (incrementErrorCount) 
+    {
+        // if any error count occurred, delay the retry of this io by
+        // at least one second, if caller supports it.
+        if (*RetryIntervalInSeconds == 0) 
+        {
+            *RetryIntervalInSeconds = 1;
+        }
+
+        DevicePerfIncrementErrorCount(DeviceExtension);
+    }
+    return;
+}
+
+VOID
+SenseInfoLogError(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_    PSCSI_REQUEST_BLOCK     Srb,
+    _In_    UCHAR                   MajorFunctionCode,
+    _In_    ULONG                   IoControlCode,
+    _In_    ULONG                   RetriedCount,
+    _In_    NTSTATUS*               Status,
+    _In_    BOOLEAN*                Retry,
+    _Inout_ PERROR_LOG_CONTEXT      LogContext
+    )
+{
+    //      Always log the error in our internal log.
+    //      If logError is set, also log the error in the system log.
+    PSENSE_DATA          senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+    ULONG                totalSize = 0;
+    ULONG                senseBufferSize = 0;
+    IO_ERROR_LOG_PACKET  staticErrLogEntry = {0};
+    CDROM_ERROR_LOG_DATA staticErrLogData = {0};
+
+    // Calculate the total size of the error log entry.
+    // add to totalSize in the order that they are used.
+    // the advantage to calculating all the sizes here is
+    // that we don't have to do a bunch of extraneous checks
+    // later on in this code path.
+    totalSize = sizeof(IO_ERROR_LOG_PACKET)     // required
+              + sizeof(CDROM_ERROR_LOG_DATA);   // struct for ease
+
+    // also save any available extra sense data, up to the maximum errlog
+    // packet size .  WMI should be used for real-time analysis.
+    // the event log should only be used for post-mortem debugging.
+    if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
+    {
+        ULONG       validSenseBytes;
+        BOOLEAN     validSense;
+
+        // make sure we can at least access the AdditionalSenseLength field
+        validSense = RTL_CONTAINS_FIELD(senseBuffer,
+                                        Srb->SenseInfoBufferLength,
+                                        AdditionalSenseLength);
+        if (validSense) 
+        {
+            // if extra info exists, copy the maximum amount of available
+            // sense data that is safe into the the errlog.
+            validSenseBytes = senseBuffer->AdditionalSenseLength
+                                + offsetof(SENSE_DATA, AdditionalSenseLength);
+
+            // this is invalid because it causes overflow!
+            // whoever sent this type of request would cause
+            // a system crash.
+            NT_ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
+
+            // set to save the most sense buffer possible
+            senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
+            senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
+        }
+        else 
+        {
+            // it's smaller than required to read the total number of
+            // valid bytes, so just use the SenseInfoBufferLength field.
+            senseBufferSize = Srb->SenseInfoBufferLength;
+        }
+
+        //  Bump totalSize by the number of extra senseBuffer bytes
+        //  (beyond the default sense buffer within CDROM_ERROR_LOG_DATA).
+        //  Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
+        if (senseBufferSize > sizeof(SENSE_DATA))
+        {
+            totalSize += senseBufferSize-sizeof(SENSE_DATA);
+            if (totalSize > ERROR_LOG_MAXIMUM_SIZE)
+            {
+                senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
+                totalSize = ERROR_LOG_MAXIMUM_SIZE;
+            }
+        }
+    }
+
+    // If we've used up all of our retry attempts, set the final status to
+    // reflect the appropriate result.
+    //
+    // ISSUE: the test below should also check RetriedCount to determine if we will actually retry,
+    //            but there is no easy test because we'd have to consider the original retry count
+    //            for the op; besides, InterpretTransferPacketError sometimes ignores the retry
+    //            decision returned by this function.  So just ErrorRetried to be true in the majority case.
+    //
+    if (*Retry)
+    {
+        staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
+        staticErrLogData.ErrorRetried = TRUE;
+    } 
+    else 
+    {
+        staticErrLogEntry.FinalStatus = *Status;
+    }
+
+    // Don't log generic IO_WARNING_PAGING_FAILURE message if either the
+    // I/O is retried, or it completed successfully.
+    if ((LogContext->ErrorCode == IO_WARNING_PAGING_FAILURE) &&
+        (*Retry || NT_SUCCESS(*Status)) ) 
+    {
+        LogContext->LogError = FALSE;
+    }
+
+    if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) 
+    {
+        staticErrLogData.ErrorPaging = TRUE;
+    }
+
+    staticErrLogData.ErrorUnhandled = LogContext->ErrorUnhandled;
+
+    // Calculate the device offset if there is a geometry.
+    staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)LogContext->BadSector;
+    staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)DeviceExtension->DiskGeometry.BytesPerSector;
+
+    if (LogContext->ErrorCode == -1)
+    {
+        staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
+    }
+    else 
+    {
+        staticErrLogEntry.ErrorCode = LogContext->ErrorCode;
+    }
+
+    //  The dump data follows the IO_ERROR_LOG_PACKET
+    staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
+
+    staticErrLogEntry.SequenceNumber = 0;
+    staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
+    staticErrLogEntry.IoControlCode = IoControlCode;
+    staticErrLogEntry.RetryCount = (UCHAR)RetriedCount;
+    staticErrLogEntry.UniqueErrorValue = LogContext->UniqueErrorValue;
+
+    KeQueryTickCount(&staticErrLogData.TickCount);
+    staticErrLogData.PortNumber = (ULONG)-1;
+
+    //  Save the entire contents of the SRB.
+    staticErrLogData.Srb = *Srb;
+
+    //  For our private log, save just the default length of the SENSE_DATA.
+    if (senseBufferSize != 0)
+    {
+        RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
+    }
+
+    // Save the error log in our context.
+    // We only save the default sense buffer length.
+    {
+        KIRQL                oldIrql;
+        KeAcquireSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, &oldIrql);
+        DeviceExtension->PrivateFdoData->ErrorLogs[DeviceExtension->PrivateFdoData->ErrorLogNextIndex] = staticErrLogData;
+        DeviceExtension->PrivateFdoData->ErrorLogNextIndex++;
+        DeviceExtension->PrivateFdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
+        KeReleaseSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, oldIrql);
+    }
+
+    //  If logError is set, also save this log in the system's error log.
+    //  But make sure we don't log TUR failures over and over
+    //  (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
+    if (LogContext->LogError)
+    {
+        // We do not want to log certain system events repetitively
+        switch (((PCDB)Srb->Cdb)->CDB10.OperationCode)
+        {
+            case SCSIOP_TEST_UNIT_READY:
+            {
+                if (DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO)
+                {
+                    LogContext->LogError = FALSE;
+                }
+                else
+                {
+                    DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = TRUE;
+                }
+
+                break;
+            }
+
+            case SCSIOP_SYNCHRONIZE_CACHE:
+            {
+                if (DeviceExtension->PrivateFdoData->LoggedSYNCFailure)
+                {
+                    LogContext->LogError = FALSE;
+                }
+                else
+                {
+                    DeviceExtension->PrivateFdoData->LoggedSYNCFailure = TRUE;
+                }
+
+                break;
+            }
+        }
+
+        // Do not log 5/21/00 LOGICAL BLOCK ADDRESS OUT OF RANGE if the disc is blank,
+        // it is known to litter the Event Log with repetitive errors
+        // Do not log this error for READ, as it's known that File System mount process reads different sectors from media.
+        if (senseBufferSize > RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier) &&
+            senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST &&
+            senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_ILLEGAL_BLOCK &&
+            senseBuffer->AdditionalSenseCodeQualifier == 0 &&
+            IS_SCSIOP_READ(((PCDB)Srb->Cdb)->CDB10.OperationCode))
+        {
+            LogContext->LogError = FALSE;
+        }
+    }
+
+    //  Write the error log packet to the system error logging thread.
+    if (LogContext->LogError)
+    {
+        PIO_ERROR_LOG_PACKET    errorLogEntry;
+        PCDROM_ERROR_LOG_DATA   errlogData;
+
+        errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceExtension->DeviceObject, (UCHAR)totalSize);
+        if (errorLogEntry)
+        {
+            errlogData = (PCDROM_ERROR_LOG_DATA)errorLogEntry->DumpData;
+
+            *errorLogEntry = staticErrLogEntry;
+            *errlogData = staticErrLogData;
+
+            //  For the system log, copy as much of the sense buffer as possible.
+            if (senseBufferSize != 0) 
+            {
+                RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
+            }
+
+            //  errorLogEntry - It will be freed by the kernel.
+            IoWriteErrorLogEntry(errorLogEntry);
+        }
+    }
+
+    return;
+}
+
+VOID
+SenseInfoInterpretRefineByScsiCommand(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      PSCSI_REQUEST_BLOCK       Srb,
+    _In_      ULONG                     RetriedCount,
+    _In_      LONGLONG                  Total100nsSinceFirstSend,
+    _In_      BOOLEAN                   OverrideVerifyVolume,
+    _Inout_   BOOLEAN*                  Retry,
+    _Inout_   NTSTATUS*                 Status,
+    _Inout_   _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+              LONGLONG*                 RetryIntervalIn100ns
+    )
+/*++
+
+Routine Description:
+
+    Based on SCSI command, modify the interpretion result.
+
+Arguments:
+
+    DeviceExtension - device extension.
+    Srb - Supplies the scsi request block which failed.
+    RetriedCount - retried count.
+    Total100nsUnitsSinceFirstSend - time spent after the request was sent down first time.
+    OverrideVerifyVolume - should override verify volume request.
+
+Return Value:
+
+    Retry - the reques should be retried or not.
+    Status - Returns the status for the request.
+    RetryInterval - waiting time (in 100ns) before the request should be retried.
+                    Zero indicates the request should be immediately retried.
+
+--*/
+{
+    UCHAR const opCode = Srb->Cdb[0];
+    CDB const*  cdb = (CDB const*)(Srb->Cdb);
+    PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+
+    if (opCode == SCSIOP_MEDIUM_REMOVAL)
+    {
+        if (( cdb->AsByte[1]         == 0) &&
+            ( cdb->AsByte[2]         == 0) &&
+            ( cdb->AsByte[3]         == 0) &&
+            ((cdb->AsByte[4] & 0xFC) == 0)
+            )
+        {
+            // byte[4] == 0x3 or byte[4] == 0x1  ==  UNLOCK OF MEDIA
+            if ((cdb->AsByte[4] & 0x01) == 0)
+            {
+                if (RetriedCount < TOTAL_COUNT_RETRY_DEFAULT)
+                {
+                    // keep retrying unlock operation for several times
+                    *Retry = TRUE;
+                }
+            }
+            else // LOCK REQUEST
+            {
+                // do not retry LOCK requests more than once (per CLASSPNP code)
+                if (RetriedCount > TOTAL_COUNT_RETRY_LOCK_MEDIA)
+                {
+                    *Retry = FALSE;
+                }
+            }
+        }
+
+        // want a minimum time to retry of 2 seconds
+        if ((*Status == STATUS_DEVICE_NOT_READY) &&
+            (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
+        {
+            *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
+        }
+        else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
+        {
+            *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
+        }
+    }
+    else if ((opCode == SCSIOP_MODE_SENSE) || (opCode == SCSIOP_MODE_SENSE10))
+    {
+        // want a minimum time to retry of 2 seconds
+        if ((*Status == STATUS_DEVICE_NOT_READY) &&
+            (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
+        {
+            *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
+        }
+        else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
+        {
+            *RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
+        }
+
+        // Want to ignore a STATUS_VERIFY_REQUIRED error because it either
+        // doesn't make sense or is required to satisfy the VERIFY.
+        if (*Status == STATUS_VERIFY_REQUIRED)
+        {
+            *Retry = TRUE;
+        }
+        else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
+        {
+            /*
+             *  This is a HACK.
+             *  Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
+             *  underrun (i.e. success, and the buffer is longer than needed).
+             *  So treat this as a success.
+             *  When the caller of this function sees that the status was changed to success,
+             *  it will add the transferred length to the original irp.
+             */
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+        }
+
+        // limit the count of retries
+        if (RetriedCount > TOTAL_COUNT_RETRY_MODESENSE)
+        {
+            *Retry = FALSE;
+        }
+    }
+    else if ((opCode == SCSIOP_READ_CAPACITY) || (opCode == SCSIOP_READ_CAPACITY16))
+    {
+        // Want to ignore a STATUS_VERIFY_REQUIRED error because it either
+        // doesn't make sense or is required to satisfy the VERIFY.
+        if (*Status == STATUS_VERIFY_REQUIRED)
+        {
+            *Retry = TRUE;
+        }
+
+        if (RetriedCount > TOTAL_COUNT_RETRY_READ_CAPACITY)
+        {
+            *Retry = FALSE;
+        }
+    }
+    else if ((opCode == SCSIOP_RESERVE_UNIT) || (opCode == SCSIOP_RELEASE_UNIT))
+    {
+        // The RESERVE(6) / RELEASE(6) commands are optional. 
+        // So if they aren't supported, try the 10-byte equivalents
+        if (*Status == STATUS_INVALID_DEVICE_REQUEST)
+        {
+            PCDB tempCdb = (PCDB)Srb->Cdb;
+
+            Srb->CdbLength = 10;
+            tempCdb->CDB10.OperationCode = (tempCdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT) 
+                                            ? SCSIOP_RESERVE_UNIT10 
+                                            : SCSIOP_RELEASE_UNIT10;
+
+            SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
+            *Retry = TRUE;
+        }
+    }
+    else if (IS_SCSIOP_READWRITE(opCode))
+    {
+        // Retry if still verifying a (possibly) reloaded disk/cdrom.
+        if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED))
+        {
+            *Status = STATUS_IO_DEVICE_ERROR;
+            *Retry = TRUE;
+        }
+
+        // Special case for streaming READ/WRITE commands
+        if (((opCode == SCSIOP_READ12) && (cdb->READ12.Streaming == 1)) ||
+            ((opCode == SCSIOP_WRITE12) && (cdb->WRITE12.Streaming == 1)))
+        {
+            // We've got a failure while performing a streaming operation and now need to guess if
+            // it's likely to be a permanent error because the drive does not support streaming at all
+            // (in which case we're going to fall back to normal reads/writes), or a transient error
+            // (in which case we quickly fail the request but contrinue to use streaming).
+            //
+            // We analyze the sense information to make that decision. Bus resets and device timeouts
+            // are treated as permanent errors, because some non-compliant devices may even hang when
+            // they get a command that they do not expect.
+
+            BOOLEAN     disableStreaming = FALSE;
+
+            if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_TIMEOUT || 
+                SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_COMMAND_TIMEOUT ||
+                SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT ||
+                SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_BUS_RESET)
+            {
+                disableStreaming = TRUE;
+            }
+            else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_UNIT_ATTENTION)
+            {
+                if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_BUS_RESET ||
+                    senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION)
+                {
+                    disableStreaming = TRUE;
+                }
+            }
+            else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_ILLEGAL_REQUEST)
+            {
+                // LBA Out of Range is an exception, as it's more likely to be caused by 
+                // upper layers attempting to read/write a wrong LBA.
+                if (senseBuffer->AdditionalSenseCode != SCSI_ADSENSE_ILLEGAL_BLOCK)
+                {
+                    disableStreaming = TRUE;
+                }
+            }
+
+            if (disableStreaming)
+            {
+                // if the failure looks permanent, we disable streaming for all future reads/writes
+                // and retry the command immediately
+                SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING);
+                *Retry = TRUE;
+                *RetryIntervalIn100ns = 0;
+            }
+            else
+            {
+                // if the failure looks transient, we simply fail the current request without retries
+                // to minimize the time of processing
+                *Retry = FALSE;
+            }
+        }
+
+        // Special-case handling of READ/WRITE commands.  These commands now have a 120 second timeout, 
+        // but the preferred behavior (and that taken by many drives) is to immediately report 2/4/x 
+        // on OPC and similar commands.  Thus, retries must occur for at least 160 seconds 
+        // (120 seconds + four 10 second retries) as a conservative guess.
+        // Note: 160s retry time is also a result of discussion with OEMs for case of 2/4/7 and 2/4/8. 
+        if (*Retry)
+        {
+            if ((Total100nsSinceFirstSend < 0) ||
+                (((senseBuffer->SenseKey &0xf) == SCSI_SENSE_HARDWARE_ERROR) && (senseBuffer->AdditionalSenseCode == 0x09)))
+            {
+                // time information is not valid. use default retry count.
+                // or if it's SERVO FAILURE, use retry count instead of 160s retry.
+                *Retry = (RetriedCount <= TOTAL_COUNT_RETRY_DEFAULT);
+            }
+            else if (Total100nsSinceFirstSend > SECONDS_TO_100NS_UNITS(TOTAL_SECONDS_RETRY_TIME_WRITE))
+            {
+                *Retry = FALSE;
+            }
+
+            // How long should we request a delay for during writing?  This depends entirely on 
+            // the current write speed of the drive.  If we request retries too quickly,
+            // we can overload the processor on the drive (resulting in garbage being written),
+            // but too slowly results in lesser performance.
+            //
+            *RetryIntervalIn100ns = DeviceExtension->DeviceAdditionalData.ReadWriteRetryDelay100nsUnits;
+
+        } // end retry for 160 seconds modification
+    }
+    else if (opCode == SCSIOP_GET_PERFORMANCE)
+    {
+        if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
+        {
+            //  This is a HACK.
+            //  Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
+            //  underrun (i.e. success, and the buffer is longer than needed).
+            //  So treat this as a success.
+            //  When the caller of this function sees that the status was changed to success,
+            //  it will add the transferred length to the original irp.
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+        }
+
+        if ((Srb->SenseInfoBufferLength < RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA,AdditionalSenseCodeQualifier)) ||
+            !TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) 
+        {
+            // If get configuration command is failing and if the request type is TYPE ONE
+            // then most likely the device does not support this request type. Set the
+            // flag so that the TYPE ONE requests will be tried as TYPE ALL requets.
+            if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
+                (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_DATA_OVERRUN) &&
+                (((PCDB)Srb->Cdb)->GET_CONFIGURATION.RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE)) 
+            {
+
+                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
+                           "TYPE ONE GetConfiguration failed. Set hack flag and retry.\n"));
+                SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
+                *Retry = TRUE;
+            }
+        }
+
+        // limit retries to GET_PERFORMANCE commands to default retry count
+        if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
+        {
+            *Retry = FALSE;
+        }
+    }
+    else // default handler -- checks for retry count only.
+    {
+        if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
+        {
+            *Retry = FALSE;
+        }
+    }
+
+    return;
+}
+
+
+VOID
+SenseInfoInterpretRefineByIoControl(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      ULONG                     IoControlCode,
+    _In_      BOOLEAN                   OverrideVerifyVolume,
+    _Inout_   BOOLEAN*                  Retry,
+    _Inout_   NTSTATUS*                 Status
+    )
+/*++
+
+Routine Description:
+
+    Based on IOCTL code, modify the interpretion result.
+
+Arguments:
+
+    Device - Supplies the device object associated with this request.
+    OriginalRequest - the irp that error occurs on.
+    Srb - Supplies the scsi request block which failed.
+    MajorFunctionCode - Supplies the function code to be used for logging.
+    IoDeviceCode - Supplies the device code to be used for logging.
+    PreviousRetryCount - retried count.
+    RequestHistory_DoNotUse - the history list
+
+Return Value:
+
+    BOOLEAN TRUE: Drivers should retry this request.
+            FALSE: Drivers should not retry this request.
+    Status - Returns the status for the request.
+    RetryInterval - Number of seconds before the request should be retried.
+                    Zero indicates the request should be immediately retried.
+
+--*/
+{
+    PAGED_CODE();
+
+    if ((IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) ||
+        (IoControlCode == IOCTL_CDROM_READ_TOC)         ||
+        (IoControlCode == IOCTL_CDROM_READ_TOC_EX)      ||
+        (IoControlCode == IOCTL_CDROM_GET_CONFIGURATION)||
+        (IoControlCode == IOCTL_CDROM_GET_VOLUME)) 
+    {
+        if (*Status == STATUS_DATA_OVERRUN) 
+        {
+            *Status = STATUS_SUCCESS;
+            *Retry = FALSE;
+        }
+    }
+
+    if (IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) 
+    {
+        PLAY_ACTIVE(DeviceExtension) = FALSE;
+    }
+
+    // If the status is verified required and the this request
+    // should bypass verify required then retry the request.
+    if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED)) 
+    {
+        // note: status gets overwritten here
+        *Status = STATUS_IO_DEVICE_ERROR;
+        *Retry = TRUE;
+
+        if ((IoControlCode == IOCTL_CDROM_CHECK_VERIFY) ||
+            (IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) ||
+            (IoControlCode == IOCTL_STORAGE_CHECK_VERIFY2) ||
+            (IoControlCode == IOCTL_DISK_CHECK_VERIFY)
+           ) 
+        {
+            // Update the geometry information, as the media could have changed.
+            (VOID) MediaReadCapacity(DeviceExtension->Device);
+        } // end of ioctls to update capacity
+    }
+
+    if (!NT_SUCCESS(*Status) && (IoControlCode == IOCTL_CDROM_SET_SPEED))
+    {
+        // If set speed request fails then we should disable the restore speed option.
+        // Otherwise we will try to restore to default speed on next media change,
+        // if requested by the caller.
+        DeviceExtension->DeviceAdditionalData.RestoreDefaults = FALSE;
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Disable restore default\n"));
+    }
+
+    return;
+}
+
+BOOLEAN
+SenseInfoInterpret(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_    WDFREQUEST              Request, 
+    _In_    PSCSI_REQUEST_BLOCK     Srb,
+    _In_    ULONG                   RetriedCount,
+    _Out_   NTSTATUS*               Status,
+    _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+              LONGLONG*             RetryIntervalIn100ns
+    )
+/*++
+
+SenseInfoInterpret()
+
+Routine Description:
+
+    This routine interprets the data returned from the SCSI request sense. 
+    It determines the status to return in the IRP 
+    and whether this request can be retried.
+
+Arguments:
+
+    Device - Supplies the device object associated with this request.
+    Srb - Supplies the scsi request block which failed.
+    MajorFunctionCode - Supplies the function code to be used for logging.
+    IoDeviceCode - Supplies the device code to be used for logging.
+
+Return Value:
+
+    BOOLEAN TRUE: Drivers should retry this request.
+            FALSE: Drivers should not retry this request.
+    Status - Returns the status for the request.
+    RetryInterval - Number of seconds before the request should be retried.
+                    Zero indicates the request should be immediately retried.
+
+--*/
+{
+    ULONG               retryIntervalInSeconds = 0;
+    BOOLEAN             retry = TRUE;
+    PSENSE_DATA         senseBuffer = Srb->SenseInfoBuffer;
+    ULONG               readSector = 0;
+    ERROR_LOG_CONTEXT   logContext;
+
+    UCHAR                   majorFunctionCode = 0;
+    ULONG                   ioControlCode = 0;
+    BOOLEAN                 overrideVerifyVolume = FALSE;
+    ULONGLONG               total100nsSinceFirstSend = 0;
+    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    //
+    *Status = STATUS_IO_DEVICE_ERROR;
+
+    RtlZeroMemory(&logContext, sizeof(ERROR_LOG_CONTEXT));
+    logContext.ErrorCode = -1;
+
+    // Get Original Request related information
+    SenseInfoRequestGetInformation(Request,
+                                   &majorFunctionCode,
+                                   &ioControlCode,
+                                   &overrideVerifyVolume,
+                                   &total100nsSinceFirstSend);
+
+    if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) 
+    {
+        // Log anything remotely incorrect about paging i/o
+        logContext.LogError = TRUE;
+        logContext.UniqueErrorValue = 301;
+        logContext.ErrorCode = IO_WARNING_PAGING_FAILURE;
+    }
+
+    // must handle the SRB_STATUS_INTERNAL_ERROR case first,
+    // as it has all the flags set.
+    if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR)
+    {
+        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+                    "SenseInfoInterpret: Internal Error code is %x\n",
+                    Srb->InternalStatus));
+
+        retry = FALSE;
+        *Status = Srb->InternalStatus;
+    } 
+    else if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) 
+    {
+        retry = FALSE;
+        *Status = STATUS_DEVICE_BUSY;
+        logContext.LogError = FALSE;
+    } 
+    else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
+             (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))) 
+    {
+        UCHAR   senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
+        UCHAR   additionalSenseCode = 0;
+        UCHAR   additionalSenseCodeQual = 0;
+
+        // Zero the additional sense code and additional sense code qualifier
+        // if they were not returned by the device.
+        readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
+        if (readSector > Srb->SenseInfoBufferLength) 
+        {
+            readSector = Srb->SenseInfoBufferLength;
+        }
+
+        additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
+                               senseBuffer->AdditionalSenseCode : 0;
+        additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
+                                   senseBuffer->AdditionalSenseCodeQualifier : 0;
+
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, 
+                    "SCSI Error - \n"
+                    "\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+                    "\tsrb status: %X; sense: %02X/%02X/%02X; Retried count: %d\n\n",
+                    Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5], 
+                    Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11], 
+                    Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15], 
+                    SRB_STATUS(Srb->SrbStatus), 
+                    senseKey, 
+                    additionalSenseCode, 
+                    additionalSenseCodeQual,
+                    RetriedCount));
+
+        if (senseKey == SCSI_SENSE_UNIT_ATTENTION)
+        {
+            ULONG   mediaChangeCount;
+
+            // A media change may have occured so increment the change count for the physical device
+            mediaChangeCount = InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  
+                       "SenseInfoInterpret: Media change count for device %d incremented to %#lx\n",
+                       DeviceExtension->DeviceNumber, mediaChangeCount));
+        }
+
+        if ((zpoddInfo != NULL) &&
+            (((PCDB)Srb->Cdb)->CDB6GENERIC.OperationCode == SCSIOP_TEST_UNIT_READY))
+        {
+            // This sense code is in response to the Test Unit Ready sent during delayed power down
+            // request. Copy the sense data into the zpoddInfo structure for later processing.
+            zpoddInfo->SenseKey = senseKey;
+            zpoddInfo->AdditionalSenseCode = additionalSenseCode;
+            zpoddInfo->AdditionalSenseCodeQualifier = additionalSenseCodeQual;
+        }
+
+        // Interpret error by specific ASC & ASCQ first,
+        // If the error is not handled, interpret using f
+        {
+            BOOLEAN notHandled = FALSE;
+            notHandled = SenseInfoInterpretByAdditionalSenseCode(DeviceExtension,
+                                                                 Srb,
+                                                                 additionalSenseCode, 
+                                                                 additionalSenseCodeQual,
+                                                                 Status,
+                                                                 &retry,
+                                                                 &retryIntervalInSeconds,
+                                                                 &logContext);
+            
+            if (notHandled)
+            {
+                SenseInfoInterpretBySenseKey(DeviceExtension,
+                                             senseBuffer,
+                                             senseKey,
+                                             Status,
+                                             &retry,
+                                             &retryIntervalInSeconds,
+                                             &logContext);
+            }
+        }
+
+        // Try to determine the bad sector from the inquiry data.
+        if ((IS_SCSIOP_READWRITE(((PCDB)Srb->Cdb)->CDB10.OperationCode)) ||
+            (((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY)     ||
+            (((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY16)) 
+        {
+            ULONG index;
+            readSector = 0;
+
+            for (index = 0; index < 4; index++) 
+            {
+                logContext.BadSector = (logContext.BadSector << 8) | senseBuffer->Information[index];
+            }
+
+            for (index = 0; index < 4; index++) 
+            {
+                readSector = (readSector << 8) | Srb->Cdb[index+2];
+            }
+
+            index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
+                    ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
+
+            // Make sure the bad sector is within the read sectors.
+            if (!(logContext.BadSector >= readSector && logContext.BadSector < (readSector + index)))
+            {
+                logContext.BadSector = readSector;
+            }
+        }
+    } 
+    else 
+    {
+        // Request sense buffer not valid. No sense information
+        // to pinpoint the error. Return general request fail.
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  
+                    "SCSI Error - \n"
+                    "\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+                    "\tsrb status: %X; sense info not valid; Retried count: %d\n\n",
+                    Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5], 
+                    Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11], 
+                    Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15], 
+                    SRB_STATUS(Srb->SrbStatus),
+                    RetriedCount));
+
+        SenseInfoInterpretBySrbStatus(DeviceExtension,
+                                      Srb, 
+                                      RetriedCount,
+                                      Status,
+                                      &retry,
+                                      &retryIntervalInSeconds,
+                                      &logContext);
+    }
+
+    // all functions using unit - seconds for retry Interval already be called.
+    *RetryIntervalIn100ns = SECONDS_TO_100NS_UNITS(retryIntervalInSeconds);
+
+    // call the device specific error handler if it has one.
+    // DeviceErrorHandlerForMmmc() for all MMC devices
+    // or DeviceErrorHandlerForHitachiGD2000() for HITACHI GD-2000, HITACHI DVD-ROM GD-2000
+    if (DeviceExtension->DeviceAdditionalData.ErrorHandler) 
+    {
+        DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension, Srb, Status, &retry);
+    }
+
+    // Refine retry based on SCSI command
+    SenseInfoInterpretRefineByScsiCommand(DeviceExtension,
+                                          Srb,
+                                          RetriedCount,
+                                          total100nsSinceFirstSend,
+                                          overrideVerifyVolume,
+                                          &retry,
+                                          Status,
+                                          RetryIntervalIn100ns);
+
+    // Refine retry based on IOCTL code. 
+    if (majorFunctionCode == IRP_MJ_DEVICE_CONTROL)
+    {
+        SenseInfoInterpretRefineByIoControl(DeviceExtension,
+                                            ioControlCode, 
+                                            overrideVerifyVolume,
+                                            &retry,
+                                            Status);
+    }
+    
+    // LOG the error:
+    //  Always log the error in our internal log.
+    //  If logError is set, also log the error in the system log.
+    SenseInfoLogError(DeviceExtension,
+                      Srb,
+                      majorFunctionCode,
+                      ioControlCode,
+                      RetriedCount,
+                      Status,
+                      &retry,
+                      &logContext);
+
+    // all process about the error done. check if the irp was cancelled.
+    if ((!NT_SUCCESS(*Status)) && retry)
+    {
+        PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
+
+        if ((requestContext->OriginalRequest != NULL) &&
+            WdfRequestIsCanceled(requestContext->OriginalRequest)
+            )
+        {
+            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  
+                       "Request %p was cancelled when it would have been retried\n",
+                       requestContext->OriginalRequest));
+            
+            *Status = STATUS_CANCELLED;
+            retry = FALSE;
+            *RetryIntervalIn100ns = 0;
+        }
+    }
+
+    // now, all decisions are made. display trace information.
+    if (retry)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  
+                   "Command shall be retried in %2I64d.%03I64d seconds\n",
+                   (*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
+                   (*RetryIntervalIn100ns / 10000) % 1000
+                   ));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  
+                   "Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
+                   senseBuffer->SenseKey,
+                   senseBuffer->AdditionalSenseCode,
+                   senseBuffer->AdditionalSenseCodeQualifier
+                   ));
+    }
+
+    return retry;
+
+} // end SenseInfoInterpret()
+
+
+BOOLEAN
+SenseInfoInterpretForZPODD(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_    PSCSI_REQUEST_BLOCK     Srb,
+    _Out_   NTSTATUS*               Status,
+    _Out_ _Out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+              LONGLONG*             RetryIntervalIn100ns
+    )
+/*++
+
+SenseInfoInterpretForZPODD()
+
+Routine Description:
+
+    This routine interprets the data returned from the SCSI request sense. 
+    It determines the status to return in the IRP 
+    and whether this request can be retried.
+
+Arguments:
+
+    Device - Supplies the device object associated with this request.
+    Srb - Supplies the scsi request block which failed.
+
+Return Value:
+
+    BOOLEAN TRUE: Drivers should retry this request.
+            FALSE: Drivers should not retry this request.
+    Status - Returns the status for the request.
+    RetryInterval - Number of seconds before the request should be retried.
+                    Zero indicates the request should be immediately retried.
+
+--*/
+{
+    BOOLEAN                 retry = FALSE;
+    PSENSE_DATA             senseBuffer = Srb->SenseInfoBuffer;
+    ULONG                   readSector = 0;
+    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    *Status = STATUS_IO_DEVICE_ERROR;
+    *RetryIntervalIn100ns = 0;
+
+    if (zpoddInfo->RetryFirstCommand != FALSE)
+    {
+        // The first command to the logical unit after power resumed will be terminated
+        // with CHECK CONDITION Status, 6/29/00 POWER ON, RESET, OR BUS DEVICE RESET OCCURRED
+
+        // We have observed some devices return a different sense code, and thus as long as
+        // the first command after power resume fails, we just retry one more time.
+        zpoddInfo->RetryFirstCommand = FALSE;
+
+        retry = TRUE;
+    }
+    else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
+             (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))) 
+    {
+        UCHAR   senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
+        UCHAR   additionalSenseCode = 0;
+        UCHAR   additionalSenseCodeQual = 0;
+
+        // Zero the additional sense code and additional sense code qualifier
+        // if they were not returned by the device.
+        readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
+        if (readSector > Srb->SenseInfoBufferLength) 
+        {
+            readSector = Srb->SenseInfoBufferLength;
+        }
+
+        additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
+                               senseBuffer->AdditionalSenseCode : 0;
+        additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
+                                   senseBuffer->AdditionalSenseCodeQualifier : 0;
+
+        // If sense code is 2/4/1, device is becoming ready from ZPODD mode. According to Mt Fuji, device
+        // could take up to 800msec to be fully operational.
+        if ((senseKey == SCSI_SENSE_NOT_READY) &&
+            (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
+            (additionalSenseCodeQual == SCSI_SENSEQ_BECOMING_READY))
+        {
+            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+                        "SenseInfoInterpretForZPODD: In process of becoming ready\n"));
+
+            zpoddInfo->BecomingReadyRetryCount--;
+
+            if (zpoddInfo->BecomingReadyRetryCount > 0)
+            {
+                DEVICE_EVENT_BECOMING_READY notReady = {0};
+
+                retry = TRUE;
+                *Status = STATUS_DEVICE_NOT_READY;
+                *RetryIntervalIn100ns = BECOMING_READY_RETRY_INTERNVAL_IN_100NS;
+
+                notReady.Version = 1;
+                notReady.Reason = 1;
+                notReady.Estimated100msToReady = (ULONG) *RetryIntervalIn100ns / (1000 * 1000);
+                DeviceSendNotification(DeviceExtension,
+                                       &GUID_IO_DEVICE_BECOMING_READY,
+                                       sizeof(DEVICE_EVENT_BECOMING_READY),
+                                       &notReady);
+            }
+        }
+    }
+
+    // now, all decisions are made. display trace information.
+    if (retry)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  
+                   "Command shall be retried in %2I64d.%03I64d seconds\n",
+                   (*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
+                   (*RetryIntervalIn100ns / 10000) % 1000
+                   ));
+    }
+    else
+    {
+        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  
+                   "Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
+                   senseBuffer->SenseKey,
+                   senseBuffer->AdditionalSenseCode,
+                   senseBuffer->AdditionalSenseCodeQualifier
+                   ));
+    }
+
+    return retry;
+
+} // end SenseInfoInterpret()
+
+
+BOOLEAN
+RequestSenseInfoInterpret(
+    _In_      PCDROM_DEVICE_EXTENSION   DeviceExtension,
+    _In_      WDFREQUEST                Request, 
+    _In_      PSCSI_REQUEST_BLOCK       Srb,
+    _In_      ULONG                     RetriedCount,
+    _Out_     NTSTATUS*                 Status,
+    _Out_opt_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+              LONGLONG*                 RetryIntervalIn100ns
+    )
+/*++
+
+Routine Description:
+
+Interpret the error, process it.
+    1. Release device queue if it's frozen.
+    2. Interpret and process the error.
+
+Arguments:
+
+    DeviceExtension - Supplies the device object associated with this request.
+    Request - the Request that error occurs on.
+    Srb - Supplies the scsi request block which failed.
+    RetriedCount - retried count.
+
+Return Value:
+
+    BOOLEAN TRUE:  Drivers should retry this request.
+            FALSE: Drivers should not retry this request.
+    Status - Returns the status for the request.
+    RetryIntervalIn100nsUnits - Number of 100ns before the request should be retried.
+                                Zero indicates the request should be immediately retried.
+
+--*/
+{
+    BOOLEAN                 retry = FALSE;
+    LONGLONG                retryIntervalIn100ns = 0;
+    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
+    {
+        // request succeeded. 
+        if ((zpoddInfo != NULL) &&
+            (zpoddInfo->BecomingReadyRetryCount > 0))
+        {
+            zpoddInfo->BecomingReadyRetryCount = 0;
+        }
+
+        *Status = STATUS_SUCCESS;
+        retry = FALSE;
+    }
+    else
+    {
+        // request failed. We need to process the error.
+        
+        // 1. Release the queue if it is frozen.
+        if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) 
+        {
+            DeviceReleaseQueue(DeviceExtension->Device);
+        }
+
+        if ((zpoddInfo != NULL) &&
+            ((zpoddInfo->RetryFirstCommand != FALSE) || (zpoddInfo->BecomingReadyRetryCount > 0)))
+        {
+            retry = SenseInfoInterpretForZPODD(DeviceExtension,
+                                               Srb,
+                                               Status,
+                                               &retryIntervalIn100ns);
+        }
+
+        if (retry == FALSE)
+        {
+            // 2. Error Processing
+            if ((zpoddInfo != NULL) &&
+                (zpoddInfo->BecomingReadyRetryCount > 0))
+            {
+                zpoddInfo->BecomingReadyRetryCount = 0;
+            }
+
+            retry = SenseInfoInterpret(DeviceExtension,
+                                       Request,
+                                       Srb,
+                                       RetriedCount,
+                                       Status,
+                                       &retryIntervalIn100ns);
+        }
+    }
+
+    if (RetryIntervalIn100ns != NULL)
+    {
+        *RetryIntervalIn100ns = retryIntervalIn100ns;
+    }
+
+    return retry;
+}
+
+
+BOOLEAN
+RequestSenseInfoInterpretForScratchBuffer(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_    ULONG                   RetriedCount,
+    _Out_   NTSTATUS*               Status,
+    _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+            LONGLONG*               RetryIntervalIn100ns
+    )
+/*++
+
+Routine Description:
+
+    to analyze the error occurred and set the status, retry interval and decide to retry or not.
+
+Arguments:
+
+    DeviceExtension - device extension
+    RetriedCount - already retried count.
+
+Return Value:
+
+    BOOLEAN - TRUE (should retry)
+    Status - NTSTATUS
+    RetryIntervalIn100nsUnits - retry interval
+
+--*/
+{
+    NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse != 0);
+
+    return RequestSenseInfoInterpret(DeviceExtension,
+                                     DeviceExtension->ScratchContext.ScratchRequest,
+                                     DeviceExtension->ScratchContext.ScratchSrb,
+                                     RetriedCount,
+                                     Status,
+                                     RetryIntervalIn100ns);
+}
+
+
diff --git a/drivers/storage/class/cdrom_new/zpodd.c b/drivers/storage/class/cdrom_new/zpodd.c
new file mode 100644 (file)
index 0000000..9fea850
--- /dev/null
@@ -0,0 +1,832 @@
+/*++
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    zpodd.c
+
+Abstract:
+
+    Code for Zero Power ODD support.
+
+Environment:
+
+    kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "ntddk.h"
+#include "ntddstor.h"
+#include "wdmguid.h"
+#include "cdrom.h"
+#include "mmc.h"
+#include "ioctl.h"
+#include "scratch.h"
+
+#ifdef DEBUG_USE_WPP
+#include "zpodd.tmh"
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetZPODDEnabledFromRegistry();
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceQueryD3ColdInterface(
+    _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _Out_   PD3COLD_SUPPORT_INTERFACE   D3ColdInterface
+    );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendEnableIdlePowerIoctl(
+    _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _In_    BOOLEAN                     WakeCapable,
+    _In_    BOOLEAN                     Enable,
+    _In_    ULONG                       D3IdleTimeout
+    );
+
+#if ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DeviceInitializeZPODD)
+#pragma alloc_text(PAGE, DeviceGetZPODDEnabledFromRegistry)
+#pragma alloc_text(PAGE, DeviceQueryD3ColdInterface)
+#pragma alloc_text(PAGE, DeviceSendEnableIdlePowerIoctl)
+#pragma alloc_text(PAGE, DeviceReleaseZPODDResources)
+#pragma alloc_text(PAGE, DeviceZPODDIsInHomePosition)
+#pragma alloc_text(PAGE, DeviceMarkActive)
+
+#endif
+
+#pragma warning(push)
+#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
+#pragma warning(disable:26000) // read overflow reported because of pointer type conversion
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceInitializeZPODD(
+    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine initialize the contents of ZPODD structure.
+
+Arguments:
+
+    DeviceExtension - the device extension
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                            status = STATUS_SUCCESS;
+    NTSTATUS                            tempStatus = STATUS_SUCCESS;
+    PZERO_POWER_ODD_INFO                zpoddInfo = NULL;
+    PFEATURE_DATA_REMOVABLE_MEDIUM      removableMediumHeader = NULL;
+    ULONG                               ZPODDEnabledInRegistry = 0;
+    PD3COLD_SUPPORT_INTERFACE           d3ColdInterface = NULL;
+    DEVICE_WAKE_DEPTH                   deepestWakeableDstate = DeviceWakeDepthNotWakeable;
+    BOOLEAN                             inHomePosition = FALSE;
+
+    PAGED_CODE();
+
+    if (DeviceExtension->ZeroPowerODDInfo != NULL)
+    {
+        //
+        // Already initialized.
+        //
+
+        goto Cleanup;
+    }
+
+    ZPODDEnabledInRegistry = DeviceGetZPODDEnabledFromRegistry();
+
+    if (ZPODDEnabledInRegistry == 0)
+    {
+        //
+        // User has explicitly disabled Zero Power ODD.
+        //
+
+        status = STATUS_NOT_SUPPORTED;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: ZPODD not enabled due to registry settings.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    zpoddInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
+                                      sizeof(ZERO_POWER_ODD_INFO),
+                                      CDROM_TAG_ZERO_POWER_ODD);
+
+    if (zpoddInfo == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+
+        goto Cleanup;
+    }
+
+    RtlZeroMemory(zpoddInfo, sizeof (ZERO_POWER_ODD_INFO));
+
+    //
+    // Check the system for the following prerequisites:
+    //
+    // 1. SATA: Device Attention line
+    // 2. SATA: Asynchronous Notification
+    // 3. ODD:  LoChange / MediaRemoval
+    // 4. ACPI: Wake capable
+    //
+    // Only drawer and slot loading types have well defined behaviors in the spec, so only these two
+    // types are supported.
+    //
+
+    //
+    // Check for DA & AN
+    //
+
+    if ((DeviceExtension->PowerDescriptor == NULL) ||
+        (DeviceExtension->PowerDescriptor->DeviceAttentionSupported == FALSE) ||
+        (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported == FALSE))
+    {
+        status = STATUS_NOT_SUPPORTED;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: ZPODD not enabled due to SATA features not present.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    //
+    // Check for LoChange / MediaRemoval
+    //
+
+    removableMediumHeader = (PFEATURE_DATA_REMOVABLE_MEDIUM)
+                            DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
+                                                  DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
+                                                  FeatureRemovableMedium);
+
+    if ((removableMediumHeader == NULL) ||
+        (!((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_TRAY) && (removableMediumHeader->Load == 0) &&     // Drawer ...
+         (removableMediumHeader->DBML != FALSE)) &&                                                                         // requires LoChange/NotBusy
+        !((removableMediumHeader->LoadingMechanism == LOADING_MECHANISM_CADDY) && (removableMediumHeader->Load == 0) &&     // Slot ...
+         (DeviceExtension->MediaChangeDetectionInfo->Gesn.Supported != FALSE))))                                            // requires MediaRemoval
+    {
+        status = STATUS_NOT_SUPPORTED;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: ZPODD not enabled due to ODD features not present.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    zpoddInfo->LoadingMechanism = removableMediumHeader->LoadingMechanism;
+    zpoddInfo->Load = removableMediumHeader->Load;
+
+    //
+    // Check for ACPI
+    //
+
+    status = DeviceQueryD3ColdInterface(DeviceExtension, &zpoddInfo->D3ColdInterface);
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: Query D3Cold support interface failed.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    //
+    // If the platform supports Zero Power ODD, the following conditions must be met:
+    //
+    // 1. The deepest wakeable D-state for the device is D3Cold;
+    // 2. The platform supports D3Cold for the device.
+    //
+
+    d3ColdInterface = &zpoddInfo->D3ColdInterface;
+
+    status = d3ColdInterface->GetIdleWakeInfo(d3ColdInterface->Context,
+                                              PowerSystemWorking,
+                                              &deepestWakeableDstate);
+
+    if (!NT_SUCCESS(status))
+    {
+        goto Cleanup;
+    }
+
+    //
+    // DeviceExtension->PowerDescriptor->D3ColdSupported is retrieved from lower layer.
+    // It has more accurate supportive information than just uses d3ColdInterface->GetD3ColdCapability
+    //
+    if ((deepestWakeableDstate != DeviceWakeDepthD3cold) ||
+        (DeviceExtension->PowerDescriptor->D3ColdSupported == FALSE))
+    {
+        status = STATUS_NOT_SUPPORTED;
+
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: ZPODD not enabled due to ACPI support not present.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    //
+    // The system meets all requirements. Go ahead and enable ZPODD.
+    //
+
+    //
+    // Register with the runtime power framework.
+    // Note that no un-registration is needed during tear-down.
+    // D3Cold will be enabled (success case of following call) or disabled by port driver during processing Enable Idle Power IOCTL.
+    //
+
+    status = DeviceSendEnableIdlePowerIoctl(DeviceExtension, TRUE, TRUE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
+
+    if (!NT_SUCCESS(status))
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                    "DeviceInitializeZPODD: ZPODD not enabled due to runtime power framework.\n"
+                    ));
+
+        goto Cleanup;
+    }
+
+    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                "DeviceInitializeZPODD: ZPODD is enabled.\n"
+                ));
+
+    DeviceExtension->ZeroPowerODDInfo = zpoddInfo;
+
+    //
+    // If device is not in home position, then we should take an active reference here
+    // to prevent it from being powered off.
+    //
+
+    inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension);
+
+    if (inHomePosition == FALSE)
+    {
+        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
+                   "DeviceInitializeZPODD: not ready to power off, device marked as active\n"));
+
+        DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+    }
+    else
+    {
+        //
+        // cache get configuration response.
+        // failing is not critical, so we don't want to check for status here.
+        //
+
+        if (zpoddInfo->GetConfigurationBuffer == NULL)
+        {
+            tempStatus = DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
+                                                         &zpoddInfo->GetConfigurationBuffer,
+                                                         &zpoddInfo->GetConfigurationBufferSize,
+                                                         FeatureProfileList,
+                                                         SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+
+            UNREFERENCED_PARAMETER(tempStatus); // Avoid PREFAST warning.
+        }
+    }
+
+Cleanup:
+
+    if (!NT_SUCCESS(status))
+    {
+        //
+        // We register always even in non-ZPODD case, per request from storport.
+        //
+
+        tempStatus = DeviceSendEnableIdlePowerIoctl(DeviceExtension, FALSE, FALSE, DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS);
+
+        if (NT_SUCCESS(tempStatus))
+        {
+            //
+            // Mark the device active; this reference will never be released unless the system enters a
+            // low power state.
+            //
+
+            DeviceMarkActive(DeviceExtension, TRUE, FALSE);
+        }
+
+        FREE_POOL(zpoddInfo);
+    }
+
+    //
+    // If Zero Power ODD is not supported, we should not block the device init sequence.
+    //
+
+    return STATUS_SUCCESS;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+DeviceGetZPODDEnabledFromRegistry()
+/*++
+
+Routine Description:
+
+    Get the ZeroPowerODDEnabled value from registry, which dictates if Zero Power ODD
+    should be enabled or not. If the value is not in registry, by default Zero
+    Power ODD is enabled.
+
+Arguments:
+
+    None
+
+Return Value:
+
+    ULONG
+
+--*/
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    WDFKEY      registryKey = NULL;
+    ULONG       ZPODDEnabled = 0;
+
+    DECLARE_CONST_UNICODE_STRING(registryValueName, L"ZeroPowerODDEnabled");
+
+    PAGED_CODE();
+
+    //
+    // open the Parameters key under the service key.
+    //
+
+    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
+                                                KEY_READ,
+                                                WDF_NO_OBJECT_ATTRIBUTES,
+                                                &registryKey);
+
+    if (NT_SUCCESS(status))
+    {
+        status = WdfRegistryQueryULong(registryKey,
+                                       &registryValueName,
+                                       &ZPODDEnabled);
+
+        WdfRegistryClose(registryKey);
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        //
+        // By default, Zero Power ODD is enabled
+        //
+
+        ZPODDEnabled = 1;
+    }
+
+    return ZPODDEnabled;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceQueryD3ColdInterface(
+    _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _Out_   PD3COLD_SUPPORT_INTERFACE   D3ColdInterface
+    )
+/*++
+
+Routine Description:
+
+    Queries ACPI for the D3Cold support interface.
+
+Arguments:
+
+    DeviceExtension - the device extension
+    D3ColdInterface - output buffer receiving the interface
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    PIRP                irp = NULL;
+    KEVENT              event;
+    NTSTATUS            status = STATUS_SUCCESS;
+    PDEVICE_OBJECT      targetDevice = NULL;
+    IO_STATUS_BLOCK     ioStatus = {0};
+    PIO_STACK_LOCATION  irpStack = NULL;
+
+    PAGED_CODE();
+
+    RtlZeroMemory(D3ColdInterface, sizeof(D3COLD_SUPPORT_INTERFACE));
+
+    //
+    // Query D3COLD support interface synchronously
+    //
+
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    targetDevice = IoGetAttachedDeviceReference(DeviceExtension->DeviceObject);
+
+    irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       targetDevice,
+                                       NULL,
+                                       0,
+                                       0,
+                                       &event,
+                                       &ioStatus);
+
+    if (irp == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+
+        goto Cleanup;
+    }
+
+    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    irp->IoStatus.Information = 0;
+
+    irpStack = IoGetNextIrpStackLocation(irp);
+    irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
+    irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_D3COLD_SUPPORT_INTERFACE;
+    irpStack->Parameters.QueryInterface.Size = sizeof (D3COLD_SUPPORT_INTERFACE);
+    irpStack->Parameters.QueryInterface.Version = D3COLD_SUPPORT_INTERFACE_VERSION;
+    irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) D3ColdInterface;
+    irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
+
+    status = IoCallDriver(targetDevice, irp);
+
+    if (status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+        status = ioStatus.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+    {
+        goto Cleanup;
+    }
+
+    NT_ASSERT(D3ColdInterface->SetD3ColdSupport != NULL);
+    NT_ASSERT(D3ColdInterface->GetIdleWakeInfo != NULL);
+    NT_ASSERT(D3ColdInterface->GetD3ColdCapability != NULL);
+
+Cleanup:
+
+    ObDereferenceObject(targetDevice);
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+DeviceSendEnableIdlePowerIoctl(
+    _In_    PCDROM_DEVICE_EXTENSION     DeviceExtension,
+    _In_    BOOLEAN                     WakeCapable,
+    _In_    BOOLEAN                     Enable,
+    _In_    ULONG                       D3IdleTimeout
+    )
+/*++
+
+Routine Description:
+
+    Enables idle power support.
+
+Arguments:
+
+    DeviceExtension - the device extension
+    WakeCapable - whether the device is wake capable
+    Enable - enable / disable idle power management
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    STORAGE_IDLE_POWER  idlePower = {0};
+    IO_STATUS_BLOCK     ioStatus = {0};
+    PIRP                irp = NULL;
+    KEVENT              event;
+
+    PAGED_CODE();
+
+    idlePower.Version = 1;
+    idlePower.Size = sizeof (STORAGE_IDLE_POWER);
+    idlePower.WakeCapableHint = WakeCapable;
+    idlePower.D3ColdSupported = Enable;
+    idlePower.D3IdleTimeout = D3IdleTimeout;
+
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_ENABLE_IDLE_POWER,
+                                        DeviceExtension->LowerPdo,
+                                        &idlePower,
+                                        sizeof(STORAGE_IDLE_POWER),
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &event,
+                                        &ioStatus);
+
+    if (irp == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        //
+        // Send the synchronous request to port driver.
+        //
+
+        status = IoCallDriver(DeviceExtension->LowerPdo, irp);
+
+        if (status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+            status = ioStatus.Status;
+        }
+    }
+
+    TracePrint((TRACE_LEVEL_INFORMATION,
+                TRACE_FLAG_POWER,
+                "DeviceSendEnableIdlePowerIoctl: Port driver returned status (%x) for FDO (%p)\n"
+                "\tD3ColdSupported: %u\n"
+                "\tD3IdleTimeout: %u (ms)",
+                status,
+                DeviceExtension->DeviceObject,
+                Enable,
+                DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS));
+
+    return status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+VOID
+DeviceReleaseZPODDResources(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    This routine will cleanup any resources allocated for ZPODD.
+
+Arguments:
+
+    DeviceExtension - the device context
+
+Return Value:
+    None.
+
+--*/
+{
+    PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    PAGED_CODE()
+
+    if (zpoddInfo != NULL)
+    {
+        FREE_POOL(zpoddInfo->GetConfigurationBuffer);
+        FREE_POOL(zpoddInfo);
+    }
+
+    return;
+}
+
+NTSTATUS
+DeviceZPODDGetPowerupReason(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _Out_   PSTORAGE_IDLE_POWERUP_REASON PowerupReason
+    )
+/*++
+
+Routine Description:
+
+    This routine queries the port driver for what caused the power up.
+
+Arguments:
+
+    DeviceExtension - device extension.
+    PowerupReason - what caused the power up.
+
+Return Value:
+    NTSTATUS
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PIRP                    irp = NULL;
+    IO_STATUS_BLOCK         ioStatus = {0};
+    KEVENT                  event;
+
+    RtlZeroMemory(PowerupReason, sizeof (STORAGE_IDLE_POWERUP_REASON));
+
+    PowerupReason->Size = sizeof (STORAGE_IDLE_POWERUP_REASON);
+    PowerupReason->Version = STORAGE_IDLE_POWERUP_REASON_VERSION_V1;
+
+    //
+    // Setup a synchronous irp.
+    //
+
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_IDLE_POWERUP_REASON,
+                                        DeviceExtension->LowerPdo,
+                                        PowerupReason,
+                                        sizeof (STORAGE_IDLE_POWERUP_REASON),
+                                        PowerupReason,
+                                        sizeof (STORAGE_IDLE_POWERUP_REASON),
+                                        FALSE,
+                                        &event,
+                                        &ioStatus);
+
+    if (irp == NULL)
+    {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        //
+        // Send the synchronous request to port driver.
+        //
+
+        status = IoCallDriver(DeviceExtension->LowerPdo, irp);
+
+        if (status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+            status = ioStatus.Status;
+        }
+    }
+
+    return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+DeviceZPODDIsInHomePosition(
+    _In_    PCDROM_DEVICE_EXTENSION DeviceExtension
+    )
+/*++
+
+Routine Description:
+
+    Checks to see if the device is ready to be powered off.
+    Requirements are: 1. tray closed 2. no media present.
+
+Arguments:
+
+    DeviceExtension - device extension.
+
+Return Value:
+    BOOLEAN
+
+--*/
+{
+    NTSTATUS             status = STATUS_SUCCESS;
+    PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+    SCSI_REQUEST_BLOCK   srb = {0};
+    PCDB                 cdb = (PCDB) srb.Cdb;
+    BOOLEAN              inHomePosition = FALSE;
+
+    PAGED_CODE();
+
+    if (zpoddInfo != NULL)
+    {
+        //
+        // Clear sense data.
+        //
+
+        zpoddInfo->SenseKey = 0;
+        zpoddInfo->AdditionalSenseCode = 0;
+        zpoddInfo->AdditionalSenseCodeQualifier = 0;
+
+        //
+        // Send a Test Unit Ready to check media & tray status.
+        //
+
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        srb.CdbLength = 6;
+        cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+        srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
+
+        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
+                                            &srb,
+                                            NULL,
+                                            0,
+                                            FALSE,
+                                            NULL);
+
+#ifdef __REACTOS__
+        if (!NT_SUCCESS(status))
+        {
+            return FALSE;
+        }
+#endif
+
+        //
+        // At this time, sense data, if available, is already copied into zpoddInfo.
+        //
+        // We don't check for status because we expect it to fail in case there is no media in device.
+        //
+        // Should enter Zero Power state if:
+        //
+        // Drawer:  02/3A/01
+        // Slot:    02/3A/xx
+        //
+
+        if (((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0) && // Drawer
+             (zpoddInfo->SenseKey                       == SCSI_SENSE_NOT_READY) &&
+             (zpoddInfo->AdditionalSenseCode            == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) &&
+             (zpoddInfo->AdditionalSenseCodeQualifier   == 0x01)) ||
+            ((zpoddInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && (zpoddInfo->Load == 0) && // Slot
+             (zpoddInfo->SenseKey                       == SCSI_SENSE_NOT_READY) &&
+             (zpoddInfo->AdditionalSenseCode            == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)))
+        {
+            inHomePosition = TRUE;
+        }
+    }
+
+    return inHomePosition;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+DeviceMarkActive(
+    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
+    _In_ BOOLEAN                 IsActive,
+    _In_ BOOLEAN                 SetIdleTimeout
+    )
+/*++
+
+Routine Description:
+
+    This routine will mark the device as active / idle.
+
+Arguments:
+
+    DeviceExtension - the device context
+    IsActive - if the device should be marked as active
+
+Return Value:
+    None.
+
+--*/
+{
+    NTSTATUS                status = STATUS_SUCCESS;
+    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
+
+    PAGED_CODE()
+
+    if (DeviceExtension->IsActive != IsActive)
+    {
+        if ((IsActive == FALSE) && (zpoddInfo != NULL))
+        {
+            // cache get configuration response.
+            // failing is not critical, so we don't want to check for status here.
+            if (zpoddInfo->GetConfigurationBuffer == NULL)
+            {
+                (VOID)DeviceGetConfigurationWithAlloc(DeviceExtension->Device,
+                                                      &zpoddInfo->GetConfigurationBuffer,
+                                                      &zpoddInfo->GetConfigurationBufferSize,
+                                                      FeatureProfileList,
+                                                      SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+            }
+        }
+
+        if (SetIdleTimeout)
+        {
+            status = DeviceSendEnableIdlePowerIoctl(DeviceExtension,
+                                                    FALSE,
+                                                    FALSE,
+                                                    IsActive ? DELAY_TIME_TO_ENTER_ZERO_POWER_IN_MS : DELAY_TIME_TO_ENTER_AOAC_IDLE_POWER_IN_MS);
+        }
+
+        if (NT_SUCCESS(status))
+        {
+            DeviceSendIoctlAsynchronously(DeviceExtension,
+                                          IsActive ? IOCTL_STORAGE_POWER_ACTIVE : IOCTL_STORAGE_POWER_IDLE,
+                                          DeviceExtension->LowerPdo);
+        }
+
+        DeviceExtension->IsActive = IsActive;
+    }
+}
+
+#pragma warning(pop) // un-sets any local warning changes
+
+