[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               :