https://git.reactos.org/?p=reactos.git;a=commitdiff;h=83b85e2124dcfc01a2351…
commit 83b85e2124dcfc01a23516d1c07b84f520507e88
Author: Victor Perevertkin <victor.perevertkin(a)reactos.org>
AuthorDate: Fri Oct 16 04:37:10 2020 +0300
Commit: Victor Perevertkin <victor.perevertkin(a)reactos.org>
CommitDate: Fri Oct 16 04:37:10 2020 +0300
[CDROM_NEW] Import Microsoft CDROM class driver from GitHub
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
---
drivers/storage/class/cdrom_new/CMakeLists.txt | 41 +
drivers/storage/class/cdrom_new/aacs.c | 1481 ++++
drivers/storage/class/cdrom_new/autorun.c | 3080 ++++++++
drivers/storage/class/cdrom_new/cdrom.c | 4242 +++++++++++
drivers/storage/class/cdrom_new/cdrom.h | 1624 +++++
drivers/storage/class/cdrom_new/cdrom.inf | 159 +
drivers/storage/class/cdrom_new/cdrom.rc | 23 +
drivers/storage/class/cdrom_new/cdromp.h | 383 +
drivers/storage/class/cdrom_new/common.c | 3878 ++++++++++
drivers/storage/class/cdrom_new/data.c | 283 +
drivers/storage/class/cdrom_new/guid.c | 16 +
drivers/storage/class/cdrom_new/init.c | 2742 +++++++
drivers/storage/class/cdrom_new/ioctl.c | 9134 ++++++++++++++++++++++++
drivers/storage/class/cdrom_new/ioctl.h | 847 +++
drivers/storage/class/cdrom_new/license.txt | 23 +
drivers/storage/class/cdrom_new/localwpp.ini | 17 +
drivers/storage/class/cdrom_new/mmc.c | 1629 +++++
drivers/storage/class/cdrom_new/mmc.h | 122 +
drivers/storage/class/cdrom_new/pnppower.c | 689 ++
drivers/storage/class/cdrom_new/scratch.c | 1467 ++++
drivers/storage/class/cdrom_new/scratch.h | 187 +
drivers/storage/class/cdrom_new/sense.c | 2600 +++++++
drivers/storage/class/cdrom_new/zpodd.c | 832 +++
23 files changed, 35499 insertions(+)
diff --git a/drivers/storage/class/cdrom_new/CMakeLists.txt
b/drivers/storage/class/cdrom_new/CMakeLists.txt
new file mode 100644
index 00000000000..7def3bc655d
--- /dev/null
+++ b/drivers/storage/class/cdrom_new/CMakeLists.txt
@@ -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
index 00000000000..f81eacb372c
--- /dev/null
+++ b/drivers/storage/class/cdrom_new/aacs.c
@@ -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
index 00000000000..e5c9b110021
--- /dev/null
+++ b/drivers/storage/class/cdrom_new/autorun.c
@@ -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(¶ms,
+ WDF_REQUEST_REUSE_NO_FLAGS,
+ STATUS_NOT_SUPPORTED);
+
+ status = WdfRequestReuse(info->MediaChangeRequest, ¶ms);
+ }
+
+ 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(¶meterKeyName, L"Parameters");
+
+ status = WdfRegistryOpenKey(serviceKey,
+ ¶meterKeyName,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ ¶metersKey);
+ }
+
+ 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
index 00000000000..ac0c9c63090
--- /dev/null
+++ b/drivers/storage/class/cdrom_new/cdrom.c
@@ -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,
+ ®istryKeyName,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ ®istryKey);
+
+ 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));
... 30678 lines suppressed ...