/*--

Copyright (C) Microsoft Corporation. All rights reserved.

Module Name:

    ioctl.c

Abstract:

    Include all funtions for processing IOCTLs

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#include "stddef.h"
#include "string.h"

#include "ntddk.h"
#include "ntddstor.h"
#include "cdrom.h"
#include "ioctl.h"
#include "scratch.h"
#include "mmc.h"


#ifdef DEBUG_USE_WPP
#include "ioctl.tmh"
#endif


#define FirstDriveLetter 'C'
#define LastDriveLetter  'Z'

#if DBG
    LPCSTR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = {
        "Physical",
        "Copyright",
        "DiskKey",
        "BCA",
        "Manufacturer",
        "Unknown"
    };
#endif // DBG

_IRQL_requires_max_(APC_LEVEL)
VOID
GetConfigurationDataConversionTypeAllToTypeOne(
    _In_    FEATURE_NUMBER       RequestedFeature,
    _In_    PSCSI_REQUEST_BLOCK  Srb,
    _Out_   size_t *             DataLength
    );

_IRQL_requires_max_(APC_LEVEL)
VOID
GetConfigurationDataSynthesize(
    _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
    _In_                            ULONG           InputBufferSize,
    _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
    _In_                            size_t          OutputBufferSize,
    _In_                            FEATURE_NUMBER  StartingFeature,
    _In_                            ULONG           RequestType,
    _Out_                           size_t *        DataLength
    );

_IRQL_requires_max_(APC_LEVEL)
PCDB
RequestGetScsiPassThroughCdb(
    _In_ PIRP Irp
    );

#ifdef ALLOC_PRAGMA

#pragma alloc_text(PAGE, DeviceIsPlayActive)
#pragma alloc_text(PAGE, RequestHandleGetDvdRegion)
#pragma alloc_text(PAGE, RequestHandleReadTOC)
#pragma alloc_text(PAGE, RequestHandleReadTocEx)
#pragma alloc_text(PAGE, RequestHandleGetConfiguration)
#pragma alloc_text(PAGE, RequestHandleGetDriveGeometry)
#pragma alloc_text(PAGE, RequestHandleDiskVerify)
#pragma alloc_text(PAGE, RequestHandleCheckVerify)
#pragma alloc_text(PAGE, RequestHandleFakePartitionInfo)
#pragma alloc_text(PAGE, RequestHandleEjectionControl)
#pragma alloc_text(PAGE, RequestHandleEnableStreaming)
#pragma alloc_text(PAGE, RequestHandleSendOpcInformation)
#pragma alloc_text(PAGE, RequestHandleGetPerformance)
#pragma alloc_text(PAGE, RequestHandleMcnSyncFakeIoctl)
#pragma alloc_text(PAGE, RequestHandleLoadEjectMedia)
#pragma alloc_text(PAGE, RequestHandleReserveRelease)
#pragma alloc_text(PAGE, RequestHandlePersistentReserve)
#pragma alloc_text(PAGE, DeviceHandleRawRead)
#pragma alloc_text(PAGE, DeviceHandlePlayAudioMsf)
#pragma alloc_text(PAGE, DeviceHandleReadQChannel)
#pragma alloc_text(PAGE, ReadQChannel)
#pragma alloc_text(PAGE, DeviceHandlePauseAudio)
#pragma alloc_text(PAGE, DeviceHandleResumeAudio)
#pragma alloc_text(PAGE, DeviceHandleSeekAudioMsf)
#pragma alloc_text(PAGE, DeviceHandleStopAudio)
#pragma alloc_text(PAGE, DeviceHandleGetSetVolume)
#pragma alloc_text(PAGE, DeviceHandleReadDvdStructure)
#pragma alloc_text(PAGE, ReadDvdStructure)
#pragma alloc_text(PAGE, DeviceHandleDvdEndSession)
#pragma alloc_text(PAGE, DeviceHandleDvdStartSessionReadKey)
#pragma alloc_text(PAGE, DvdStartSessionReadKey)
#pragma alloc_text(PAGE, DeviceHandleDvdSendKey)
#pragma alloc_text(PAGE, DvdSendKey)
#pragma alloc_text(PAGE, DeviceHandleSetReadAhead)
#pragma alloc_text(PAGE, DeviceHandleSetSpeed)
#pragma alloc_text(PAGE, RequestHandleExclusiveAccessQueryLockState)
#pragma alloc_text(PAGE, RequestHandleExclusiveAccessLockDevice)
#pragma alloc_text(PAGE, RequestHandleExclusiveAccessUnlockDevice)
#pragma alloc_text(PAGE, RequestHandleScsiPassThrough)
#pragma alloc_text(PAGE, RequestGetScsiPassThroughCdb)
#pragma alloc_text(PAGE, GetConfigurationDataConversionTypeAllToTypeOne)
#pragma alloc_text(PAGE, GetConfigurationDataSynthesize)

#endif

NTSTATUS
RequestHandleUnknownIoctl(
    _In_ WDFDEVICE    Device,
    _In_ WDFREQUEST   Request
    )
/*++

Routine Description:

    All unknown IOCTLs will be forward to lower level driver.

Arguments:

    Device - device object
    Request - request to be handled

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_UNSUCCESSFUL;
    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
    PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
    BOOLEAN                     syncRequired = requestContext->SyncRequired;

    ULONG                       sendOptionsFlags = 0;
    BOOLEAN                     requestSent = FALSE;

    WdfRequestFormatRequestUsingCurrentType(Request);

    if (syncRequired)
    {
        sendOptionsFlags = WDF_REQUEST_SEND_OPTION_SYNCHRONOUS;
    }
    else
    {
        WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL);
    }

    status = RequestSend(deviceExtension,
                         Request,
                         deviceExtension->IoTarget,
                         sendOptionsFlags,
                         &requestSent);

    if (requestSent)
    {
        if (syncRequired)
        {
            // the request needs to be completed here.
            RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
        }
    }
    else
    {
        // failed to send the request to IoTarget
        RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
BOOLEAN
DeviceIsPlayActive(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This routine determines if the cd is currently playing music.

Arguments:

    Device - Device object.

Return Value:

    BOOLEAN - TRUE if the device is playing music.

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
    PSUB_Q_CURRENT_POSITION currentBuffer;
    size_t                  bytesRead = 0;

    PAGED_CODE ();

    // if we don't think it is playing audio, don't bother checking.
    if (!deviceExtension->DeviceAdditionalData.PlayActive)
    {
        return FALSE;
    }

    // Allocate the required memory
    NT_ASSERT(sizeof(SUB_Q_CURRENT_POSITION) >= sizeof(CDROM_SUB_Q_DATA_FORMAT));
    currentBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                          sizeof(SUB_Q_CURRENT_POSITION),
                                          CDROM_TAG_PLAY_ACTIVE);
    if (currentBuffer == NULL)
    {
        return FALSE;
    }

    // set the options in the output buffer format
    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;

    // Send SCSI command to read Q Channel information.
    status = ReadQChannel(deviceExtension,
                          NULL,
                          currentBuffer,
                          sizeof(CDROM_SUB_Q_DATA_FORMAT),
                          currentBuffer,
                          sizeof(SUB_Q_CURRENT_POSITION),
                          &bytesRead);

    if (!NT_SUCCESS(status))
    {
        ExFreePool(currentBuffer);
        return FALSE;
    }

    // update the playactive flag appropriately
    if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
    {
        deviceExtension->DeviceAdditionalData.PlayActive = TRUE;
    }
    else
    {
        deviceExtension->DeviceAdditionalData.PlayActive = FALSE;
    }

    ExFreePool(currentBuffer);

    return deviceExtension->DeviceAdditionalData.PlayActive;
}

NTSTATUS
RequestHandleGetInquiryData(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength)
/*++

Routine Description:

   Handler for IOCTL_CDROM_GET_INQUIRY_DATA

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;
    PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData);

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
    {
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else
    {
        PVOID outputBuffer = NULL;

        *DataLength = min(cdData->CachedInquiryDataByteCount,
                          RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);

        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);

        if (NT_SUCCESS(status) &&
            (outputBuffer != NULL))
        {
            // Always copy as much data as possible
            RtlCopyMemory(outputBuffer,
                          cdData->CachedInquiryData,
                          *DataLength);
        }

        // and finally decide between two possible status values
        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < cdData->CachedInquiryDataByteCount)
        {
            status = STATUS_BUFFER_OVERFLOW;
        }
    }

    return status;
}


NTSTATUS
RequestHandleGetMediaTypeEx(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handler for IOCTL_STORAGE_GET_MEDIA_TYPES_EX

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);

    PGET_MEDIA_TYPES        mediaTypes = NULL;
    PDEVICE_MEDIA_INFO      mediaInfo = NULL; //&mediaTypes->MediaInfo[0];
    ULONG                   sizeNeeded = 0;
    PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;

    *DataLength = 0;

    // Must run below dispatch level.
    if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
    {
        NT_ASSERT(FALSE);
        return STATUS_INVALID_LEVEL;
    }

    sizeNeeded = sizeof(GET_MEDIA_TYPES);

    // IsMmc is static...
    if (cdData->Mmc.IsMmc)
    {
        sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types
    }

    status = WdfRequestRetrieveOutputBuffer(Request,
                                            sizeNeeded,
                                            (PVOID*)&mediaTypes,
                                            NULL);

    if (NT_SUCCESS(status) &&
        (mediaTypes != NULL))
    {
        mediaInfo = &mediaTypes->MediaInfo[0];

        RtlZeroMemory(mediaTypes, sizeNeeded);

        // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX

        mediaTypes->DeviceType = cdData->DriveDeviceType;

        mediaTypes->MediaInfoCount = 1;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
        mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;

        if (cdData->Mmc.IsMmc)
        {
            // also report a removable disk
            mediaTypes->MediaInfoCount += 1;

            mediaInfo++;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
            mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;
            mediaInfo--;

        }

        // Status will either be success, if media is present, or no media.
        // It would be optimal to base from density code and medium type, but not all devices
        // have values for these fields.

        // Send a TUR to determine if media is present, only if the device is not in ZPODD mode.
        if ((!EXCLUSIVE_MODE(cdData) ||
             EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request))) &&
            ((zpoddInfo == NULL) ||
             (zpoddInfo->InZeroPowerState == FALSE)))
        {
            SCSI_REQUEST_BLOCK  srb;
            PCDB                cdb = (PCDB)srb.Cdb;

            RtlZeroMemory(&srb,sizeof(SCSI_REQUEST_BLOCK));

            srb.CdbLength = 6;
            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

            srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;

            status = DeviceSendSrbSynchronously(DeviceExtension->Device,
                                                &srb,
                                                NULL,
                                                0,
                                                FALSE,
                                                Request);

            if (NT_SUCCESS(status))
            {
                // set the disk's media as current if we can write to it.
                if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed)
                {
                    mediaInfo++;
                    SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
                             MEDIA_CURRENTLY_MOUNTED);
                    mediaInfo--;
                }
                else
                {
                    SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
                             MEDIA_CURRENTLY_MOUNTED);
                }
            }
            else
            {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                           "RequestHandleGetMediaTypeEx: GET_MEDIA_TYPES status of TUR - %lx\n", status));
            }
        }

        // per legacy cdrom behavior, always return success
        status = STATUS_SUCCESS;
    }

    *DataLength = sizeNeeded;

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleGetDvdRegion(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handler for IOCTL_DVD_GET_REGION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;

    PVOID               outputBuffer = NULL;
    size_t              bytesReturned = 0;

    PDVD_COPY_PROTECT_KEY       copyProtectKey = NULL;
    ULONG                       keyLength = 0;
    PDVD_DESCRIPTOR_HEADER      dvdHeader;
    PDVD_COPYRIGHT_DESCRIPTOR   copyRightDescriptor;
    PDVD_REGION                 dvdRegion = NULL;
    PDVD_READ_STRUCTURE         readStructure = NULL;
    PDVD_RPC_KEY                rpcKey;

    PAGED_CODE ();

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
                "RequestHandleGetDvdRegion: [%p] IOCTL_DVD_GET_REGION\n", Request));

    *DataLength = 0;

    // reject the request if it's not a DVD device.
    if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                sizeof(DVD_REGION),
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        // figure out how much data buffer we need
        keyLength = max((sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR)),
                        sizeof(DVD_READ_STRUCTURE));
        keyLength = max(keyLength,
                        DVD_RPC_KEY_LENGTH);

        // round the size to nearest ULONGLONG -- why?
        // could this be to deal with device alignment issues?
        keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1));

        readStructure = ExAllocatePoolWithTag(NonPagedPoolNx,
                                              keyLength,
                                              DVD_TAG_READ_KEY);
        if (readStructure == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory (readStructure, keyLength);
        readStructure->Format = DvdCopyrightDescriptor;

        // use READ_STRUCTURE to read copyright descriptor
        status = ReadDvdStructure(DeviceExtension,
                                  Request,
                                  readStructure,
                                  keyLength,
                                  readStructure,
                                  sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
                                  &bytesReturned);
    }

    if (NT_SUCCESS(status))
    {
        // we got the copyright descriptor, so now get the region if possible
        dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure;
        copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data;

        // the original irp's systembuffer has a copy of the info that
        // should be passed down in the request
        dvdRegion = outputBuffer;

        dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType;
        dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation;

        // now reuse the buffer to request the copy protection info
        copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure;
        RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH);
        copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
        copyProtectKey->KeyType = DvdGetRpcKey;

        // send a request for READ_KEY
        status = DvdStartSessionReadKey(DeviceExtension,
                                        IOCTL_DVD_READ_KEY,
                                        Request,
                                        copyProtectKey,
                                        DVD_RPC_KEY_LENGTH,
                                        copyProtectKey,
                                        DVD_RPC_KEY_LENGTH,
                                        &bytesReturned);
    }

    if (NT_SUCCESS(status))
    {
        // the request succeeded.  if a supported scheme is returned,
        // then return the information to the caller
        rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;

        if (rpcKey->RpcScheme == 1)
        {
            if (rpcKey->TypeCode)
            {
                dvdRegion->SystemRegion = ~rpcKey->RegionMask;
                dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
            }
            else
            {
                // the drive has not been set for any region
                dvdRegion->SystemRegion = 0;
                dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
            }

            *DataLength = sizeof(DVD_REGION);
        }
        else
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestHandleGetDvdRegion => rpcKey->RpcScheme != 1\n"));
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    // Clean up
    if (readStructure != NULL)
    {
        ExFreePool(readStructure);
    }

    return status;
}

NTSTATUS
RequestValidateRawRead(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_RAW_READ

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);

    PVOID               inputBuffer = NULL;
    PIRP                irp = NULL;
    PIO_STACK_LOCATION  currentStack = NULL;

    LARGE_INTEGER       startingOffset = {0};
    ULONGLONG           transferBytes = 0;
    ULONGLONG           endOffset;
    ULONGLONG           mdlBytes;
    RAW_READ_INFO       rawReadInfo = {0};

    *DataLength = 0;

    irp = WdfRequestWdmGetIrp(Request);
    currentStack = IoGetCurrentIrpStackLocation(irp);

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    // Check that ending sector is on disc and buffers are there and of
    // correct size.
    if (NT_SUCCESS(status) &&
        (RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer == NULL))
    {
        //  This is a call from user space.  This is the only time that we need to validate parameters.
        //  Validate the input and get the input buffer into Type3InputBuffer
        //  so the rest of the code will be uniform.
        if (inputBuffer != NULL)
        {
            currentStack->Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;
            RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;

            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(RAW_READ_INFO))
            {
                *DataLength = sizeof(RAW_READ_INFO);
                status = STATUS_BUFFER_TOO_SMALL;
            }
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        //  Since this ioctl is METHOD_OUT_DIRECT, we need to copy away the input buffer before interpreting it.
        //  This prevents a malicious app from messing with the input buffer while we are interpreting it.
        rawReadInfo = *(PRAW_READ_INFO)RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer;

        startingOffset.QuadPart = rawReadInfo.DiskOffset.QuadPart;

        if ((rawReadInfo.TrackMode == CDDA)        ||
            (rawReadInfo.TrackMode == YellowMode2) ||
            (rawReadInfo.TrackMode == XAForm2)     )
        {
            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
        }
        else if (rawReadInfo.TrackMode == RawWithSubCode)
        {
            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
        }
        else if (rawReadInfo.TrackMode == RawWithC2)
        {
            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
        }
        else if (rawReadInfo.TrackMode == RawWithC2AndSubCode)
        {
            transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
        }
        else
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                      "RequestValidateRawRead: Invalid TrackMode type %x for XA read\n",
                      rawReadInfo.TrackMode
                      ));
        }

        endOffset = (ULONGLONG)rawReadInfo.SectorCount * COOKED_SECTOR_SIZE;
        endOffset += startingOffset.QuadPart;

        // check for overflows....
        if (rawReadInfo.SectorCount == 0)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid I/O parameters for XA "
                        "Read (zero sectors requested)\n"));
            status = STATUS_INVALID_PARAMETER;
        }
        else if (transferBytes < (ULONGLONG)(rawReadInfo.SectorCount))
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid I/O parameters for XA "
                        "Read (TransferBytes Overflow)\n"));
            status = STATUS_INVALID_PARAMETER;
        }
        else if (endOffset < (ULONGLONG)startingOffset.QuadPart)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid I/O parameters for XA "
                        "Read (EndingOffset Overflow)\n"));
            status = STATUS_INVALID_PARAMETER;
        }
        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < transferBytes)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid I/O parameters for XA "
                        "Read (Bad buffer size)\n"));
            status = STATUS_INVALID_PARAMETER;
        }
        else if (endOffset > (ULONGLONG)DeviceExtension->PartitionLength.QuadPart)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid I/O parameters for XA "
                        "Read (Request Out of Bounds)\n"));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        // cannot validate the MdlAddress, since it is not included in any
        // other location per the DDK and file system calls.

        // validate the mdl describes at least the number of bytes
        // requested from us.
        mdlBytes = (ULONGLONG)MmGetMdlByteCount(irp->MdlAddress);
        if (mdlBytes < transferBytes)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestValidateRawRead: Invalid MDL %s, Irp %p\n",
                        "size (5)", irp));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        // check the buffer for alignment
        // This is important for x86 as some busses (ie ATAPI)
        // require word-aligned buffers.
        if ( ((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) &
             DeviceExtension->AdapterDescriptor->AlignmentMask )
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                      "RequestValidateRawRead: Invalid I/O parameters for "
                      "XA Read (Buffer %p not aligned with mask %x\n",
                      RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer,
                      DeviceExtension->AdapterDescriptor->AlignmentMask));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        // Validate the request is not too large for the adapter
        BOOLEAN bufferIsPageAligned = FALSE;
        ULONG   maxLength = 0;

        // if buffer is not page-aligned, then subtract one as the
        // transfer could cross a page boundary.
        if ((((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) & (PAGE_SIZE-1)) == 0)
        {
            bufferIsPageAligned = TRUE;
        }

        if (bufferIsPageAligned)
        {
            maxLength = cdData->MaxPageAlignedTransferBytes;
        }
        else
        {
            maxLength = cdData->MaxUnalignedTransferBytes;
        }

        if (transferBytes > maxLength)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                      "RequestValidateRawRead: The XA Read (type %x) would require %I64x bytes, "
                      "but the adapter can only handle %x bytes (for a%saligned buffer)\n",
                      rawReadInfo.TrackMode,
                      transferBytes,
                      maxLength,
                      (bufferIsPageAligned ? " " : "n un")
                      ));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        //
        // HACKHACK - REF #0001
        // The retry count will be in this irp's IRP_MN function,
        // as the new irp was freed, and we therefore cannot use
        // this irp's next stack location for this function.
        // This may be a good location to store this info for
        // when we remove RAW_READ (mode switching), as we will
        // no longer have the nextIrpStackLocation to play with
        // when that occurs
        //
        currentStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001
    }

    return status;
}

NTSTATUS
RequestValidateReadTocEx(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_READ_TOC_EX

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;

    PCDROM_READ_TOC_EX  inputBuffer = NULL;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
        sizeof(CDROM_READ_TOC_EX))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
             MINIMUM_CDROM_READ_TOC_EX_SIZE)
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = MINIMUM_CDROM_READ_TOC_EX_SIZE;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
             ((USHORT)-1))
    {
        status = STATUS_INVALID_PARAMETER;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
             DeviceExtension->AdapterDescriptor->AlignmentMask)
    {
        status = STATUS_INVALID_PARAMETER;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if ((inputBuffer->Reserved1 != 0) ||
            (inputBuffer->Reserved2 != 0) ||
            (inputBuffer->Reserved3 != 0))
        {
            status = STATUS_INVALID_PARAMETER;
        }
        // NOTE: when adding new formats, ensure that first two bytes
        //       specify the amount of additional data available.
        else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC     ) ||
                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) ||
                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT  ))
        {
            // SessionTrack field is used
        }
        else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) ||
                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA)     ||
                 (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP))
        {
            // SessionTrack field is reserved
            if (inputBuffer->SessionTrack != 0)
            {
                status = STATUS_INVALID_PARAMETER;
            }
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateReadToc(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_READ_TOC

Arguments:

    DeviceExtension - device context
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
        sizeof(CDROM_TOC))
    {
        // they didn't request the entire TOC -- use _EX version
        // for partial transfers and such.
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(CDROM_TOC);
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
             DeviceExtension->AdapterDescriptor->AlignmentMask)
    {
        status = STATUS_INVALID_PARAMETER;
    }

    return status;
}

NTSTATUS
RequestValidateGetLastSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_GET_LAST_SESSION

Arguments:

    DeviceExtension - device context
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
        sizeof(CDROM_TOC_SESSION_DATA))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(CDROM_TOC_SESSION_DATA);
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
             DeviceExtension->AdapterDescriptor->AlignmentMask)
    {
        status = STATUS_INVALID_PARAMETER;
    }

    return status;
}

NTSTATUS
RequestValidateReadQChannel(
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_READ_Q_CHANNEL

Arguments:

    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                 status = STATUS_SUCCESS;
    PCDROM_SUB_Q_DATA_FORMAT inputBuffer = NULL;
    ULONG                    transferByteCount = 0;

    *DataLength = 0;

    if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
       sizeof(CDROM_SUB_Q_DATA_FORMAT))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        // check for all valid types of request
        if (inputBuffer->Format == IOCTL_CDROM_CURRENT_POSITION)
        {
            transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
        }
        else if (inputBuffer->Format == IOCTL_CDROM_MEDIA_CATALOG)
        {
            transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
        }
        else if (inputBuffer->Format == IOCTL_CDROM_TRACK_ISRC)
        {
            transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
        }
        else
        {
            // Format not valid
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
            transferByteCount)
        {
            status = STATUS_BUFFER_TOO_SMALL;
            *DataLength = transferByteCount;
        }
    }

    return status;
}

NTSTATUS
RequestValidateDvdReadStructure(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_DVD_READ_STRUCTURE

Arguments:

    DeviceExtension - device context
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
           sizeof(DVD_READ_STRUCTURE))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestValidateDvdReadStructure - input buffer "
                        "length too small (was %d should be %d)\n",
                        (int)RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                        sizeof(DVD_READ_STRUCTURE)));
            status = STATUS_INVALID_PARAMETER;
        }
        else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
                sizeof(READ_DVD_STRUCTURES_HEADER))
        {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestValidateDvdReadStructure - output buffer "
                        "cannot hold header information\n"));
            status = STATUS_BUFFER_TOO_SMALL;
            *DataLength = sizeof(READ_DVD_STRUCTURES_HEADER);
        }
        else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
                MAXUSHORT)
        {
            // key length must fit in two bytes
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestValidateDvdReadStructure - output buffer "
                        "too large\n"));
            status = STATUS_INVALID_PARAMETER;
        }
        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
                 DeviceExtension->AdapterDescriptor->AlignmentMask)
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
        {
            // reject the request if it's not a DVD device.
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    return status;
}

NTSTATUS
RequestValidateDvdStartSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Validate request of IOCTL_DVD_START_SESSION

Arguments:

    DeviceExtension - device context
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
           sizeof(DVD_SESSION_ID))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestValidateDvdStartSession: DVD_START_SESSION - output "
                        "buffer too small\n"));
            status = STATUS_BUFFER_TOO_SMALL;
            *DataLength = sizeof(DVD_SESSION_ID);
        }
        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
        {
            // reject the request if it's not a DVD device.
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    return status;
}

NTSTATUS
RequestValidateDvdSendKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_DVD_SEND_KEY, IOCTL_DVD_SEND_KEY2

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDVD_COPY_PROTECT_KEY   key = NULL;

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &key,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        if((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
           (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != key->KeyLength))
        {

            //
            // Key is too small to have a header or the key length doesn't
            // match the input buffer length.  Key must be invalid
            //

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestValidateDvdSendKey: [%p] IOCTL_DVD_SEND_KEY - "
                        "key is too small or does not match KeyLength\n",
                        Request));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        // allow only certain key type (non-destructive) to go through
        // IOCTL_DVD_SEND_KEY (which only requires READ access to the device)
        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_SEND_KEY)
        {
            if ((key->KeyType != DvdChallengeKey) &&
                (key->KeyType != DvdBusKey2) &&
                (key->KeyType != DvdInvalidateAGID))
            {
                status = STATUS_INVALID_PARAMETER;
            }
        }
        else if ((key->KeyType != DvdChallengeKey) &&
                (key->KeyType != DvdBusKey1) &&
                (key->KeyType != DvdBusKey2) &&
                (key->KeyType != DvdTitleKey) &&
                (key->KeyType != DvdAsf) &&
                (key->KeyType != DvdSetRpcKey) &&
                (key->KeyType != DvdGetRpcKey) &&
                (key->KeyType != DvdDiskKey) &&
                (key->KeyType != DvdInvalidateAGID))
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
        {
            // reject the request if it's not a DVD device.
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    return status;
}

NTSTATUS
RequestValidateGetConfiguration(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_GET_CONFIGURATION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
        sizeof(GET_CONFIGURATION_HEADER))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(GET_CONFIGURATION_HEADER);
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0xffff)
    {
        // output buffer is too large
        status = STATUS_INVALID_BUFFER_SIZE;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
        DeviceExtension->AdapterDescriptor->AlignmentMask)
    {
        // buffer is not proper size multiple
        status = STATUS_INVALID_PARAMETER;
    }

    if (NT_SUCCESS(status))
    {

#if BUILD_WOW64_ENABLED && defined(_WIN64)

        if (WdfRequestIsFrom32BitProcess(Request))
        {
            PGET_CONFIGURATION_IOCTL_INPUT32 inputBuffer = NULL;

            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
                sizeof(GET_CONFIGURATION_IOCTL_INPUT32))
            {
                status = STATUS_INFO_LENGTH_MISMATCH;
            }

            //
            // also verify the arguments are reasonable.
            //
            if (NT_SUCCESS(status))
            {
                status = WdfRequestRetrieveInputBuffer(Request,
                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                                       &inputBuffer,
                                                       NULL);
            }

            if (NT_SUCCESS(status))
            {
                if (inputBuffer->Feature > 0xffff)
                {
                    status = STATUS_INVALID_PARAMETER;
                }
                else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
                {
                    status = STATUS_INVALID_PARAMETER;
                }
                else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
                {
                    status = STATUS_INVALID_PARAMETER;
                }
            }
        }
        else

#endif

        {
            PGET_CONFIGURATION_IOCTL_INPUT inputBuffer = NULL;

            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
                sizeof(GET_CONFIGURATION_IOCTL_INPUT))
            {
                status = STATUS_INFO_LENGTH_MISMATCH;
            }

            // also verify the arguments are reasonable.
            if (NT_SUCCESS(status))
            {
                status = WdfRequestRetrieveInputBuffer(Request,
                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                                       &inputBuffer,
                                                       NULL);
            }

            if (NT_SUCCESS(status))
            {
                if (inputBuffer->Feature > 0xffff)
                {
                    status = STATUS_INVALID_PARAMETER;
                }
                else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
                         (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
                {
                    status = STATUS_INVALID_PARAMETER;
                }
                else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
                {
                    status = STATUS_INVALID_PARAMETER;
                }
            }
        }
    }

    return status;
}

NTSTATUS
RequestValidateSetSpeed(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_SET_SPEED

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PCDROM_SET_SPEED        inputBuffer = NULL;
    ULONG                   requiredLength = 0;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SET_SPEED))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        // Get the request type using CDROM_SET_SPEED structure
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(CDROM_SET_SPEED),
                                               &inputBuffer,
                                               NULL);

    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->RequestType > CdromSetStreaming)
        {
            // Unknown request type.
            status = STATUS_INVALID_PARAMETER;
        }
        else if (inputBuffer->RequestType == CdromSetSpeed)
        {
            requiredLength = sizeof(CDROM_SET_SPEED);
        }
        else
        {
            // Don't send SET STREAMING command if this is not a MMC compliant device
            if (cdData->Mmc.IsMmc == FALSE)
            {
                status = STATUS_INVALID_DEVICE_REQUEST;
            }

            requiredLength = sizeof(CDROM_SET_STREAMING);
        }
    }

    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < requiredLength)
        {
            // Input buffer too small
            status = STATUS_INFO_LENGTH_MISMATCH;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsReadMediaKeyBlock(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_READ_MEDIA_KEY_BLOCK

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PAACS_LAYER_NUMBER      layerNumber = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_LAYER_NUMBER))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < 8)
    {
        // This is a variable-length structure, but we're pretty sure
        // it can never be less than eight bytes...
        *DataLength = 8;
        status = STATUS_BUFFER_TOO_SMALL;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &layerNumber,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*layerNumber > 255)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsStartSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_START_SESSION

Arguments:

    DeviceExtension - device context
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID))
    {
        *DataLength = sizeof(DVD_SESSION_ID);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    return status;
}

NTSTATUS
RequestValidateAacsSendCertificate(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_SEND_CERTIFICATE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PAACS_SEND_CERTIFICATE  inputBuffer = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CERTIFICATE))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsGetCertificate(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_GET_CERTIFICATE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PDVD_SESSION_ID         sessionId = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CERTIFICATE))
    {
        *DataLength = sizeof(AACS_CERTIFICATE);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CERTIFICATE))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &sessionId,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*sessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsGetChallengeKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_GET_CHALLENGE_KEY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PDVD_SESSION_ID         sessionId = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CHALLENGE_KEY))
    {
        *DataLength = sizeof(AACS_CHALLENGE_KEY);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CHALLENGE_KEY))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &sessionId,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*sessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsSendChallengeKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_SEND_CHALLENGE_KEY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
    PAACS_SEND_CHALLENGE_KEY    inputBuffer = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CHALLENGE_KEY))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsReadVolumeId(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_READ_VOLUME_ID

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PDVD_SESSION_ID         sessionId = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_VOLUME_ID))
    {
        *DataLength = sizeof(AACS_VOLUME_ID);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_VOLUME_ID))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &sessionId,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*sessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsReadSerialNumber(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_READ_SERIAL_NUMBER

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PDVD_SESSION_ID         sessionId = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_SERIAL_NUMBER))
    {
        *DataLength = sizeof(AACS_SERIAL_NUMBER);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_SERIAL_NUMBER))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &sessionId,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*sessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsReadMediaId(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_READ_MEDIA_ID

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
    PDVD_SESSION_ID         sessionId = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_MEDIA_ID))
    {
        *DataLength = sizeof(AACS_MEDIA_ID);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_MEDIA_ID))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &sessionId,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (*sessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateAacsBindingNonce(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_READ_BINDING_NONCE
                       IOCTL_AACS_GENERATE_BINDING_NONCE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                 status = STATUS_SUCCESS;
    PCDROM_DATA              cdData = &(DeviceExtension->DeviceAdditionalData);
    PAACS_READ_BINDING_NONCE inputBuffer = NULL;

    *DataLength = 0;

    if (!cdData->Mmc.IsAACS)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_READ_BINDING_NONCE))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_BINDING_NONCE))
    {
        *DataLength = sizeof(AACS_BINDING_NONCE);
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_BINDING_NONCE))
    {
        status = STATUS_INVALID_BUFFER_SIZE;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else if (inputBuffer->NumberOfSectors > 255)
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else if (inputBuffer->StartLba > MAXULONG)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
RequestValidateExclusiveAccess(
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_CDROM_EXCLUSIVE_ACCESS

Arguments:

    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;

    *DataLength = 0;

    if (KeGetCurrentIrql() != PASSIVE_LEVEL)
    {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: IOCTL must be called at passive level.\n"));
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_EXCLUSIVE_ACCESS))
    {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &exclusiveAccess,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        switch (exclusiveAccess->RequestType)
        {
            case ExclusiveAccessQueryState:
            {
                if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
                    sizeof(CDROM_EXCLUSIVE_LOCK_STATE))
                {
                    //
                    // Output buffer too small.
                    //
                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Output buffer too small\n"));
                    *DataLength = sizeof(CDROM_EXCLUSIVE_LOCK_STATE);
                    status = STATUS_BUFFER_TOO_SMALL;
                }
                break;
            }

            case ExclusiveAccessLockDevice:
            {
                if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
                    sizeof(CDROM_EXCLUSIVE_LOCK))
                {
                    //
                    // Input buffer too small
                    //
                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
                    status = STATUS_INFO_LENGTH_MISMATCH;
                }
                break;
            }
            case ExclusiveAccessUnlockDevice:
            {
                //
                // Nothing to check
                //
                break;
            }

            default:
            {
                //
                // Unknown request type.
                //
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Invalid request type\n"));
                status = STATUS_INVALID_PARAMETER;
            }
        }
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleExclusiveAccessQueryLockState(
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessQueryState

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
    PCDROM_DATA                 cdData = &deviceExtension->DeviceAdditionalData;
    PCDROM_EXCLUSIVE_LOCK_STATE exclusiveLockState = NULL;

    PAGED_CODE();

    status = WdfRequestRetrieveOutputBuffer(Request,
                                            sizeof(CDROM_EXCLUSIVE_LOCK_STATE),
                                            &exclusiveLockState,
                                            NULL);
    NT_ASSERT(NT_SUCCESS(status));

    RtlZeroMemory(exclusiveLockState, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));

    if (EXCLUSIVE_MODE(cdData))
    {
        // Device is locked for exclusive use
        exclusiveLockState->LockState = TRUE;

        RtlCopyMemory(&exclusiveLockState->CallerName,
                      &cdData->CallerName,
                      CDROM_EXCLUSIVE_CALLER_LENGTH);

    }
    else
    {
        // Device is not locked
        exclusiveLockState->LockState = FALSE;
    }

    RequestCompletion(deviceExtension, Request, status, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleExclusiveAccessLockDevice(
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessLockDevice

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
    PCDROM_DATA             cdData = &deviceExtension->DeviceAdditionalData;
    PCDROM_EXCLUSIVE_LOCK   exclusiveLock = NULL;
    PIO_ERROR_LOG_PACKET    logEntry;

    WDFFILEOBJECT           fileObject = NULL;
    ULONG                   idx = 0;
    ULONG                   nameLength = 0;

    PAGED_CODE();

    fileObject = WdfRequestGetFileObject(Request);

    if (fileObject == NULL)
    {
        status = STATUS_INVALID_HANDLE;

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                    "RequestHandleExclusiveAccessLockDevice: FileObject is NULL, cannot grant exclusive access\n"));
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(CDROM_EXCLUSIVE_LOCK),
                                               &exclusiveLock,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        // Validate the caller name string
        for (idx = 0; (idx < CDROM_EXCLUSIVE_CALLER_LENGTH) && (exclusiveLock->CallerName[idx] != '\0'); idx++)
        {
            if (!ValidChar(exclusiveLock->CallerName[idx]))
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                            "RequestHandleExclusiveAccessLockDevice: Invalid characters in caller name\n"));
                // error out
                status = STATUS_INVALID_PARAMETER;
                break;
            }
        }
    }

    if (NT_SUCCESS(status))
    {
        if ((idx == 0) || (idx >= CDROM_EXCLUSIVE_CALLER_LENGTH))
        {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestHandleExclusiveAccessLockDevice: Not a valid null terminated string.\n"));
            //error out
            status = STATUS_INVALID_PARAMETER;
        }
        else
        {
            nameLength = idx+1; // Add 1 for the NULL character
            NT_ASSERT(nameLength <= CDROM_EXCLUSIVE_CALLER_LENGTH);
        }
    }

    // If the file system is still mounted on this device fail the request,
    // unless the force lock flag is set.
    if (NT_SUCCESS(status))
    {
        if ((TEST_FLAG(exclusiveLock->Access.Flags, CDROM_LOCK_IGNORE_VOLUME) == FALSE) &&
            IsVolumeMounted(deviceExtension->DeviceObject))
        {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                            "RequestHandleExclusiveAccessLockDevice: Unable to lock device, file system mounted\n"));
                status = STATUS_INVALID_DEVICE_STATE;
        }
    }

    // Lock the device for exclusive access if the device is not already locked
    if (NT_SUCCESS(status))
    {
        if (InterlockedCompareExchangePointer((PVOID)&cdData->ExclusiveOwner, (PVOID)fileObject, NULL) == NULL)
        {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                        "RequestHandleExclusiveAccessLockDevice: Entering exclusive mode! Device locked by file object %p\n", fileObject));

            // Zero out the CallerName before storing it in the extension
            RtlZeroMemory(&cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH);
            RtlCopyMemory(&cdData->CallerName,
                          &exclusiveLock->CallerName,
                          nameLength);

            // Send Exclusive Lock notification
            DeviceSendNotification(deviceExtension,
                                   &GUID_IO_CDROM_EXCLUSIVE_LOCK,
                                   0,
                                   NULL);

            // Log an informational event with the caller name
            logEntry = IoAllocateErrorLogEntry(
                                deviceExtension->DeviceObject,
                                sizeof(IO_ERROR_LOG_PACKET) + CDROM_EXCLUSIVE_CALLER_LENGTH);

            if (logEntry != NULL)
            {
                PUCHAR dumpDataPtr = (PUCHAR) logEntry->DumpData;

                logEntry->FinalStatus       = STATUS_SUCCESS;
                logEntry->ErrorCode         = IO_CDROM_EXCLUSIVE_LOCK;
                logEntry->SequenceNumber    = 0;
                logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
                logEntry->IoControlCode     = IOCTL_CDROM_EXCLUSIVE_ACCESS;
                logEntry->RetryCount        = 0;
                logEntry->UniqueErrorValue  = 0x1;
                logEntry->DumpDataSize      = CDROM_EXCLUSIVE_CALLER_LENGTH;

                RtlCopyMemory(dumpDataPtr,
                                (PUCHAR)&cdData->CallerName,
                                CDROM_EXCLUSIVE_CALLER_LENGTH);

                // Write the error log packet.
                IoWriteErrorLogEntry(logEntry);
            }

        }
        else
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestHandleExclusiveAccessLockDevice: Unable to lock device, device already locked.\n"));

            status = STATUS_ACCESS_DENIED;
        }
    }

    RequestCompletion(deviceExtension, Request, status, 0);

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleExclusiveAccessUnlockDevice(
    _In_ WDFDEVICE Device,
    _In_ WDFREQUEST Request
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessUnlockDevice

Arguments:

    Device - device handle
    Request - request to be handled

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
    PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
    WDFFILEOBJECT           fileObject = NULL;

    PAGED_CODE();

    fileObject = WdfRequestGetFileObject(Request);

    if (fileObject == NULL)
    {
        // The device can be unlocked from exclusive mode only via the file object which locked it.
        status = STATUS_INVALID_HANDLE;

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                    "RequestHandleExclusiveAccessUnlockDevice: FileObject is NULL, cannot release exclusive access\n"));
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(PCDROM_EXCLUSIVE_ACCESS),
                                               &exclusiveAccess,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = DeviceUnlockExclusive(deviceExtension, fileObject,
                        TEST_FLAG(exclusiveAccess->Flags, CDROM_NO_MEDIA_NOTIFICATIONS));

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessUnlockDevice: Device unlocked\n"));
    }

    RequestCompletion(deviceExtension, Request, status, 0);

    return status;
}

NTSTATUS
RequestHandleQueryPropertyRetrieveCachedData(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_QUERY_PROPERTY when the required data is cached.

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PSTORAGE_PROPERTY_QUERY inputBuffer = NULL;

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->PropertyId == StorageDeviceProperty)
        {
            // check output buffer length
            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
            {
                // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
                // so this must be a success case with no data transferred
                *DataLength = 0;
                status = STATUS_SUCCESS;
            }
            else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
            {
                // Buffer too small
                *DataLength = DeviceExtension->DeviceDescriptor->Size;
                status = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
                PSTORAGE_DEVICE_DESCRIPTOR  outputDescriptor = NULL;
                CHAR*                       localDescriptorBuffer = (CHAR*)DeviceExtension->DeviceDescriptor;

                status = WdfRequestRetrieveOutputBuffer(Request,
                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                        &outputDescriptor,
                                                        NULL);

                if (NT_SUCCESS(status))
                {
                    // transfer as much data out as the buffer will allow
                    *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                      DeviceExtension->DeviceDescriptor->Size);

                    RtlCopyMemory(outputDescriptor,
                                  DeviceExtension->DeviceDescriptor,
                                  *DataLength);

                    // walk through and update offset variables to reflect data that didn't make it into the output buffer
                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, VendorIdOffset)) &&
                        (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0) &&
                        (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0xFFFFFFFF))
                    {
                        // set VendorIdOffset appropriately
                        if (*DataLength <
                            (DeviceExtension->DeviceDescriptor->VendorIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->VendorIdOffset)))
                        {
                            outputDescriptor->VendorIdOffset = 0;
                        }
                    }

                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductIdOffset)) &&
                        (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0) &&
                        (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0xFFFFFFFF))
                    {
                        // set ProductIdOffset appropriately
                        if (*DataLength <
                            (DeviceExtension->DeviceDescriptor->ProductIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductIdOffset)))
                        {
                            outputDescriptor->ProductIdOffset = 0;
                        }
                    }

                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductRevisionOffset)) &&
                        (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0) &&
                        (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0xFFFFFFFF))
                    {
                        // set ProductRevisionOffset appropriately
                        if (*DataLength <
                            (DeviceExtension->DeviceDescriptor->ProductRevisionOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductRevisionOffset)))
                        {
                            outputDescriptor->ProductRevisionOffset = 0;
                        }
                    }

                    if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, SerialNumberOffset)) &&
                        (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0) &&
                        (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0xFFFFFFFF))
                    {
                        // set SerialNumberOffset appropriately
                        if (*DataLength <
                            (DeviceExtension->DeviceDescriptor->SerialNumberOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->SerialNumberOffset)))
                        {
                            // NOTE: setting this to 0 since that is what most port drivers do
                            //       [this could cause issues with SCSI port devices whose clients expect -1 in this field]
                            outputDescriptor->SerialNumberOffset = 0;
                        }
                    }
                    status = STATUS_SUCCESS;
                }
            }
        }   //end of StorageDeviceProperty
        else if (inputBuffer->PropertyId == StorageAdapterProperty)
        {
            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
            {
                // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
                // so this must be a success case with no data transferred
                *DataLength = 0;
                status = STATUS_SUCCESS;
            }
            else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
            {
                // Buffer too small
                *DataLength = DeviceExtension->AdapterDescriptor->Size;
                status = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
                PSTORAGE_ADAPTER_DESCRIPTOR outputDescriptor = NULL;

                status = WdfRequestRetrieveOutputBuffer(Request,
                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                        &outputDescriptor,
                                                        NULL);
                if (NT_SUCCESS(status))
                {
                    // copy as much data out as the buffer will allow
                    *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                      DeviceExtension->AdapterDescriptor->Size);

                    RtlCopyMemory(outputDescriptor,
                                  DeviceExtension->AdapterDescriptor,
                                  *DataLength);

                    // set status
                    status = STATUS_SUCCESS;
                }
            }
        }
    }

    return status;
}

NTSTATUS
RequestHandleQueryPropertyDeviceUniqueId(
    _In_ WDFDEVICE        Device,
    _In_ WDFREQUEST       Request
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceUniqueIdProperty.

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
    PSTORAGE_PROPERTY_QUERY     inputBuffer = NULL;
    PSTORAGE_DESCRIPTOR_HEADER  descHeader = NULL;
    size_t                      outLength = 0;
    WDF_REQUEST_PARAMETERS      requestParameters;

    // Get the Request parameters
    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
    WdfRequestGetParameters(Request, &requestParameters);

    status = WdfRequestRetrieveInputBuffer(Request,
                                           requestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        BOOLEAN overflow = FALSE;
        BOOLEAN infoFound = FALSE;

        // Must run at less then dispatch.
        if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
        {
            NT_ASSERT(FALSE);
            outLength = 0;
            status = STATUS_INVALID_LEVEL;
        }
        else if (inputBuffer->QueryType == PropertyExistsQuery)
        {
            outLength = 0;
            status = STATUS_SUCCESS;
        }
        else if (inputBuffer->QueryType != PropertyStandardQuery)
        {
            outLength = 0;
            status = STATUS_NOT_SUPPORTED;
        }
        else
        {
            // Check AdditionalParameters validity.
            if (inputBuffer->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS)
            {
                // Do nothing
            }
            else if (inputBuffer->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY)
            {
                // Do nothing
            }
            else
            {
                outLength = 0;
                status = STATUS_INVALID_PARAMETER;
            }

            if (NT_SUCCESS(status) &&
                (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)))
            {
                outLength = 0;
                status = STATUS_INFO_LENGTH_MISMATCH;
            }
        }

        // From this point forward the status depends on the overflow
        // and infoFound flags.
        if (NT_SUCCESS(status))
        {
            outLength = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;
            status = WdfRequestRetrieveOutputBuffer(Request,
                                                    requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                    &descHeader,
                                                    NULL);
        }

        if (NT_SUCCESS(status))
        {
            RtlZeroMemory(descHeader, outLength);

            descHeader->Version = DUID_VERSION_1;
            descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER);

            // Try to build device unique id from StorageDeviceIdProperty.
            status = RequestDuidGetDeviceIdProperty(deviceExtension,
                                                    Request,
                                                    requestParameters,
                                                    &outLength);

            if (status == STATUS_BUFFER_OVERFLOW)
            {
                overflow = TRUE;
            }

            if (NT_SUCCESS(status))
            {
                infoFound = TRUE;
            }

            // Try to build device unique id from StorageDeviceProperty.
            status = RequestDuidGetDeviceProperty(deviceExtension,
                                                  Request,
                                                  requestParameters,
                                                  &outLength);

            if (status == STATUS_BUFFER_OVERFLOW)
            {
                overflow = TRUE;
            }

            if (NT_SUCCESS(status))
            {
                infoFound = TRUE;
            }

            // Return overflow, success, or a generic error.
            if (overflow)
            {
                // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
                // success to the user.  Otherwise, send an error so the user
                // knows a larger buffer is required.
                if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER))
                {
                    status = STATUS_SUCCESS;
                }
                else
                {
                    outLength = (ULONG)WdfRequestGetInformation(Request);
                    status = STATUS_BUFFER_OVERFLOW;
                }

            }
            else if (infoFound)
            {
                status = STATUS_SUCCESS;

                // Exercise the compare routine.  This should always succeed.
                NT_ASSERT(DuidExactMatch == CompareStorageDuids((PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader,
                                                             (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader));

            }
            else
            {
                status = STATUS_NOT_FOUND;
            }
        }
    }

    RequestCompletion(deviceExtension, Request, status, outLength);

    return status;
}

NTSTATUS
RequestHandleQueryPropertyWriteCache(
    _In_ WDFDEVICE    Device,
    _In_ WDFREQUEST   Request
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceWriteCacheProperty.

Arguments:

    DeviceExtension - device context
    Request - request to be handled

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                      status = STATUS_SUCCESS;
    PCDROM_DEVICE_EXTENSION       deviceExtension = DeviceGetExtension(Device);
    PSTORAGE_PROPERTY_QUERY       query = NULL;
    PSTORAGE_WRITE_CACHE_PROPERTY writeCache = NULL;
    PMODE_PARAMETER_HEADER        modeData = NULL;
    PMODE_CACHING_PAGE            pageData = NULL;
    size_t                        length = 0;
    ULONG                         information = 0;
    PSCSI_REQUEST_BLOCK           srb = NULL;
    WDF_REQUEST_PARAMETERS        requestParameters;

    // Get the Request parameters
    WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
    WdfRequestGetParameters(Request, &requestParameters);

    status = WdfRequestRetrieveInputBuffer(Request,
                                           requestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &query,
                                           NULL);

    if (NT_SUCCESS(status))
    {

        // Must run at less then dispatch.
        if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
        {
            NT_ASSERT(FALSE);
            status = STATUS_INVALID_LEVEL;
        }
        else if (query->QueryType == PropertyExistsQuery)
        {
            information = 0;
            status = STATUS_SUCCESS;
        }
        else if (query->QueryType != PropertyStandardQuery)
        {
            status = STATUS_NOT_SUPPORTED;
        }
    }

    if (NT_SUCCESS(status))
    {
        length = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;

        if (length < sizeof(STORAGE_DESCRIPTOR_HEADER))
        {
            status = STATUS_INFO_LENGTH_MISMATCH;
        }
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &writeCache,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(writeCache, length);

        // Set version and required size.
        writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
        writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY);

        if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY))
        {
            // caller only wants header information, bail out.
            information = sizeof(STORAGE_DESCRIPTOR_HEADER);
            status = STATUS_SUCCESS;

            RequestCompletion(deviceExtension, Request, status, information);
            return status;
        }
    }

    if (NT_SUCCESS(status))
    {
        srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                    sizeof(SCSI_REQUEST_BLOCK) +
                                    (sizeof(ULONG_PTR) * 2),
                                    CDROM_TAG_SRB);

        if (srb == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));

        // Set known values
        writeCache->NVCacheEnabled = FALSE;
        writeCache->UserDefinedPowerProtection = TEST_FLAG(deviceExtension->DeviceFlags, DEV_POWER_PROTECTED);

        // Check for flush cache support by sending a sync cache command
        // to the device.

        // Set timeout value and mark the request as not being a tagged request.
        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
        srb->TimeOutValue = TimeOutValueGetCapValue(deviceExtension->TimeOutValue, 4);
        srb->QueueTag = SP_UNTAGGED;
        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
        srb->SrbFlags = deviceExtension->SrbFlags;

        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->CdbLength = 10;

        srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;

        status = DeviceSendSrbSynchronously(Device,
                                            srb,
                                            NULL,
                                            0,
                                            TRUE,   //flush drive cache
                                            Request);

        if (NT_SUCCESS(status))
        {
            writeCache->FlushCacheSupported = TRUE;
        }
        else
        {
            // Device does not support sync cache
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                        "RequestHandleQueryPropertyWriteCache: Synchronize cache failed with status 0x%X\n", status));
            writeCache->FlushCacheSupported = FALSE;

            // Reset the status if there was any failure
            status = STATUS_SUCCESS;
        }

        modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                         MODE_PAGE_DATA_SIZE,
                                         CDROM_TAG_MODE_DATA);

        if (modeData == NULL)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                        "RequestHandleQueryPropertyWriteCache: Unable to allocate mode data buffer\n"));
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);

        length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
                                                     (PCHAR)modeData,
                                                     MODE_PAGE_DATA_SIZE,
                                                     MODE_PAGE_CACHING,
                                                     MODE_SENSE_CURRENT_VALUES);

        if (length < sizeof(MODE_PARAMETER_HEADER))
        {
            // Retry the request in case of a check condition.
            length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
                                                         (PCHAR)modeData,
                                                         MODE_PAGE_DATA_SIZE,
                                                         MODE_PAGE_CACHING,
                                                         MODE_SENSE_CURRENT_VALUES);

            if (length < sizeof(MODE_PARAMETER_HEADER))
            {
                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));
                status = STATUS_IO_DEVICE_ERROR;
            }
        }
    }

    if (NT_SUCCESS(status))
    {
        // If the length is greater than length indicated by the mode data reset
        // the data to the mode data.
        if (length > (ULONG) (modeData->ModeDataLength + 1))
        {
            length = modeData->ModeDataLength + 1;
        }

        // Look for caching page in the returned mode page data.
        pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
                                             length,
                                             MODE_PAGE_CACHING,
                                             TRUE);

        // Check if valid caching page exists.
        if (pageData == NULL)
        {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));

            // Set write cache value as unknown.
            writeCache->WriteCacheEnabled = WriteCacheEnableUnknown;
            writeCache->WriteCacheType = WriteCacheTypeUnknown;
        }
        else
        {
            writeCache->WriteCacheEnabled = pageData->WriteCacheEnable
                                            ? WriteCacheEnabled
                                            : WriteCacheDisabled;

            writeCache->WriteCacheType = pageData->WriteCacheEnable
                                         ? WriteCacheTypeWriteBack
                                         : WriteCacheTypeUnknown;
        }

        // Check write through support.
        if (modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED)
        {
            writeCache->WriteThroughSupported = WriteThroughSupported;
        }
        else
        {
            writeCache->WriteThroughSupported = WriteThroughNotSupported;
        }

        // Get the changeable caching mode page and check write cache is changeable.
        RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);

        length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
                                                     (PCHAR) modeData,
                                                     MODE_PAGE_DATA_SIZE,
                                                     MODE_PAGE_CACHING,
                                                     MODE_SENSE_CHANGEABLE_VALUES);

        if (length < sizeof(MODE_PARAMETER_HEADER))
        {
            // Retry the request in case of a check condition.
            length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
                                                         (PCHAR) modeData,
                                                         MODE_PAGE_DATA_SIZE,
                                                         MODE_PAGE_CACHING,
                                                         MODE_SENSE_CHANGEABLE_VALUES);

            if (length < sizeof(MODE_PARAMETER_HEADER))
            {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));

                // If the device fails to return changeable pages, then
                // set the write cache changeable value to unknown.
                writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
                information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
            }
        }
    }

    if (NT_SUCCESS(status))
    {
        // If the length is greater than length indicated by the mode data reset
        // the data to the mode data.
        if (length > (ULONG) (modeData->ModeDataLength + 1))
        {
            length = modeData->ModeDataLength + 1;
        }

        // Look for caching page in the returned mode page data.
        pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
                                             length,
                                             MODE_PAGE_CACHING,
                                             TRUE);
        // Check if valid caching page exists.
        if (pageData == NULL)
        {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));

            // Set write cache changeable value to unknown.
            writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
        }
        else
        {
            writeCache->WriteCacheChangeable = pageData->WriteCacheEnable
                                               ? WriteCacheChangeable
                                               : WriteCacheNotChangeable;
        }

        information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);

    }

    FREE_POOL(srb);
    FREE_POOL(modeData);

    RequestCompletion(deviceExtension, Request, status, information);

    return status;
}

NTSTATUS
RequestValidateDvdReadKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_DVD_READ_KEY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
    ULONG                   keyLength = 0;

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &keyParameters,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "DvdDeviceControl: EstablishDriveKey - challenge "
                        "key buffer too small\n"));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        switch(keyParameters->KeyType)
        {

            case DvdChallengeKey:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_CHALLENGE_KEY_LENGTH);
                keyLength = DVD_CHALLENGE_KEY_LENGTH;
                break;
            }
            case DvdBusKey1:
            case DvdBusKey2:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_BUS_KEY_LENGTH);
                keyLength = DVD_BUS_KEY_LENGTH;
                break;
            }
            case DvdTitleKey:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_TITLE_KEY_LENGTH);
                keyLength = DVD_TITLE_KEY_LENGTH;
                break;
            }
            case DvdAsf:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_ASF_LENGTH);
                keyLength = DVD_ASF_LENGTH;
                break;
            }
            case DvdDiskKey:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_DISK_KEY_LENGTH);
                keyLength = DVD_DISK_KEY_LENGTH;
                break;
            }
            case DvdGetRpcKey:
            {
                C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_RPC_KEY_LENGTH);
                keyLength = DVD_RPC_KEY_LENGTH;
                break;
            }
            default:
            {
                keyLength = sizeof(DVD_COPY_PROTECT_KEY);
                break;
            }
        }

        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < keyLength)
        {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "DvdDeviceControl: EstablishDriveKey - output "
                        "buffer too small\n"));
            status = STATUS_BUFFER_TOO_SMALL;
            *DataLength = keyLength;
        }
        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
                 DeviceExtension->AdapterDescriptor->AlignmentMask)
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
        {
            // reject the request if it's not a DVD device.
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    return status;
}


NTSTATUS
RequestValidateDvdEndSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DVD_END_SESSION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID sessionId = NULL;

    UNREFERENCED_PARAMETER(DeviceExtension);

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &sessionId,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
            sizeof(DVD_SESSION_ID))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "DvdDeviceControl: EndSession - input buffer too "
                        "small\n"));
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}


NTSTATUS
RequestValidateAacsEndSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_AACS_END_SESSION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID sessionId = NULL;
    PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &sessionId,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        if (!cdData->Mmc.IsAACS)
        {
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
        else if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}


NTSTATUS
RequestValidateEnableStreaming(
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Validates an IOCTL_CDROM_ENABLE_STREAMING request

Arguments:

    Request - request to be handled
    RequestParameters - request parameters
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;

    PCDROM_STREAMING_CONTROL    inputBuffer = NULL;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
        sizeof(CDROM_STREAMING_CONTROL))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        // Get the request type using CDROM_STREAMING_CONTROL structure
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(CDROM_STREAMING_CONTROL),
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->RequestType != CdromStreamingDisable &&
            inputBuffer->RequestType != CdromStreamingEnableForReadOnly &&
            inputBuffer->RequestType != CdromStreamingEnableForWriteOnly &&
            inputBuffer->RequestType != CdromStreamingEnableForReadWrite)
        {
            // Unknown request type
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}


NTSTATUS
RequestValidateSendOpcInformation(
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Validates an IOCTL_CDROM_SEND_OPC_INFORMATION request

Arguments:

    Request - request to be handled
    RequestParameters - request parameters
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;

    PCDROM_SIMPLE_OPC_INFO      inputBuffer = NULL;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
        sizeof(CDROM_SIMPLE_OPC_INFO))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        // Get the request type using CDROM_SIMPLE_OPC_INFO structure
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(CDROM_SIMPLE_OPC_INFO),
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->RequestType != SimpleOpcInfo)
        {
            // Unknown request type
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}


NTSTATUS
RequestValidateGetPerformance(
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Validates an IOCTL_CDROM_GET_PERFORMANCE request

Arguments:

    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_WRITE_SPEED_REQUEST  writeSpeedRequest = NULL;
    PCDROM_PERFORMANCE_REQUEST  performanceRequest = NULL;

    *DataLength = 0;

    // CDROM_WRITE_SPEED_REQUEST is the smallest performance request that we support.
    // We use it to retrieve request type and then check input length more carefully
    // on a per request type basis.
    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
        sizeof(CDROM_WRITE_SPEED_REQUEST))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               sizeof(CDROM_WRITE_SPEED_REQUEST),
                                               (PVOID*)&writeSpeedRequest,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (writeSpeedRequest->RequestType == CdromPerformanceRequest)
        {
            // CDROM_PERFORMANCE_REQUEST is bigger than CDROM_WRITE_SPEED_REQUEST,
            // so we perform more checks and retrieve more bytes through WDF.
            if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
                sizeof(CDROM_PERFORMANCE_REQUEST))
            {
                status = STATUS_INFO_LENGTH_MISMATCH;
            }
            if (NT_SUCCESS(status))
            {
                status = WdfRequestRetrieveInputBuffer(Request,
                                                       sizeof(CDROM_PERFORMANCE_REQUEST),
                                                       &performanceRequest,
                                                       NULL);
            }

            if (!NT_SUCCESS(status))
            {
                // just pass the status code from above
            }
            // validate all enum-type fields of CDROM_PERFORMANCE_REQUEST
            else if (performanceRequest->PerformanceType != CdromReadPerformance &&
                     performanceRequest->PerformanceType != CdromWritePerformance)
            {
                status = STATUS_INVALID_PARAMETER;
            }
            else if (performanceRequest->Exceptions != CdromNominalPerformance &&
                     performanceRequest->Exceptions != CdromEntirePerformanceList &&
                     performanceRequest->Exceptions != CdromPerformanceExceptionsOnly)
            {
                status = STATUS_INVALID_PARAMETER;
            }
            else if (performanceRequest->Tolerance != Cdrom10Nominal20Exceptions)
            {
                status = STATUS_INVALID_PARAMETER;
            }
        }
        else if (writeSpeedRequest->RequestType == CdromWriteSpeedRequest)
        {
            // No additional checks here: all remaining fields are ignored
            // if RequestType == CdromWriteSpeedRequest.
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    // finally, check output buffer length
    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
                 sizeof(CDROM_PERFORMANCE_HEADER))
        {
            status = STATUS_BUFFER_TOO_SMALL;
            *DataLength = sizeof(CDROM_PERFORMANCE_HEADER);
        }
        else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
            ((USHORT)-1))
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
PCDB
RequestGetScsiPassThroughCdb(
    _In_ PIRP Irp
    )
/*++

Routine Description:

    Get the CDB structure from the SCSI pass through

Arguments:

    Irp - request to be handled

Return Value:

    PCDB

--*/
{
    PCDB                cdb = NULL;
    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG               inputBufferLength = 0;
    PVOID               inputBuffer = NULL;
    BOOLEAN             legacyPassThrough = FALSE;

    PAGED_CODE();

    if (((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) ||
         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
         (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX)) &&
        (Irp->AssociatedIrp.SystemBuffer != NULL))
    {
        inputBufferLength = currentIrpStack->Parameters.DeviceIoControl.InputBufferLength;
        inputBuffer = Irp->AssociatedIrp.SystemBuffer;
        legacyPassThrough = TRUE;

        if ((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
            (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX))
        {
            legacyPassThrough = FALSE;
        }

        //
        //  If this is a 32 bit application running on 64 bit then thunk the
        //  input structures to grab the cdb.
        //

#if BUILD_WOW64_ENABLED && defined(_WIN64)

        if (IoIs32bitProcess(Irp))
        {
            if (legacyPassThrough)
            {
                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32))
                {
                    cdb = (PCDB)((PSCSI_PASS_THROUGH32)inputBuffer)->Cdb;
                }
            }
            else
            {
                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32_EX))
                {
                    cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)inputBuffer)->Cdb;
                }
            }

        }
        else

#endif

        {
            if (legacyPassThrough)
            {
                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH))
                {
                    cdb = (PCDB)((PSCSI_PASS_THROUGH)inputBuffer)->Cdb;
                }
            }
            else
            {
                if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH_EX))
                {
                    cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)inputBuffer)->Cdb;
                }
            }
        }
    }

    return cdb;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleScsiPassThrough(
    _In_ WDFDEVICE    Device,
    _In_ WDFREQUEST   Request
    )
/*++

Routine Description:

   Handle request of IOCTL_SCSI_PASS_THROUGH
                     IOCTL_SCSI_PASS_THROUGH_DIRECT

   The function sets the MinorFunction field of irpStack,
   and pass the request to lower level driver.

Arguments:

    Device - device object
    Request - request to be handled

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_UNSUCCESSFUL;
    PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
    PIRP                        irp = WdfRequestWdmGetIrp(Request);
    PZERO_POWER_ODD_INFO        zpoddInfo = deviceExtension->ZeroPowerODDInfo;
    PCDB                        cdb = NULL;
    BOOLEAN                     isSoftEject = FALSE;

#if DBG
    PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
#endif


    PAGED_CODE();

#if DBG
    // SPTI is always processed in sync manner.
    NT_ASSERT(requestContext->SyncRequired);
#endif

    if ((zpoddInfo != NULL) &&
        (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
        (zpoddInfo->Load == 0))                                         // Drawer
    {
        cdb = RequestGetScsiPassThroughCdb(irp);

        if ((cdb != NULL) &&
            (cdb->AsByte[0] == SCSIOP_START_STOP_UNIT) &&
            (cdb->START_STOP.LoadEject == 1) &&
            (cdb->START_STOP.Start == 0))
        {
            isSoftEject = TRUE;
        }
    }

    WdfRequestFormatRequestUsingCurrentType(Request);

    // Special for SPTI, set the MinorFunction.
    {
        PIO_STACK_LOCATION  nextStack = IoGetNextIrpStackLocation(irp);

        nextStack->MinorFunction = 1;
    }


    status = RequestSend(deviceExtension,
                         Request,
                         deviceExtension->IoTarget,
                         WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
                         NULL);


    if (!NT_SUCCESS(status) &&
        (isSoftEject != FALSE))
    {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
                   "RequestHandleScsiPassThrough: soft eject detected, device marked as active\n"));

        DeviceMarkActive(deviceExtension, TRUE, FALSE);
    }

    RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));

    return status;
}

NTSTATUS
RequestHandleMountQueryUniqueId(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_MOUNTDEV_QUERY_UNIQUE_ID

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PMOUNTDEV_UNIQUE_ID uniqueId = NULL;

    *DataLength = 0;

    if (!DeviceExtension->MountedDeviceInterfaceName.Buffer)
    {
        status = STATUS_INVALID_PARAMETER;
    }
    else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID))
    {
        *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
        status = STATUS_BUFFER_TOO_SMALL;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &uniqueId,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(uniqueId, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);

        uniqueId->UniqueIdLength = DeviceExtension->MountedDeviceInterfaceName.Length;

        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
            (sizeof(USHORT) + DeviceExtension->MountedDeviceInterfaceName.Length))
        {
            *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
            status = STATUS_BUFFER_OVERFLOW;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlCopyMemory(uniqueId->UniqueId,
                      DeviceExtension->MountedDeviceInterfaceName.Buffer,
                      uniqueId->UniqueIdLength);

        *DataLength = sizeof(USHORT) + uniqueId->UniqueIdLength;
        status = STATUS_SUCCESS;
    }

    return status;
}

NTSTATUS
RequestHandleMountQueryDeviceName(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_MOUNTDEV_QUERY_DEVICE_NAME

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;
    PMOUNTDEV_NAME  name = NULL;

    *DataLength = 0;

    NT_ASSERT(DeviceExtension->DeviceName.Buffer);

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(MOUNTDEV_NAME);
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &name,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(name, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
        name->NameLength = DeviceExtension->DeviceName.Length;

        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
            (sizeof(USHORT) + DeviceExtension->DeviceName.Length))
        {
            status = STATUS_BUFFER_OVERFLOW;
            *DataLength = sizeof(MOUNTDEV_NAME);
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlCopyMemory(name->Name,
                      DeviceExtension->DeviceName.Buffer,
                      name->NameLength);

        status = STATUS_SUCCESS;
        *DataLength = sizeof(USHORT) + name->NameLength;
    }

    return status;
}

NTSTATUS
RequestHandleMountQuerySuggestedLinkName(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;

    PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName = NULL;

    WCHAR                    driveLetterNameBuffer[10] = {0};
    RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
    PWSTR                    valueName = NULL;
    UNICODE_STRING           driveLetterName = {0};

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
        sizeof(MOUNTDEV_SUGGESTED_LINK_NAME))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
    }

    if (NT_SUCCESS(status))
    {
        valueName = ExAllocatePoolWithTag(PagedPool,
                                          DeviceExtension->DeviceName.Length + sizeof(WCHAR),
                                          CDROM_TAG_STRINGS);
        if (valueName == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlCopyMemory(valueName,
                      DeviceExtension->DeviceName.Buffer,
                      DeviceExtension->DeviceName.Length);
        valueName[DeviceExtension->DeviceName.Length/sizeof(WCHAR)] = 0;

        driveLetterName.Buffer = driveLetterNameBuffer;
        driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
        driveLetterName.Length = 0;

        queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
        queryTable[0].Name = valueName;
        queryTable[0].EntryContext = &driveLetterName;
        queryTable[0].DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;

        status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
                                        L"\\Registry\\Machine\\System\\DISK", // why hard coded?
                                        queryTable, NULL, NULL);
    }

    if (NT_SUCCESS(status))
    {
        if ((driveLetterName.Length == 4) &&
            (driveLetterName.Buffer[0] == '%') &&
            (driveLetterName.Buffer[1] == ':'))
        {
            driveLetterName.Buffer[0] = 0xFF;
        }
        else if ((driveLetterName.Length != 4) ||
                 (driveLetterName.Buffer[0] < FirstDriveLetter) ||
                 (driveLetterName.Buffer[0] > LastDriveLetter) ||
                 (driveLetterName.Buffer[1] != ':'))
        {
            status = STATUS_NOT_FOUND;
        }
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &suggestedName,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        RtlZeroMemory(suggestedName, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
        suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
        suggestedName->NameLength = 28;

        *DataLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;

        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < *DataLength)
        {
            *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
            status = STATUS_BUFFER_OVERFLOW;
        }
    }

    if (NT_SUCCESS(status))
    {
        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
                               L"\\Registry\\Machine\\System\\DISK",
                               valueName);

        RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
        suggestedName->Name[12] = driveLetterName.Buffer[0];
        suggestedName->Name[13] = ':';
    }

    FREE_POOL(valueName);

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleReadTOC(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_READ_TOC
                     IOCTL_CDROM_GET_LAST_SESSION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    VOID*    outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if (DeviceIsPlayActive(DeviceExtension->Device))
    {
        status = STATUS_DEVICE_BUSY;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    // handle the request
    if (NT_SUCCESS(status))
    {
        size_t  transferSize;
        CDB     cdb;

        transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, sizeof(CDROM_TOC));

        RtlZeroMemory(outputBuffer, transferSize);

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION)
        {
            // Set format to return first and last session numbers.
            cdb.READ_TOC.Format2 = CDROM_READ_TOC_EX_FORMAT_SESSION;
        }
        else
        {
            // Use MSF addressing
            cdb.READ_TOC.Msf = 1;
        }

        cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
        cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
        cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
            RtlCopyMemory(outputBuffer,
                          DeviceExtension->ScratchContext.ScratchBuffer,
                          *DataLength);
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleReadTocEx(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_READ_TOC_EX

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_READ_TOC_EX  inputBuffer = NULL;
    VOID*               outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if (DeviceIsPlayActive(DeviceExtension->Device))
    {
        status = STATUS_DEVICE_BUSY;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    // handle the request
    if (NT_SUCCESS(status))
    {
        size_t  transferSize;
        CDB     cdb;

        transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, MAXUSHORT);
        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
        cdb.READ_TOC.Msf = inputBuffer->Msf;
        cdb.READ_TOC.Format2 = inputBuffer->Format;
        cdb.READ_TOC.StartingTrack = inputBuffer->SessionTrack;
        cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
        cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE)
            {
                *DataLength = 0;
                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            else
            {
                *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
                RtlCopyMemory(outputBuffer,
                              DeviceExtension->ScratchContext.ScratchBuffer,
                              *DataLength);
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
VOID
GetConfigurationDataConversionTypeAllToTypeOne(
    _In_    FEATURE_NUMBER       RequestedFeature,
    _In_    PSCSI_REQUEST_BLOCK  Srb,
    _Out_   size_t *             DataLength
    )
/*++

Routine Description:

    Some CDROM devices do not handle the GET CONFIGURATION commands with
    TYPE ONE request. The command will time out causing a bus reset.
    To avoid this problem we set a device flag during start device if the device
    fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
    tried as TYPE ALL request and the data will be converted to TYPE ONE format
    in this routine.

Arguments:

    RequestedFeature - device context
    Srb - request to be handled
    DataLength - transfer data length

Return Value:

    NTSTATUS

--*/
{
    PFEATURE_HEADER     featureHeader = NULL;
    FEATURE_NUMBER      thisFeature;
    ULONG               totalLength = 0;
    ULONG               featureLength = 0;
    ULONG               headerLength = 0;

    PGET_CONFIGURATION_HEADER   header = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if (Srb->DataTransferLength < RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
    {
        // do not have valid data.
        return;
    }

    // Calculate the length of valid data available in the
    // capabilities buffer from the DataLength field
    header = (PGET_CONFIGURATION_HEADER) Srb->DataBuffer;
    REVERSE_BYTES(&totalLength, header->DataLength);

    totalLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);

    // Make sure the we have enough data in the SRB
    totalLength = min(totalLength, Srb->DataTransferLength);

    // If we have received enough data from the device
    // check for the given feature.
    if (totalLength >= (sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)))
    {
        // Feature information is present. Verify the feature.
        featureHeader = (PFEATURE_HEADER)((PUCHAR)Srb->DataBuffer + sizeof(GET_CONFIGURATION_HEADER));

        thisFeature  = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);

        if (thisFeature == RequestedFeature)
        {
            // Calculate the feature length
            featureLength = sizeof(FEATURE_HEADER) + featureHeader->AdditionalLength;
        }
    }

    // Calculate the total size
    totalLength = sizeof(GET_CONFIGURATION_HEADER) + featureLength;

    headerLength = totalLength -
        RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);

    REVERSE_BYTES(header->DataLength, &headerLength);

    *DataLength = totalLength;

    return;
}

_IRQL_requires_max_(APC_LEVEL)
VOID
GetConfigurationDataSynthesize(
    _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
    _In_                            ULONG           InputBufferSize,
    _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
    _In_                            size_t          OutputBufferSize,
    _In_                            FEATURE_NUMBER  StartingFeature,
    _In_                            ULONG           RequestType,
    _Out_                           size_t *        DataLength
    )
/*++

Routine Description:

    Get Configuration is a frequently used command, and we don't want it to wake
    up the device in case it is in Zero Power state. Before entering Zero Power state,
    the complete response of the command is saved in cache, and since the response
    is always the same in case there is no media, we can synthesize the response
    based on the user request.

Arguments:

    InputBuffer - buffer containing cached command response
    InputBufferSize - size of above buffer
    OutputBuffer - buffer to fill in result
    OutputBufferSize - size of above buffer
    StartingFeature - requested Starting Feature Number
    RequestType - requested Request Type
    DataLength - transfer data length

Return Value:

--*/
{
    PFEATURE_HEADER     featureHeader = NULL;
    ULONG               validLength = 0;
    ULONG               featureLength = 0;
    ULONG               headerLength = 0;
    PUCHAR              buffer = NULL;
    ULONG               bytesRemaining = 0;
    FEATURE_NUMBER      featureCode = 0;
    BOOLEAN             shouldCopy = FALSE;
    size_t              copyLength = 0;
    size_t              transferedLength = 0;
    size_t              requiredLength = 0;

    PGET_CONFIGURATION_HEADER   header = NULL;

    PAGED_CODE ();

    if (InputBufferSize < sizeof (GET_CONFIGURATION_HEADER))
    {
        // do not have valid data.
        *DataLength = 0;

        return;
    }

    // Calculate the length of valid data available in the
    // capabilities buffer from the DataLength field
    header = (PGET_CONFIGURATION_HEADER) InputBuffer;
    REVERSE_BYTES(&validLength, header->DataLength);

    validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);

    // Make sure we have enough data
    validLength = min(validLength, InputBufferSize);

    // Copy the header first
    copyLength = min(OutputBufferSize, sizeof (GET_CONFIGURATION_HEADER));

    RtlMoveMemory(OutputBuffer,
                  InputBuffer,
                  copyLength);

    transferedLength = copyLength;
    requiredLength = sizeof (GET_CONFIGURATION_HEADER);

    if (validLength > sizeof (GET_CONFIGURATION_HEADER))
    {
        buffer = header->Data;
        bytesRemaining = validLength - sizeof (GET_CONFIGURATION_HEADER);

        // Ignore incomplete feature descriptor
        while (bytesRemaining >= sizeof (FEATURE_HEADER))
        {
            featureHeader = (PFEATURE_HEADER) buffer;
            shouldCopy = FALSE;

            featureCode = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);
            featureLength = sizeof (FEATURE_HEADER) + featureHeader->AdditionalLength;

            if (featureCode >= StartingFeature)
            {
                switch (RequestType) {

                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL:

                    shouldCopy = TRUE;
                    break;

                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT:

                    if (featureHeader->Current)
                    {
                        shouldCopy = TRUE;
                    }
                    break;

                case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE:

                    if (featureCode == StartingFeature)
                    {
                        shouldCopy = TRUE;
                    }
                    break;

                default:

                    break;
                }
            }

            if (shouldCopy != FALSE)
            {
                copyLength = min(featureLength, bytesRemaining);
                copyLength = min(copyLength, OutputBufferSize - transferedLength);

                RtlMoveMemory((PUCHAR) OutputBuffer + transferedLength,
                              buffer,
                              copyLength);

                transferedLength += copyLength;
                requiredLength += featureLength;
            }

            buffer += min(featureLength, bytesRemaining);
            bytesRemaining -= min(featureLength, bytesRemaining);
        }
    }

    // Adjust Data Length field in header
    if (transferedLength >= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
    {
        headerLength = (ULONG) requiredLength - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);

        header = (PGET_CONFIGURATION_HEADER) OutputBuffer;
        REVERSE_BYTES(header->DataLength, &headerLength);
    }

    *DataLength = transferedLength;

    return;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleGetConfiguration(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_GET_CONFIGURATION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                        status = STATUS_SUCCESS;
    PCDROM_DATA                     cdData = &(DeviceExtension->DeviceAdditionalData);
    PGET_CONFIGURATION_IOCTL_INPUT  inputBuffer = NULL;
    VOID*                           outputBuffer = NULL;
    size_t                          transferByteCount = 0;
    PZERO_POWER_ODD_INFO            zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
    BOOLEAN                         inZeroPowerState = FALSE;

    PAGED_CODE ();

    *DataLength = 0;

    //
    if (!cdData->Mmc.IsMmc)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
    }
    else
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &inputBuffer,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        // If device is Zero Power state, there should be no media in device, thus we can synthesize the response
        // from our cache. Avoid waking up the device in this case.
        if ((zpoddInfo != NULL) &&
            (zpoddInfo->InZeroPowerState != FALSE))
        {
            inZeroPowerState = TRUE;
        }

        if ((inZeroPowerState == FALSE) ||
            (zpoddInfo->GetConfigurationBuffer == NULL))
        {
            CDB cdb;

            //The maximum number of bytes that a Drive may return
            //to describe its Features in one GET CONFIGURATION Command is 65,534
            transferByteCount = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, (MAXUSHORT - 1));

            // If this is a TYPE ONE request and if this device can't handle this
            // request, then we need to send TYPE ALL request to the device and
            // convert the data in the completion routine. If required allocate a big
            // buffer to get both configuration and feature header.
            if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
                TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
            {
                transferByteCount = max(transferByteCount,
                                        sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER));
            }

            ScratchBuffer_BeginUse(DeviceExtension);

            RtlZeroMemory(&cdb, sizeof(CDB));
            // Set up the CDB
            cdb.GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
            cdb.GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8);
            cdb.GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff);

            cdb.GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8);
            cdb.GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff);
            cdb.GET_CONFIGURATION.RequestType        = (UCHAR)(inputBuffer->RequestType);

            // If the device does not support TYPE ONE get configuration commands
            // then change the request type to TYPE ALL. Convert the returned data to
            // TYPE ONE format in the completion routine.
            if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
                TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
            {

                TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                           "DeviceHandleGetConfiguration: Changing TYPE_ONE Get Config to TYPE_ALL\n"));
                cdb.GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL;
            }

            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE, &cdb, 12);

            if (NT_SUCCESS(status))
            {
                if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
                    TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
                {

                    if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(GET_CONFIGURATION_HEADER))
                    {
                        // Not enough data to calculate the data length.
                        // So assume feature is not present
                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DeviceHandleGetConfiguration: No get config header!\n"));
                        *DataLength = 0;
                        status = STATUS_INVALID_DEVICE_REQUEST;
                    }
                    else
                    {
                        //Some CDROM devices do not handle the GET CONFIGURATION commands with
                        //TYPE ONE request. The command will time out causing a bus reset.
                        //To avoid this problem we set a device flag during start device if the device
                        //fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
                        //tried as TYPE ALL request and the data will be converted to TYPE ONE format
                        //in this routine.
                        GetConfigurationDataConversionTypeAllToTypeOne(inputBuffer->Feature, DeviceExtension->ScratchContext.ScratchSrb, DataLength);
                        *DataLength = min(*DataLength, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
                    }
                }
                else
                {
                    *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
                }

                // copy data to output buffer
                if (NT_SUCCESS(status))
                {
                    RtlMoveMemory(outputBuffer,
                                  DeviceExtension->ScratchContext.ScratchBuffer,
                                  *DataLength);
                }
            }

            ScratchBuffer_EndUse(DeviceExtension);
        }
        else
        {
            // We are in Zero Power state, and our cached response is available.
            // Synthesize the requested data.
            GetConfigurationDataSynthesize(zpoddInfo->GetConfigurationBuffer,
                                           zpoddInfo->GetConfigurationBufferSize,
                                           outputBuffer,
                                           RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                           inputBuffer->Feature,
                                           inputBuffer->RequestType,
                                           DataLength
                                           );
        }
    }

    return status;
}

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
RequestHandleGetDriveGeometry(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DISK_GET_LENGTH_INFO
                     IOCTL_DISK_GET_DRIVE_GEOMETRY
                     IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
                     IOCTL_CDROM_GET_DRIVE_GEOMETRY
                     IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX
                     IOCTL_STORAGE_READ_CAPACITY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    VOID*               outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveOutputBuffer(Request,
                                            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                            &outputBuffer,
                                            NULL);

    // Issue ReadCapacity to update device extension
    // with information for current media.
    if (NT_SUCCESS(status))
    {
        status = MediaReadCapacity(DeviceExtension->Device);
    }

    if (NT_SUCCESS(status))
    {
        switch(RequestParameters.Parameters.DeviceIoControl.IoControlCode)
        {
            case IOCTL_DISK_GET_LENGTH_INFO:
            {
                PGET_LENGTH_INFORMATION lengthInfo = (PGET_LENGTH_INFORMATION)outputBuffer;

                lengthInfo->Length = DeviceExtension->PartitionLength;
                *DataLength = sizeof(GET_LENGTH_INFORMATION);
                break;
            }
            case IOCTL_DISK_GET_DRIVE_GEOMETRY:
            case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
            {
                PDISK_GEOMETRY geometry = (PDISK_GEOMETRY)outputBuffer;

                *geometry = DeviceExtension->DiskGeometry;
                *DataLength = sizeof(DISK_GEOMETRY);
                break;
            }
            case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
            case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
            {
                PDISK_GEOMETRY_EX geometryEx = (PDISK_GEOMETRY_EX)outputBuffer;

                geometryEx->DiskSize = DeviceExtension->PartitionLength;
                geometryEx->Geometry = DeviceExtension->DiskGeometry;
                *DataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
                break;
            }
            case IOCTL_STORAGE_READ_CAPACITY:
            {
                PSTORAGE_READ_CAPACITY readCapacity = (PSTORAGE_READ_CAPACITY)outputBuffer;

                readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
                readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);

                readCapacity->BlockLength = DeviceExtension->DiskGeometry.BytesPerSector;
                if (readCapacity->BlockLength > 0)
                {
                    readCapacity->NumberOfBlocks.QuadPart = DeviceExtension->PartitionLength.QuadPart/readCapacity->BlockLength;
                }
                else
                {
                    readCapacity->NumberOfBlocks.QuadPart = 0;
                }

                readCapacity->DiskLength = DeviceExtension->PartitionLength;

                *DataLength = sizeof(STORAGE_READ_CAPACITY);
                break;
            }
            default:
            {
                NT_ASSERT(FALSE);
                break;
            }
        } // end of switch()
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleDiskVerify(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DISK_VERIFY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
    PVERIFY_INFORMATION verifyInfo = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if (!cdData->Mmc.WriteAllowed)
    {
        status = STATUS_MEDIA_WRITE_PROTECTED;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &verifyInfo,
                                               NULL);
    }

    // handle the request
    if (NT_SUCCESS(status))
    {
        LARGE_INTEGER byteOffset = {0};

        // Add disk offset to starting sector.
        byteOffset.QuadPart = DeviceExtension->StartingOffset.QuadPart +
                              verifyInfo->StartingOffset.QuadPart;

        // prevent overflow returning success but only validating small area
        if (((DeviceExtension->StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart) < DeviceExtension->StartingOffset.QuadPart) ||
            ((verifyInfo->Length >> DeviceExtension->SectorShift) > MAXUSHORT) ||
            ((byteOffset.QuadPart >> DeviceExtension->SectorShift) > MAXULONG) )
        {
            status = STATUS_INVALID_PARAMETER;
        }
        else
        {
            ULONG   transferSize = 0;
            ULONG   timeoutValue = 0;
            CDB     cdb;
            ULONG   sectorOffset;
            USHORT  sectorCount;

            ScratchBuffer_BeginUse(DeviceExtension);

            // Convert byte offset to sector offset.
            sectorOffset = (ULONG)(byteOffset.QuadPart >> DeviceExtension->SectorShift);

            // Convert ULONG byte count to USHORT sector count.
            sectorCount = (USHORT)(verifyInfo->Length >> DeviceExtension->SectorShift);

            RtlZeroMemory(&cdb, sizeof(CDB));
            // Set up the CDB
            cdb.CDB10.OperationCode = SCSIOP_VERIFY;

            // Move little endian values into CDB in big endian format.
            cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
            cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
            cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
            cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;

            cdb.CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
            cdb.CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->Byte0;

            // The verify command is used by the NT FORMAT utility and
            // requests are sent down for 5% of the volume size. The
            // request timeout value is calculated based on the number of
            // sectors verified.
            if (sectorCount != 0)
            {
                // sectorCount is a USHORT, so no overflow here...
                timeoutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, ((sectorCount + 128) / 128));
            }

            status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 10, timeoutValue);

            // nothing to do after the command finishes.
            ScratchBuffer_EndUse(DeviceExtension);
        }
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleCheckVerify(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_CHECK_VERIFY2
                     IOCTL_STORAGE_CHECK_VERIFY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

        status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 6, CDROM_TEST_UNIT_READY_TIMEOUT);

        if (NT_SUCCESS(status))
        {
            if((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) &&
               (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength))
            {
                PULONG outputBuffer = NULL;
                status = WdfRequestRetrieveOutputBuffer(Request,
                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                        &outputBuffer,
                                                        NULL);

                if (outputBuffer != NULL)
                {
                    *outputBuffer = DeviceExtension->MediaChangeCount;
                    *DataLength = sizeof(ULONG);
                }
            }
            else
            {
                *DataLength = 0;
            }
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleFakePartitionInfo(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DISK_GET_DRIVE_LAYOUT
                     IOCTL_DISK_GET_DRIVE_LAYOUT_EX
                     IOCTL_DISK_GET_PARTITION_INFO
                     IOCTL_DISK_GET_PARTITION_INFO_EX

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;
    VOID*       outputBuffer = NULL;
    ULONG       ioctl = RequestParameters.Parameters.DeviceIoControl.IoControlCode;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) &&
            (ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) &&
            (ioctl != IOCTL_DISK_GET_PARTITION_INFO) &&
            (ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX))
        {
            status = STATUS_INTERNAL_ERROR;
        }
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    // handle the request
    if (NT_SUCCESS(status))
    {
        switch (ioctl)
        {
        case IOCTL_DISK_GET_DRIVE_LAYOUT:
            *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
            RtlZeroMemory(outputBuffer, *DataLength);
            break;
        case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
            *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
            RtlZeroMemory(outputBuffer, *DataLength);
            break;
        case IOCTL_DISK_GET_PARTITION_INFO:
            *DataLength = sizeof(PARTITION_INFORMATION);
            RtlZeroMemory(outputBuffer, *DataLength);
            break;
        case IOCTL_DISK_GET_PARTITION_INFO_EX:
            *DataLength = sizeof(PARTITION_INFORMATION_EX);
            RtlZeroMemory(outputBuffer, *DataLength);
            break;
        default:
            NT_ASSERT(!"Invalid ioctl should not have reached this point\n");
            break;
        }

        // if we are getting the drive layout, then we need to start by
        // adding some of the non-partition stuff that says we have
        // exactly one partition available.
        if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT)
        {
            PDRIVE_LAYOUT_INFORMATION layout;
            layout = (PDRIVE_LAYOUT_INFORMATION)outputBuffer;
            layout->PartitionCount = 1;
            layout->Signature = 1;
            outputBuffer = (PVOID)(layout->PartitionEntry);
            ioctl = IOCTL_DISK_GET_PARTITION_INFO;
        }
        else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX)
        {
            PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
            layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)outputBuffer;
            layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
            layoutEx->PartitionCount = 1;
            layoutEx->Mbr.Signature = 1;
            outputBuffer = (PVOID)(layoutEx->PartitionEntry);
            ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX;
        }

        // NOTE: the local var 'ioctl' is now modified to either EX or
        // non-EX version. the local var 'systemBuffer' is now pointing
        // to the partition information structure.
        if (ioctl == IOCTL_DISK_GET_PARTITION_INFO)
        {
            PPARTITION_INFORMATION partitionInfo;
            partitionInfo = (PPARTITION_INFORMATION)outputBuffer;
            partitionInfo->RewritePartition = FALSE;
            partitionInfo->RecognizedPartition = TRUE;
            partitionInfo->PartitionType = PARTITION_FAT32;
            partitionInfo->BootIndicator = FALSE;
            partitionInfo->HiddenSectors = 0;
            partitionInfo->StartingOffset.QuadPart = 0;
            partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
            partitionInfo->PartitionNumber = 0;
        }
        else
        {
            PPARTITION_INFORMATION_EX partitionInfo;
            partitionInfo = (PPARTITION_INFORMATION_EX)outputBuffer;
            partitionInfo->PartitionStyle = PARTITION_STYLE_MBR;
            partitionInfo->RewritePartition = FALSE;
            partitionInfo->Mbr.RecognizedPartition = TRUE;
            partitionInfo->Mbr.PartitionType = PARTITION_FAT32;
            partitionInfo->Mbr.BootIndicator = FALSE;
            partitionInfo->Mbr.HiddenSectors = 0;
            partitionInfo->StartingOffset.QuadPart = 0;
            partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
            partitionInfo->PartitionNumber = 0;
        }
    }

    return status;
}

NTSTATUS
RequestHandleGetDeviceNumber(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_GET_DEVICE_NUMBER

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;

    *DataLength = 0;

    if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
       sizeof(STORAGE_DEVICE_NUMBER))
    {
        PSTORAGE_DEVICE_NUMBER deviceNumber = NULL;
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &deviceNumber,
                                                NULL);
        if (NT_SUCCESS(status))
        {
            deviceNumber->DeviceType = DeviceExtension->DeviceObject->DeviceType;
            deviceNumber->DeviceNumber = DeviceExtension->DeviceNumber;
            deviceNumber->PartitionNumber = (ULONG)-1; // legacy reason, return (-1) for this IOCTL.

            status = STATUS_SUCCESS;
            *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
        }
    }
    else
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
    }

    return status;
}

NTSTATUS
RequestHandleGetHotPlugInfo(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_GET_HOTPLUG_INFO

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;

    *DataLength = 0;

    if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
       sizeof(STORAGE_HOTPLUG_INFO))
    {
        PSTORAGE_HOTPLUG_INFO info = NULL;
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &info,
                                                NULL);
        if (NT_SUCCESS(status))
        {
            *info = DeviceExtension->PrivateFdoData->HotplugInfo;

            status = STATUS_SUCCESS;
            *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
        }
    }
    else
    {
        status = STATUS_BUFFER_TOO_SMALL;
        *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
    }

    return status;
}

NTSTATUS
RequestHandleSetHotPlugInfo(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_SET_HOTPLUG_INFO

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PSTORAGE_HOTPLUG_INFO   info = NULL;

    *DataLength = 0;

    if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
        sizeof(STORAGE_HOTPLUG_INFO))
    {
        // Indicate unsuccessful status and no data transferred.
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &info,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (info->Size != DeviceExtension->PrivateFdoData->HotplugInfo.Size)
        {
            status = STATUS_INVALID_PARAMETER_1;
        }

        if (info->MediaRemovable != DeviceExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
        {
            status = STATUS_INVALID_PARAMETER_2;
        }

        if (info->MediaHotplug != DeviceExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
        {
            status = STATUS_INVALID_PARAMETER_3;
        }

        if (info->WriteCacheEnableOverride != DeviceExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
        {
            status = STATUS_INVALID_PARAMETER_5;
        }
    }

    if (NT_SUCCESS(status))
    {
        DeviceExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;

        // Store the user-defined override in the registry
        DeviceSetParameter(DeviceExtension,
                           CLASSP_REG_SUBKEY_NAME,
                           CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
                           (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleEventNotification(
    _In_      PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_opt_  WDFREQUEST               Request,
    _In_opt_  PWDF_REQUEST_PARAMETERS  RequestParameters,
    _Out_     size_t *                 DataLength
    )
/*++

Routine Description:

    This routine handles the process of IOCTL_STORAGE_EVENT_NOTIFICATION

Arguments:

    DeviceExtension - device context

    Request - request to be handled

    RequestParameters - request parameter

    DataLength - data transferred

Return Value:
    NTSTATUS

--*/
{
    NTSTATUS                     status = STATUS_SUCCESS;
    PMEDIA_CHANGE_DETECTION_INFO info = NULL;
    LONG                         requestInUse;
    PSTORAGE_EVENT_NOTIFICATION  eventBuffer = NULL;

    *DataLength = 0;

    info = DeviceExtension->MediaChangeDetectionInfo;

    // Since AN is ASYNCHRONOUS and can happen at any time,
    // make certain not to do anything before properly initialized.
    if ((!DeviceExtension->IsInitialized) || (info == NULL))
    {
        status = STATUS_UNSUCCESSFUL;
    }

    if (NT_SUCCESS(status) && (Request != NULL) && (RequestParameters != NULL)) {

        //
        // Validate IOCTL parameters
        //
        if (RequestParameters->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(STORAGE_EVENT_NOTIFICATION)) {
            status = STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // Check for an supported event
        //
        if (NT_SUCCESS(status)) {
            status = WdfRequestRetrieveInputBuffer(Request,
                                                   RequestParameters->Parameters.DeviceIoControl.InputBufferLength,
                                                   &eventBuffer,
                                                   NULL);
            if (NT_SUCCESS(status)) {
                if ((eventBuffer->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) ||
                    (eventBuffer->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) {
                    status = STATUS_INVALID_PARAMETER;
                } else if ((eventBuffer->Events &
                           (STORAGE_EVENT_MEDIA_STATUS | STORAGE_EVENT_DEVICE_STATUS | STORAGE_EVENT_DEVICE_OPERATION)) == 0) {
                    status = STATUS_NOT_SUPPORTED;
                }
            }
        }

    }

    if (NT_SUCCESS(status))
    {
        if (info->MediaChangeDetectionDisableCount != 0)
        {
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
                       "RequestHandleEventNotification: device %p has detection disabled \n",
                       DeviceExtension->DeviceObject));
            status = STATUS_UNSUCCESSFUL;
        }
    }

    if (NT_SUCCESS(status))
    {
        // if the request is not in use, mark it as such.
        requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);

        if (requestInUse != 0)
        {
            status = STATUS_UNSUCCESSFUL;
        }
    }

    if (NT_SUCCESS(status))
    {
        // The last MCN finished. ok to issue the new one.
        RequestSetupMcnSyncIrp(DeviceExtension);

        // The irp will go into KMDF framework and a request will be created there to represent it.
        IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
    }

    return status;
}


_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
RequestHandleEjectionControl(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_MEDIA_REMOVAL
                     IOCTL_STORAGE_EJECTION_CONTROL

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PPREVENT_MEDIA_REMOVAL  mediaRemoval = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
       sizeof(PREVENT_MEDIA_REMOVAL))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               &mediaRemoval,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = PerformEjectionControl(DeviceExtension,
                                        Request,
                                        ((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECTION_CONTROL)
                                          ? SecureMediaLock
                                          : SimpleMediaLock),
                                        mediaRemoval->PreventMediaRemoval);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleEnableStreaming(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Handles an IOCTL_CDROM_ENABLE_STREAMING request

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Notes:

    This IOCTL is serialized because it changes read/write
    behavior and we want to make sure that all previous
    reads/writes have been completed before we change the
    behavior.

Return Value:

    NTSTATUS

--*/
{
    PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
    PCDROM_PRIVATE_FDO_DATA     fdoData = DeviceExtension->PrivateFdoData;

    WDFFILEOBJECT               fileObject = NULL;
    PFILE_OBJECT_CONTEXT        fileObjectContext = NULL;

    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_STREAMING_CONTROL    inputBuffer = NULL;

    BOOLEAN                     enforceStreamingRead = FALSE;
    BOOLEAN                     enforceStreamingWrite = FALSE;
    BOOLEAN                     streamingReadSupported, streamingWriteSupported;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           sizeof(CDROM_STREAMING_CONTROL),
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        // get file object context
        fileObject = WdfRequestGetFileObject(Request);
        if (fileObject != NULL) {
            fileObjectContext = FileObjectGetContext(fileObject);
        }
        NT_ASSERT(fileObjectContext != NULL);

        if (fileObjectContext == NULL)
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestHandleEnableStreaming: cannot find file object context\n"));
            status = STATUS_INVALID_HANDLE;
        }
    }

    if (NT_SUCCESS(status))
    {
        if (inputBuffer->RequestType == CdromStreamingDisable)
        {
            enforceStreamingRead = FALSE;
            enforceStreamingWrite = FALSE;
        }
        else if (inputBuffer->RequestType == CdromStreamingEnableForReadOnly)
        {
            enforceStreamingRead = TRUE;
            enforceStreamingWrite = FALSE;
        }
        else if (inputBuffer->RequestType == CdromStreamingEnableForWriteOnly)
        {
            enforceStreamingRead = FALSE;
            enforceStreamingWrite = TRUE;
        }
        else if (inputBuffer->RequestType == CdromStreamingEnableForReadWrite)
        {
            enforceStreamingRead = TRUE;
            enforceStreamingWrite = TRUE;
        }

        streamingReadSupported = cdData->Mmc.StreamingReadSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
        streamingWriteSupported = cdData->Mmc.StreamingWriteSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
        if ((enforceStreamingRead && !streamingReadSupported) ||
            (enforceStreamingWrite && !streamingWriteSupported))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                        "RequestHandleEnableStreaming: requested Streaming mode is not supported\n"));
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
        else
        {
            fileObjectContext->EnforceStreamingRead = enforceStreamingRead;
            fileObjectContext->EnforceStreamingWrite = enforceStreamingWrite;
        }
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleSendOpcInformation(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Handles an IOCTL_CDROM_SEND_OPC_INFORMATION request

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_SIMPLE_OPC_INFO  inputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           sizeof(CDROM_SIMPLE_OPC_INFO),
                                           (PVOID*)&inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        CDB                         cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));

        // we support only the simplest version for now
        cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION;
        cdb.SEND_OPC_INFORMATION.DoOpc = 1;
        cdb.SEND_OPC_INFORMATION.Exclude0 = (inputBuffer->Exclude0 ? 1 : 0);
        cdb.SEND_OPC_INFORMATION.Exclude1 = (inputBuffer->Exclude1 ? 1 : 0);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, 0, FALSE, &cdb, sizeof(cdb.SEND_OPC_INFORMATION));

        // nothing to do after the command finishes
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleGetPerformance(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Handles an IOCTL_CDROM_GET_PERFORMANCE request

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PCDROM_PERFORMANCE_REQUEST  inputBuffer = NULL;
    PVOID                       outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    // Retrieve pointers to input/output data. The size has been validated earlier
    // in RequestValidateGetPerformance, so we do not check it again.
    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               (PVOID*)&inputBuffer,
                                               NULL);
    }
    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        USHORT                      descriptorSize = 0;
        USHORT                      descriptorCount = 0;
        size_t                      transferSize = 0;
        CDB                         cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        // Set up the CDB
        RtlZeroMemory(&cdb, sizeof(CDB));

        if (inputBuffer->RequestType == CdromPerformanceRequest)
        {
            cdb.GET_PERFORMANCE.Type = 0;

            // 10b is the only defined tolerance in MMCr6
            cdb.GET_PERFORMANCE.Tolerance = 2;

            switch (inputBuffer->Exceptions) {
                case CdromNominalPerformance:
                    cdb.GET_PERFORMANCE.Except = 0;
                    descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
                    break;
                case CdromEntirePerformanceList:
                    cdb.GET_PERFORMANCE.Except = 1;
                    descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
                    break;
                case CdromPerformanceExceptionsOnly:
                    cdb.GET_PERFORMANCE.Except = 2;
                    descriptorSize = sizeof(CDROM_EXCEPTION_PERFORMANCE_DESCRIPTOR);
                    break;
            }

            switch (inputBuffer->PerformanceType) {
                case CdromReadPerformance:
                    cdb.GET_PERFORMANCE.Write = 0; break;
                case CdromWritePerformance:
                    cdb.GET_PERFORMANCE.Write = 1; break;
            }

            REVERSE_BYTES(&cdb.GET_PERFORMANCE.StartingLBA, &inputBuffer->StaringLba);
        }
        else if (inputBuffer->RequestType == CdromWriteSpeedRequest)
        {
            cdb.GET_PERFORMANCE.Type = 3;
            descriptorSize = sizeof(CDROM_WRITE_SPEED_DESCRIPTOR);
        }

        cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;

        // calculate how many descriptors can fit into the output buffer
        if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
                sizeof(CDROM_PERFORMANCE_HEADER) &&
            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <=
                MAXUSHORT &&
            descriptorSize > 0)
        {
            descriptorCount = (USHORT)(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength - sizeof(CDROM_PERFORMANCE_HEADER));
            descriptorCount /= descriptorSize;
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }

        REVERSE_BYTES_SHORT(&cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors, &descriptorCount);

        // Calculate transfer size. We round it up to meet adapter requirements.
        // Extra bytes are discarded later, when we copy data to the output buffer.
        transferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
        transferSize += DeviceExtension->AdapterDescriptor->AlignmentMask;
        transferSize &= ~DeviceExtension->AdapterDescriptor->AlignmentMask;

        if (NT_SUCCESS(status))
        {
            status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, sizeof(cdb.GET_PERFORMANCE), CDROM_GET_PERFORMANCE_TIMEOUT);
        }

        if (NT_SUCCESS(status))
        {
            if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(CDROM_PERFORMANCE_HEADER))
            {
                *DataLength = 0;
                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            else
            {
                *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                 DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
                RtlCopyMemory(outputBuffer,
                              DeviceExtension->ScratchContext.ScratchBuffer,
                              *DataLength);
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleMcnSyncFakeIoctl(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

    Handles an IOCTL_MCN_SYNC_FAKE_IOCTL request

Arguments:

    DeviceExtension - device context
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                        status = STATUS_SUCCESS;
    PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
    BOOLEAN                         shouldRetry = TRUE;
    BOOLEAN                         requestSent = FALSE;

    PAGED_CODE ();

    *DataLength = 0;

    //
    // Try to acquire the media change event.  If we can't do it immediately
    // then bail out and assume the caller will try again later.
    //
    while (shouldRetry)
    {

        status = RequestSetupMcnRequest(DeviceExtension,
                                        info->Gesn.Supported);

        if (!NT_SUCCESS(status))
        {
            shouldRetry = FALSE;
        }

        if (NT_SUCCESS(status))
        {
            requestSent = RequestSendMcnRequest(DeviceExtension);

            if (requestSent)
            {
                shouldRetry = RequestPostWorkMcnRequest(DeviceExtension);
            }
            else
            {
                shouldRetry = FALSE;
            }
        }
    }

    // If there were any media change notifications that were not delivered
    // for some reason, make an attempt to do so at this time.
    DeviceSendDelayedMediaChangeNotifications(DeviceExtension);

    // Set the status and then complete the original request.
    // The timer handler will be able to send the next request.
    status = STATUS_SUCCESS;

    return status;
}

BOOLEAN
RequestIsRealtimeStreaming(
    _In_  WDFREQUEST       Request,
    _In_  BOOLEAN          IsReadRequest
    )
/*++

Routine Description:

    Checks whether a given read/write request should
    be performed in Real-Time Streaming mode.

Arguments:

    Request - request to be checked
    IsReadRequest - TRUE = read request; FALSE = write request

Return Value:

    TRUE  - a Real-Time Streaming operation has to be performed
    FALSE - a normal (non-Streaming) operation has to be performed

--*/
{
    BOOLEAN     useStreaming = FALSE;

    if (!useStreaming) {
        //
        // Check if we're required to use Streaming via I/O Stack Location flags
        //
        UCHAR currentStackLocationFlags = 0;
        currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);

        useStreaming = TEST_FLAG(currentStackLocationFlags, SL_REALTIME_STREAM);
    }

    if (!useStreaming) {
        //
        // Check if we were previously requested to enforce Streaming for
        // the file handle through which this request was sent.
        //

        WDFFILEOBJECT           fileObject;
        PFILE_OBJECT_CONTEXT    fileObjectContext;

        fileObject = WdfRequestGetFileObject(Request);

        if (fileObject != NULL) {
            fileObjectContext = FileObjectGetContext(fileObject);
            NT_ASSERT(fileObjectContext != NULL);

            if (IsReadRequest && fileObjectContext->EnforceStreamingRead)
            {
                useStreaming = TRUE;
            }

            if (!IsReadRequest && fileObjectContext->EnforceStreamingWrite)
            {
                useStreaming = TRUE;
            }
        }
    }

    return useStreaming;
}


NTSTATUS
RequestValidateReadWrite(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
    )
/*++

Routine Description:

   Validate Read/Write request

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);

    BOOLEAN             isValid = TRUE;
    LONGLONG            startingOffset = 0;
    size_t              transferByteCount = 0;
    PIRP                irp = NULL;
    PIO_STACK_LOCATION  currentStack = NULL;

    irp = WdfRequestWdmGetIrp(Request);
    currentStack = IoGetCurrentIrpStackLocation(irp);

    if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
        (currentStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
        !TEST_FLAG(currentStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
    {
        //  DO_VERIFY_VOLUME is set for the device object,
        //  but this request is not itself a verify request.
        //  So fail this request.

        //set the status for volume verification.
        status = STATUS_VERIFY_REQUIRED;
    }

    if (NT_SUCCESS(status))
    {
        if (PLAY_ACTIVE(DeviceExtension))
        {
            status = STATUS_DEVICE_BUSY;
        }
    }

    if (NT_SUCCESS(status))
    {
        // If the device is in exclusive mode, check whether the request is from
        // the handle that locked the device.
        if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
        {
            // This request is not from the owner. We can't let the operation go.
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Access Denied! Device in exclusive mode.\n"));

            status = STATUS_ACCESS_DENIED;
        }
    }

    // Validate the request alignment.
    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Type == WdfRequestTypeRead)
        {
            startingOffset = RequestParameters.Parameters.Read.DeviceOffset;
            transferByteCount = RequestParameters.Parameters.Read.Length;
        }
        else
        {
            startingOffset = RequestParameters.Parameters.Write.DeviceOffset;
            transferByteCount = RequestParameters.Parameters.Write.Length;
        }

        if (!DeviceExtension->DiskGeometry.BytesPerSector)
        {
            DeviceExtension->DiskGeometry.BytesPerSector = 2048;
        }

        if (!DeviceExtension->SectorShift)
        {
            DeviceExtension->SectorShift = 11;
        }

        // Perform some basic validation up front
        if (TEST_FLAG(startingOffset, DeviceExtension->DiskGeometry.BytesPerSector - 1) ||
            TEST_FLAG(transferByteCount,       DeviceExtension->DiskGeometry.BytesPerSector - 1))
        {
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }

    // validate the request against the current mmc schema
    if (NT_SUCCESS(status))
    {
        FEATURE_NUMBER schema = cdData->Mmc.ValidationSchema;

        // We validate read requests according to the RandomWritable schema, except in the
        // case of IncrementalStreamingWritable, wherein  the drive is responsible for all
        // of the verification
        if (RequestParameters.Type == WdfRequestTypeRead)
        {
            if (!cdData->Mmc.WriteAllowed)
            {
                // standard legacy validation of read irps
                // if writing is not allowed on the media
                schema =  FeatureRandomWritable;
            }
            else if (schema != FeatureIncrementalStreamingWritable)
            {
                // standard legacy validation of read irps
                // if not using streaming writes on writable media
                schema =  FeatureRandomWritable;
            }
        }

        // Fail write requests to read-only media
        if ((RequestParameters.Type == WdfRequestTypeWrite) &&
            !(cdData->Mmc.WriteAllowed))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Write request to read-only media\n"));
            isValid = FALSE;
        }

        if (isValid)
        {
            switch (schema)
            {
                case FeatureDefectManagement:
                case FeatureRandomWritable:
                    // Ensure that the request is within bounds for ROM drives.
                    // Writer drives do not need to have bounds as outbounds request should not damage the drive.
                    if(!cdData->Mmc.IsWriter)
                    {
                        if ((startingOffset >= DeviceExtension->PartitionLength.QuadPart) ||
                            startingOffset < 0)
                        {
                            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
                            isValid = FALSE;

                        }
                        else
                        {
                            ULONGLONG bytesRemaining = DeviceExtension->PartitionLength.QuadPart - startingOffset;

                            if ((ULONGLONG)transferByteCount > bytesRemaining)
                            {
                                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
                                isValid = FALSE;
                            }
                        }
                    }
                    break;

                case FeatureRigidRestrictedOverwrite:
                    // Ensure that the number of blocks is a multiple of the blocking size
                    if (((transferByteCount >> DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
                    {
                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
                                   "RequestValidateReadWrite: Number of blocks is not a multiple of the blocking size (%x)\n",
                                   cdData->Mmc.Blocking));

                        isValid = FALSE;
                    }
                    // Fall through
                case FeatureRestrictedOverwrite:
                    // Ensure that the request begins on a blocking boundary
                    if ((Int64ShrlMod32(startingOffset, DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
                    {
                        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
                                   "RequestValidateReadWrite: Starting block is not a multiple of the blocking size (%x)\n",
                                   cdData->Mmc.Blocking));

                        isValid = FALSE;
                    }
                    break;

                case FeatureIncrementalStreamingWritable:
                    // Let the drive handle the verification
                    break;

                default:
                    // Unknown schema. Fail the request
                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
                               "RequestValidateReadWrite: Unknown validation schema (%x)\n",
                               schema));

                    isValid = FALSE;
                    break;
            } //end of switch (schema)
        } // end of if (isValid)

        if (!isValid)
        {
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    } // end of mmc schema validation

    // validate that the Real-Time Streaming requests meet device capabilties
    if (NT_SUCCESS(status))
    {
        // We do not check for FDO_HACK_NO_STREAMING in DeviceExtension->PrivateFdoData->HackFlags here,
        // because we're going to hide device failures related to streaming reads/writes from the sender
        // of the request. FDO_HACK_NO_STREAMING is going to be taken into account later during actual
        // processing of the request.
        if (RequestIsRealtimeStreaming(Request, RequestParameters.Type == WdfRequestTypeRead))
        {
            if (RequestParameters.Type == WdfRequestTypeRead && !cdData->Mmc.StreamingReadSupported)
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
                           "RequestValidateReadWrite: Streaming reads are not supported.\n"));

                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            if (RequestParameters.Type == WdfRequestTypeWrite && !cdData->Mmc.StreamingWriteSupported)
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
                           "RequestValidateReadWrite: Streaming writes are not supported.\n"));

                status = STATUS_INVALID_DEVICE_REQUEST;
            }
        }
    }

    return status;
}

NTSTATUS
RequestHandleReadWrite(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters
    )
/*++

Routine Description:

    Handle a read/write request

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);

    size_t              transferByteCount = 0;
    PIRP                irp = NULL;
    PIO_STACK_LOCATION  currentStack = NULL;

    PUCHAR              dataBuffer;


    irp = WdfRequestWdmGetIrp(Request);
    currentStack = IoGetCurrentIrpStackLocation(irp);
    dataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);

    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Type == WdfRequestTypeRead)
        {
            transferByteCount = RequestParameters.Parameters.Read.Length;
        }
        else
        {
            transferByteCount = RequestParameters.Parameters.Write.Length;
        }

        if (transferByteCount == 0)
        {
            //  Several parts of the code turn 0 into 0xffffffff,
            //  so don't process a zero-length request any further.
            status = STATUS_SUCCESS;
            RequestCompletion(DeviceExtension, Request, status, 0);
            return status;
        }

        // Add partition byte offset to make starting byte relative to
        // beginning of disk.
        currentStack->Parameters.Read.ByteOffset.QuadPart += (DeviceExtension->StartingOffset.QuadPart);

        //not very necessary as the starting offset for CD/DVD device is always 0.
        if (RequestParameters.Type == WdfRequestTypeRead)
        {
            RequestParameters.Parameters.Read.DeviceOffset = currentStack->Parameters.Read.ByteOffset.QuadPart;
        }
        else
        {
            RequestParameters.Parameters.Write.DeviceOffset = currentStack->Parameters.Write.ByteOffset.QuadPart;
        }
    }

    if (NT_SUCCESS(status))
    {
        ULONG   entireXferLen = currentStack->Parameters.Read.Length;
        ULONG   maxLength = 0;
        ULONG   packetsCount = 0;

        PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext;
        PCDROM_REQUEST_CONTEXT            requestContext;
        PCDROM_REQUEST_CONTEXT            originalRequestContext;

        // get the count of packets we need to send.
        if ((((ULONG_PTR)dataBuffer) & (PAGE_SIZE-1)) == 0)
        {
            maxLength = cdData->MaxPageAlignedTransferBytes;
        }
        else
        {
            maxLength = cdData->MaxUnalignedTransferBytes;
        }

        packetsCount = entireXferLen / maxLength;

        if (entireXferLen % maxLength != 0)
        {
            packetsCount++;
        }

        originalRequestContext = RequestGetContext(Request);


        ScratchBuffer_BeginUse(DeviceExtension);

        readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
        requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);

        readWriteContext->PacketsCount = packetsCount;
        readWriteContext->EntireXferLen = entireXferLen;
        readWriteContext->MaxLength = maxLength;
        readWriteContext->StartingOffset = currentStack->Parameters.Read.ByteOffset;
        readWriteContext->DataBuffer = dataBuffer;
        readWriteContext->TransferedBytes = 0;
        readWriteContext->IsRead = (RequestParameters.Type == WdfRequestTypeRead);

        requestContext->OriginalRequest = Request;
        requestContext->DeviceExtension = DeviceExtension;

        //
        // Setup the READ/WRITE fields in the original request which is what
        // we use to properly synchronize cancellation logic between the
        // cancel callback and the timer routine.
        //

        originalRequestContext->ReadWriteIsCompleted = FALSE;
        originalRequestContext->ReadWriteRetryInitialized = FALSE;
        originalRequestContext->DeviceExtension = DeviceExtension;

        status = ScratchBuffer_PerformNextReadWrite(DeviceExtension, TRUE);

        // We do not call ScratchBuffer_EndUse here, because we're not releasing the scratch SRB.
        // It will be released in the completion routine.
    }

    return status;
}

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
RequestHandleLoadEjectMedia(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_EJECT_MEDIA
                     IOCTL_STORAGE_LOAD_MEDIA
                     IOCTL_STORAGE_LOAD_MEDIA2

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PZERO_POWER_ODD_INFO        zpoddInfo = DeviceExtension->ZeroPowerODDInfo;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        // Synchronize with ejection control and ejection cleanup code as
        // well as other eject/load requests.
        WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);

        if(DeviceExtension->ProtectedLockCount != 0)
        {
            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "RequestHandleLoadEjectMedia: call to eject protected locked "
                                                                "device - failure\n"));
            status = STATUS_DEVICE_BUSY;
        }

        if (NT_SUCCESS(status))
        {
            SCSI_REQUEST_BLOCK  srb;
            PCDB                cdb = (PCDB)srb.Cdb;

            RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));

            srb.CdbLength = 6;

            cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
            cdb->START_STOP.LoadEject = 1;

            if(RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECT_MEDIA)
            {
                cdb->START_STOP.Start = 0;

                // We are sending down a soft eject, and in this case we should take an active ref
                // if the command succeeds.
                if ((zpoddInfo != NULL) &&
                    (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
                    (zpoddInfo->Load == 0))                                         // Drawer
                {
                    zpoddInfo->MonitorStartStopUnit = TRUE;
                }
            }
            else
            {
                cdb->START_STOP.Start = 1;
            }

            status = DeviceSendSrbSynchronously(DeviceExtension->Device,
                                                &srb,
                                                NULL,
                                                0,
                                                FALSE,
                                                Request);

            if (zpoddInfo != NULL)
            {
                zpoddInfo->MonitorStartStopUnit = FALSE;
            }
        }

        WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
    }

    return status;
}


_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
RequestHandleReserveRelease(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_RESERVE
                     IOCTL_STORAGE_RELEASE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PSCSI_REQUEST_BLOCK srb = NULL;
    PCDB                cdb = NULL;
    ULONG               ioctlCode = 0;

    PAGED_CODE ();

    *DataLength = 0;

    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                         sizeof(SCSI_REQUEST_BLOCK) +
                         (sizeof(ULONG_PTR) * 2),
                         CDROM_TAG_SRB);

    if (srb == NULL)
    {
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (NT_SUCCESS(status))
    {
        ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
        cdb = (PCDB)srb->Cdb;
        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));

        if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
        {
            srb->CdbLength = 10;
            cdb->CDB10.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
        }
        else
        {
            srb->CdbLength = 6;
            cdb->CDB6GENERIC.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
        }

        // Set timeout value.
        srb->TimeOutValue = DeviceExtension->TimeOutValue;

        // Send reserves as tagged requests.
        if (ioctlCode == IOCTL_STORAGE_RESERVE)
        {
            SET_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
            srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
        }

        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
                                            srb,
                                            NULL,
                                            0,
                                            FALSE,
                                            Request);
        // no data transfer.
        *DataLength = 0;

        FREE_POOL(srb);
    }

    return status;
} // end RequestHandleReserveRelease()

static // __REACTOS__
BOOLEAN
ValidPersistentReserveScope(
    UCHAR Scope)
{
    switch (Scope) {
    case RESERVATION_SCOPE_LU:
    case RESERVATION_SCOPE_ELEMENT:

        return TRUE;

    default:

        return FALSE;
    }
}

static // __REACTOS__
BOOLEAN
ValidPersistentReserveType(
    UCHAR Type)
{
    switch (Type) {
    case RESERVATION_TYPE_WRITE_EXCLUSIVE:
    case RESERVATION_TYPE_EXCLUSIVE:
    case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS:
    case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS:

        return TRUE;

    default:

        return FALSE;
    }
}

NTSTATUS
RequestValidatePersistentReserve(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Validate request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
                     IOCTL_STORAGE_PERSISTENT_RESERVE_OUT

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;

    PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&reserveCommand,
                                           NULL);
    if (NT_SUCCESS(status))
    {
        if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(PERSISTENT_RESERVE_COMMAND)) ||
            (reserveCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND)))
        {
            *DataLength = 0;
            status = STATUS_INFO_LENGTH_MISMATCH;
        }
        else if ((ULONG_PTR)reserveCommand & DeviceExtension->AdapterDescriptor->AlignmentMask)
        {
            // Check buffer alignment. Only an issue if another kernel mode component
            // (not the I/O manager) allocates the buffer.
            *DataLength = 0;
            status = STATUS_INVALID_USER_BUFFER;
        }
    }

    if (NT_SUCCESS(status))
    {
        if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
        {
            if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < reserveCommand->PR_IN.AllocationLength)
            {
                *DataLength = 0;
                status = STATUS_INVALID_PARAMETER;
            }
            else
            {
                switch (reserveCommand->PR_IN.ServiceAction)
                {
                case RESERVATION_ACTION_READ_KEYS:
                    if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST))
                    {
                        *DataLength = 0;
                        status = STATUS_INVALID_PARAMETER;
                    }
                    break;

                case RESERVATION_ACTION_READ_RESERVATIONS:
                    if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST))
                    {
                        *DataLength = 0;
                        status = STATUS_INVALID_PARAMETER;
                    }
                    break;

                default:
                    *DataLength = 0;
                    status = STATUS_INVALID_PARAMETER;
                    break;
                }
            }
        }
        else // case of IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
        {
            // Verify ServiceAction, Scope, and Type
            switch (reserveCommand->PR_OUT.ServiceAction)
            {
            case RESERVATION_ACTION_REGISTER:
            case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING:
            case RESERVATION_ACTION_CLEAR:
                // Scope and type ignored.
                break;

            case RESERVATION_ACTION_RESERVE:
            case RESERVATION_ACTION_RELEASE:
            case RESERVATION_ACTION_PREEMPT:
            case RESERVATION_ACTION_PREEMPT_ABORT:
                if (!ValidPersistentReserveScope(reserveCommand->PR_OUT.Scope) ||
                    !ValidPersistentReserveType(reserveCommand->PR_OUT.Type))
                {
                    *DataLength = 0;
                    status = STATUS_INVALID_PARAMETER;
                }
                break;

            default:
                *DataLength = 0;
                status = STATUS_INVALID_PARAMETER;
                break;
            }

            // Check input buffer for PR Out.
            // Caller must include the PR parameter list.
            if (NT_SUCCESS(status))
            {
                if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
                        (sizeof(PERSISTENT_RESERVE_COMMAND) + sizeof(PRO_PARAMETER_LIST))) ||
                    (reserveCommand->Size < RequestParameters.Parameters.DeviceIoControl.InputBufferLength))
                {
                    *DataLength = 0;
                    status = STATUS_INVALID_PARAMETER;
                }
            }
        } // end of validation for IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
    }

    return status;
} // end RequestValidatePersistentReserve()


_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
RequestHandlePersistentReserve(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
                     IOCTL_STORAGE_PERSISTENT_RESERVE_OUT

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
    PSCSI_REQUEST_BLOCK srb = NULL;
    PCDB                cdb = NULL;
    BOOLEAN             writeToDevice;

    PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&reserveCommand,
                                           NULL);
    if (NT_SUCCESS(status))
    {
        srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                             sizeof(SCSI_REQUEST_BLOCK) +
                             (sizeof(ULONG_PTR) * 2),
                             CDROM_TAG_SRB);

        if (srb == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }
        else
        {
            cdb = (PCDB)srb->Cdb;
            RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
        }
    }

    if (NT_SUCCESS(status))
    {
        size_t dataBufLen = 0;

        // Fill in the CDB.
        if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
        {
            cdb->PERSISTENT_RESERVE_IN.OperationCode    = SCSIOP_PERSISTENT_RESERVE_IN;
            cdb->PERSISTENT_RESERVE_IN.ServiceAction    = reserveCommand->PR_IN.ServiceAction;

            REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength),
                                &(reserveCommand->PR_IN.AllocationLength));

            dataBufLen = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
            writeToDevice = FALSE;
        }
        else
        {
            cdb->PERSISTENT_RESERVE_OUT.OperationCode   = SCSIOP_PERSISTENT_RESERVE_OUT;
            cdb->PERSISTENT_RESERVE_OUT.ServiceAction   = reserveCommand->PR_OUT.ServiceAction;
            cdb->PERSISTENT_RESERVE_OUT.Scope           = reserveCommand->PR_OUT.Scope;
            cdb->PERSISTENT_RESERVE_OUT.Type            = reserveCommand->PR_OUT.Type;

            cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST);

            // Move the parameter list to the beginning of the data buffer (so it is aligned
            // correctly and that the MDL describes it correctly).
            RtlMoveMemory(reserveCommand,
                          reserveCommand->PR_OUT.ParameterList,
                          sizeof(PRO_PARAMETER_LIST));

            dataBufLen = sizeof(PRO_PARAMETER_LIST);
            writeToDevice = TRUE;
        }

        srb->CdbLength = 10;
        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;

        status = DeviceSendSrbSynchronously(DeviceExtension->Device,
                                            srb,
                                            reserveCommand,
                                            (ULONG) dataBufLen,
                                            writeToDevice,
                                            Request);

        FREE_POOL(srb);
    }

    return status;
}

#if (NTDDI_VERSION >= NTDDI_WIN8)
_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleAreVolumesReady(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DISK_ARE_VOLUMES_READY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    BOOLEAN                 completeRequest = TRUE;
    NTSTATUS                status = STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(RequestParameters);

    *DataLength = 0;

    if (DeviceExtension->IsVolumeOnlinePending == FALSE)
    {
        status = STATUS_SUCCESS;
        goto Cleanup;
    }

    //
    // Add to the volume ready queue. No worries about request cancellation,
    // since KMDF will automatically handle that while request is in queue.
    //
    status = WdfRequestForwardToIoQueue(Request,
                                        DeviceExtension->ManualVolumeReadyQueue
                                        );

    if(!NT_SUCCESS(status))
    {
        goto Cleanup;
    }

    status = STATUS_PENDING;
    completeRequest = FALSE;

Cleanup:

    if (completeRequest)
    {
        RequestCompletion(DeviceExtension, Request, status, 0);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
RequestHandleVolumeOnline(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_VOLUME_ONLINE / IOCTL_VOLUME_POST_ONLINE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    WDFREQUEST              request;
    PIRP                    irp = NULL;
    PIO_STACK_LOCATION      nextStack = NULL;

    UNREFERENCED_PARAMETER(RequestParameters);

    *DataLength = 0;

    DeviceExtension->IsVolumeOnlinePending = FALSE;

    // Complete all parked volume ready requests.
    for (;;)
    {
        status = WdfIoQueueRetrieveNextRequest(DeviceExtension->ManualVolumeReadyQueue,
                                               &request);

        if (!NT_SUCCESS(status))
        {
            break;
        }

        RequestCompletion(DeviceExtension, request, STATUS_SUCCESS, 0);
    }

    // Change the IOCTL code to IOCTL_DISK_VOLUMES_ARE_READY, and forward the request down,
    // so that if the underlying port driver will also know that volume is ready.
    WdfRequestFormatRequestUsingCurrentType(Request);
    irp = WdfRequestWdmGetIrp(Request);
    nextStack = IoGetNextIrpStackLocation(irp);

    irp->AssociatedIrp.SystemBuffer = &DeviceExtension->DeviceNumber;
    nextStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
    nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof (DeviceExtension->DeviceNumber);
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_VOLUMES_ARE_READY;

    // Send the request straight down (synchronously).
    RequestSend(DeviceExtension,
                Request,
                DeviceExtension->IoTarget,
                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
                NULL);

    return STATUS_SUCCESS;
}
#endif

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
DeviceHandleRawRead(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_RAW_READ

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    // Determine whether the drive is currently in raw or cooked mode,
    // and which command to use to read the data.

    NTSTATUS        status = STATUS_SUCCESS;
    PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);
    RAW_READ_INFO   rawReadInfo = {0};
    PVOID           outputVirtAddr = NULL;
    ULONG           startingSector;

    PIRP                irp = WdfRequestWdmGetIrp(Request);
    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(irp);

    VOID*               outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
        (currentIrpStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
        !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
    {
        //  DO_VERIFY_VOLUME is set for the device object,
        //  but this request is not itself a verify request.
        //  So fail this request.
        (VOID) MediaReadCapacity(DeviceExtension->Device);

        *DataLength = 0;
        //set the status for volume verification.
        status = STATUS_VERIFY_REQUIRED;
    }

    if (NT_SUCCESS(status))
    {
        // Since this ioctl is METHOD_OUT_DIRECT, we need to copy away
        // the input buffer before interpreting it.  This prevents a
        // malicious app from messing with the input buffer while we
        // are interpreting it. This is done in dispacth.
        //
        // Here, we are going to get the input buffer out of saved place.
        rawReadInfo = *(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;

        if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength > 0)
        {
            // Make sure that any user buffer that we pass down to
            // the hardware is properly aligned
            NT_ASSERT(irp->MdlAddress);
            outputVirtAddr = MmGetMdlVirtualAddress(irp->MdlAddress);
            if ((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask)
            {
                NT_ASSERT(!((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask));
                *DataLength = 0;
                status = STATUS_INVALID_PARAMETER;
            }
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        switch (rawReadInfo.TrackMode)
        {
        case CDDA:
        case YellowMode2:
        case XAForm2:
            // no check needed.
            break;

        case RawWithC2AndSubCode:
            if (!cdData->Mmc.ReadCdC2Pointers || !cdData->Mmc.ReadCdSubCode)
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                          "Request to read C2 & Subcode rejected.  "
                          "Is C2 supported: %d   Is Subcode supported: %d\n",
                          cdData->Mmc.ReadCdC2Pointers,
                          cdData->Mmc.ReadCdSubCode
                          ));
                *DataLength = 0;
                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            break;
        case RawWithC2:
            if (!cdData->Mmc.ReadCdC2Pointers)
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                          "Request to read C2 rejected because drive does not "
                          "report support for C2 pointers\n"
                          ));
                *DataLength = 0;
                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            break;
        case RawWithSubCode:

            if (!cdData->Mmc.ReadCdSubCode)
            {
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                          "Request to read subcode rejected because drive does "
                          "not report support for reading the subcode data\n"
                          ));
                *DataLength = 0;
                status = STATUS_INVALID_DEVICE_REQUEST;
            }
            break;

        default:
            *DataLength = 0;
            status = STATUS_INVALID_DEVICE_REQUEST;
            break;
        }
    }

    if (NT_SUCCESS(status))
    {
        PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
        PCDB                cdb = (PCDB)(srb->Cdb);

        size_t              transferByteCount;
        BOOLEAN             shouldRetry = TRUE;
        ULONG               timesAlreadyRetried = 0;
        LONGLONG            retryIn100nsUnits = 0;

        transferByteCount = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;

        ScratchBuffer_BeginUse(DeviceExtension);

        while (shouldRetry)
        {
            // Setup cdb depending upon the sector type we want.
            switch (rawReadInfo.TrackMode)
            {
            case CDDA:
                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
                cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR;
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 3;
                cdb->READ_CD.IncludeSyncData = 1;
                break;

            case YellowMode2:
                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
                cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR;
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 1;
                cdb->READ_CD.IncludeSyncData = 1;
                break;

            case XAForm2:
                transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
                cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR;
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 3;
                cdb->READ_CD.IncludeSyncData = 1;
                break;

            case RawWithC2AndSubCode:
                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
                cdb->READ_CD.IncludeSyncData = 1;
                cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
                cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
                break;

            case RawWithC2:
                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
                cdb->READ_CD.IncludeSyncData = 1;
                cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
                break;

            case RawWithSubCode:
                transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
                cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
                cdb->READ_CD.IncludeUserData = 1;
                cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
                cdb->READ_CD.IncludeSyncData = 1;
                cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
                break;

            default:
                // should already checked before coming in loop.
                NT_ASSERT(FALSE);
                break;
            }

            ScratchBuffer_SetupSrb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE);
            // Restore the buffer, buffer size and MdlAddress. They got changed but we don't want to use the scratch buffer.
            {
                PIRP    scratchIrp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
                scratchIrp->MdlAddress = irp->MdlAddress;
                srb->DataBuffer = outputVirtAddr;
                srb->DataTransferLength = (ULONG)transferByteCount;
            }
            // Calculate starting offset.
            startingSector = (ULONG)(rawReadInfo.DiskOffset.QuadPart >> DeviceExtension->SectorShift);

            // Fill in CDB fields.
            srb->CdbLength = 12;

            cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
            cdb->READ_CD.TransferBlocks[2]  = (UCHAR) (rawReadInfo.SectorCount & 0xFF);
            cdb->READ_CD.TransferBlocks[1]  = (UCHAR) (rawReadInfo.SectorCount >> 8 );
            cdb->READ_CD.TransferBlocks[0]  = (UCHAR) (rawReadInfo.SectorCount >> 16);

            cdb->READ_CD.StartingLBA[3]  = (UCHAR) (startingSector & 0xFF);
            cdb->READ_CD.StartingLBA[2]  = (UCHAR) ((startingSector >>  8));
            cdb->READ_CD.StartingLBA[1]  = (UCHAR) ((startingSector >> 16));
            cdb->READ_CD.StartingLBA[0]  = (UCHAR) ((startingSector >> 24));

            ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);

            if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
                (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
            {
                shouldRetry = FALSE;
                status = STATUS_CANCELLED;
            }
            else
            {
                shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
                                                                        timesAlreadyRetried,
                                                                        &status,
                                                                        &retryIn100nsUnits);
                if (shouldRetry)
                {
                    LARGE_INTEGER t;
                    t.QuadPart = -retryIn100nsUnits;
                    timesAlreadyRetried++;
                    KeDelayExecutionThread(KernelMode, FALSE, &t);
                    // keep items clean
                    ScratchBuffer_ResetItems(DeviceExtension, FALSE);
                }
            }
        }

        if (NT_SUCCESS(status))
        {
            *DataLength = srb->DataTransferLength;
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandlePlayAudioMsf(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_PLAY_AUDIO_MSF

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PCDROM_PLAY_AUDIO_MSF   inputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&inputBuffer,
                                           NULL);


    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;

        cdb.PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
        cdb.PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
        cdb.PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;

        cdb.PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
        cdb.PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
        cdb.PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            PLAY_ACTIVE(DeviceExtension) = TRUE;
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleReadQChannel(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_READ_Q_CHANNEL

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;
    PVOID       inputBuffer = NULL;
    PVOID       outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = ReadQChannel(DeviceExtension,
                              Request,
                              inputBuffer,
                              RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                              outputBuffer,
                              RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                              DataLength);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
ReadQChannel(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_opt_  WDFREQUEST           OriginalRequest,
    _In_  PVOID                    InputBuffer,
    _In_  size_t                   InputBufferLength,
    _In_  PVOID                    OutputBuffer,
    _In_  size_t                   OutputBufferLength,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   base function to handle request of IOCTL_CDROM_READ_Q_CHANNEL

Arguments:

    DeviceExtension - device context
    OriginalRequest - original request to be handled
    InputBuffer - input buffer
    InputBufferLength - length of input buffer
    OutputBuffer - output buffer
    OutputBufferLength - length of output buffer

Return Value:

    NTSTATUS
    DataLength - returned data length

--*/
{
    NTSTATUS                    status = STATUS_SUCCESS;
    ULONG                       transferByteCount = 0;
    CDB                         cdb;
    PCDROM_SUB_Q_DATA_FORMAT    inputBuffer = (PCDROM_SUB_Q_DATA_FORMAT)InputBuffer;
    PSUB_Q_CHANNEL_DATA         userChannelData = (PSUB_Q_CHANNEL_DATA)OutputBuffer;

    PAGED_CODE ();

    UNREFERENCED_PARAMETER(InputBufferLength);

    *DataLength = 0;

    // Set size of channel data -- however, this is dependent on
    // what information we are requesting (which Format)
    switch( inputBuffer->Format )
    {
        case IOCTL_CDROM_CURRENT_POSITION:
            transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
            break;

        case IOCTL_CDROM_MEDIA_CATALOG:
            transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
            break;

        case IOCTL_CDROM_TRACK_ISRC:
            transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
            break;
    }

    ScratchBuffer_BeginUse(DeviceExtension);

    RtlZeroMemory(&cdb, sizeof(CDB));
    // Set up the CDB
    // Always logical unit 0, but only use MSF addressing
    // for IOCTL_CDROM_CURRENT_POSITION
    if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
    {
        cdb.SUBCHANNEL.Msf = CDB_USE_MSF;
    }

    // Return subchannel data
    cdb.SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;

    // Specify format of informatin to return
    cdb.SUBCHANNEL.Format = inputBuffer->Format;

    // Specify which track to access (only used by Track ISRC reads)
    if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC)
    {
        cdb.SUBCHANNEL.TrackNumber = inputBuffer->Track;
    }

    cdb.SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
    cdb.SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
    cdb.SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;

    status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, transferByteCount, TRUE, &cdb, 10);

    if (NT_SUCCESS(status))
    {
        PSUB_Q_CHANNEL_DATA subQPtr = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;

#if DBG
        switch( inputBuffer->Format )
        {
        case IOCTL_CDROM_CURRENT_POSITION:
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
            break;

        case IOCTL_CDROM_MEDIA_CATALOG:
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
            break;

        case IOCTL_CDROM_TRACK_ISRC:
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
            break;
        }
#endif

        // Update the play active status.
        if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
        {
            PLAY_ACTIVE(DeviceExtension) = TRUE;
        }
        else
        {
            PLAY_ACTIVE(DeviceExtension) = FALSE;
        }

        // Check if output buffer is large enough to contain
        // the data.
        if (OutputBufferLength < DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength)
        {
            DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength = (ULONG)OutputBufferLength;
        }

        // Copy our buffer into users.
        RtlMoveMemory(userChannelData,
                      subQPtr,
                      DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);

        *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
    }

    // nothing to do after the command finishes.
    ScratchBuffer_EndUse(DeviceExtension);

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandlePauseAudio(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_PAUSE_AUDIO

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
        cdb.PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            PLAY_ACTIVE(DeviceExtension) = FALSE;
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleResumeAudio(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_RESUME_AUDIO

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
        cdb.PAUSE_RESUME.Action = CDB_AUDIO_RESUME;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            PLAY_ACTIVE(DeviceExtension) = TRUE;  //not in original code. But we should set it.
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleSeekAudioMsf(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_SEEK_AUDIO_MSF

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS              status = STATUS_SUCCESS;
    PCDROM_SEEK_AUDIO_MSF inputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;
        ULONG   logicalBlockAddress;

        logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F);

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.SEEK.OperationCode      = SCSIOP_SEEK;
        cdb.SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
        cdb.SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
        cdb.SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
        cdb.SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            *DataLength = 0;
        }

        // nothing to do after the command finishes.

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleStopAudio(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _Out_ size_t *                 DataLength
    )
{
    NTSTATUS                status = STATUS_SUCCESS;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = 0;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
        cdb.START_STOP.Immediate = 1;
        cdb.START_STOP.Start = 0;
        cdb.START_STOP.LoadEject = 0;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 6);

        if (NT_SUCCESS(status))
        {
            PLAY_ACTIVE(DeviceExtension) = FALSE;
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleGetSetVolume(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_GET_VOLUME
                     IOCTL_CDROM_SET_VOLUME

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS              status = STATUS_SUCCESS;

    PAGED_CODE ();

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        ULONG   transferSize = MODE_DATA_SIZE;
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
        cdb.MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
        cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
        cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 10);

        if (NT_SUCCESS(status))
        {
            if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)
            {
                PAUDIO_OUTPUT   audioOutput;
                PVOLUME_CONTROL volumeControl;
                ULONG           bytesTransferred;

                status = WdfRequestRetrieveOutputBuffer(Request,
                                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                        (PVOID*)&volumeControl,
                                                        NULL);
                if (NT_SUCCESS(status))
                {
                    audioOutput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
                                                            DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
                                                            CDROM_AUDIO_CONTROL_PAGE,
                                                            FALSE);

                    // Verify the page is as big as expected.
                    bytesTransferred = (ULONG)((PCHAR)audioOutput - (PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer) +
                                        sizeof(AUDIO_OUTPUT);

                    if ((audioOutput != NULL) &&
                        (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength >= bytesTransferred))
                    {
                        ULONG i;
                        for (i=0; i<4; i++)
                        {
                            volumeControl->PortVolume[i] = audioOutput->PortOutput[i].Volume;
                        }

                        // Set bytes transferred in IRP.
                        *DataLength = sizeof(VOLUME_CONTROL);

                    }
                    else
                    {
                        *DataLength = 0;
                        status = STATUS_INVALID_DEVICE_REQUEST;
                    }
                }
            }
            else    //IOCTL_CDROM_SET_VOLUME
            {
                PAUDIO_OUTPUT   audioInput = NULL;
                PAUDIO_OUTPUT   audioOutput = NULL;
                PVOLUME_CONTROL volumeControl = NULL;
                ULONG           i,bytesTransferred,headerLength;

                status = WdfRequestRetrieveInputBuffer(Request,
                                                       RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                                       (PVOID*)&volumeControl,
                                                       NULL);

                if (NT_SUCCESS(status))
                {
                    audioInput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
                                                           DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
                                                           CDROM_AUDIO_CONTROL_PAGE,
                                                           FALSE);

                    // Check to make sure the mode sense data is valid before we go on
                    if(audioInput == NULL)
                    {
                        TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                                    "Mode Sense Page %d not found\n",
                                    CDROM_AUDIO_CONTROL_PAGE));

                        *DataLength = 0;
                        status = STATUS_INVALID_DEVICE_REQUEST;
                    }
                }

                if (NT_SUCCESS(status))
                {
                    // keep items clean; clear the command history
                    ScratchBuffer_ResetItems(DeviceExtension, TRUE);

                    headerLength = sizeof(MODE_PARAMETER_HEADER10);
                    bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;

                    // use the scratch buffer as input buffer.
                    // the content of this buffer will not be changed in the following loop.
                    audioOutput = (PAUDIO_OUTPUT)((PCHAR)DeviceExtension->ScratchContext.ScratchBuffer + headerLength);

                    for (i=0; i<4; i++)
                    {
                        audioOutput->PortOutput[i].Volume = volumeControl->PortVolume[i];
                        audioOutput->PortOutput[i].ChannelSelection = audioInput->PortOutput[i].ChannelSelection;
                    }

                    audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
                    audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
                    audioOutput->Immediate = MODE_SELECT_IMMEDIATE;

                    RtlZeroMemory(&cdb, sizeof(CDB));
                    // Set up the CDB
                    cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
                    cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8);
                    cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF);
                    cdb.MODE_SELECT10.PFBit = 1;

                    status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, bytesTransferred, FALSE, &cdb, 10);

                }
                *DataLength = 0;
            }
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleReadDvdStructure(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DVD_READ_STRUCTURE

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS    status = STATUS_SUCCESS;
    PVOID       inputBuffer = NULL;
    PVOID       outputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = ReadDvdStructure(DeviceExtension,
                                  Request,
                                  inputBuffer,
                                  RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                  outputBuffer,
                                  RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                  DataLength);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
ReadDvdStructure(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_opt_  WDFREQUEST           OriginalRequest,
    _In_  PVOID                    InputBuffer,
    _In_  size_t                   InputBufferLength,
    _In_  PVOID                    OutputBuffer,
    _In_  size_t                   OutputBufferLength,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   base function to handle request of IOCTL_DVD_START_SESSION
                                      IOCTL_DVD_READ_KEY

Arguments:

    DeviceExtension - device context
    OriginalRequest - original request to be handled
    InputBuffer - input buffer
    InputBufferLength - length of input buffer
    OutputBuffer - output buffer
    OutputBufferLength - length of output buffer

Return Value:

    NTSTATUS
    DataLength - returned data length

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDVD_READ_STRUCTURE     request = (PDVD_READ_STRUCTURE)InputBuffer;
    PDVD_DESCRIPTOR_HEADER  header = (PDVD_DESCRIPTOR_HEADER)OutputBuffer;
    CDB                     cdb;

    USHORT                  dataLength;
    ULONG                   blockNumber;
    PFOUR_BYTE              fourByte;

    PAGED_CODE ();

    UNREFERENCED_PARAMETER(InputBufferLength);

    if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
    {
        *DataLength = 0;
        return STATUS_INVALID_DEVICE_REQUEST;
    }

    dataLength = (USHORT)OutputBufferLength;
    blockNumber = (ULONG)(request->BlockByteOffset.QuadPart >> DeviceExtension->SectorShift);
    fourByte = (PFOUR_BYTE)&blockNumber;

    ScratchBuffer_BeginUse(DeviceExtension);

    RtlZeroMemory(&cdb, sizeof(CDB));
    // Set up the CDB
    cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3;
    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2;
    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1;
    cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0;
    cdb.READ_DVD_STRUCTURE.LayerNumber   = request->LayerNumber;
    cdb.READ_DVD_STRUCTURE.Format        = (UCHAR)request->Format;

#if DBG
    {
        if ((UCHAR)request->Format > DvdMaxDescriptor)
        {
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
                        "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
                        (UCHAR)request->Format,
                        READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor],
                        dataLength
                        ));
        }
        else
        {
            TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
                        "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
                        (UCHAR)request->Format,
                        READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format],
                        dataLength
                        ));
        }
    }
#endif // DBG

    if (request->Format == DvdDiskKeyDescriptor)
    {
        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)request->SessionId;
    }

    cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8);
    cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff);

    status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, dataLength, TRUE, &cdb, 12);

    if (NT_SUCCESS(status))
    {
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                    "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", request->Format));

        RtlMoveMemory(header,
                      DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
                      DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);

        // Cook the data.  There are a number of fields that really
        // should be byte-swapped for the caller.
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                  "DvdDCCompletion - READ_STRUCTURE:\n"
                  "\tHeader at %p\n"
                  "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n"
                  "\tDataBuffer was at %p\n"
                  "\tDataTransferLength was %lx\n",
                  header,
                  header->Data,
                  DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
                  DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));

        // First the fields in the header
        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: header->Length %lx -> ",
                   header->Length));

        REVERSE_SHORT(&header->Length);

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", header->Length));

        // Now the fields in the descriptor
        if(request->Format == DvdPhysicalDescriptor)
        {
            ULONG tempLength = (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength > (ULONG)FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
                                ? (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
                                : 0;

            PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR)&(header->Data[0]);

            // Make sure the buffer size is good for swapping bytes.
            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, StartingDataSector))
            {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: StartingDataSector %lx -> ",
                               layer->StartingDataSector));
                REVERSE_LONG(&(layer->StartingDataSector));
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->StartingDataSector));
            }
            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndDataSector))
            {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndDataSector %lx -> ",
                               layer->EndDataSector));
                REVERSE_LONG(&(layer->EndDataSector));
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndDataSector));
            }
            if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndLayerZeroSector))
            {
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndLayerZeroSector %lx -> ",
                               layer->EndLayerZeroSector));
                REVERSE_LONG(&(layer->EndLayerZeroSector));
                TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndLayerZeroSector));
            }
        }

        if (!NT_SUCCESS(status))
        {
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Status is %lx\n", status));
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDeviceControlCompletion - "
                        "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n",
                        DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));
        }

        *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
    }

    ScratchBuffer_EndUse(DeviceExtension);

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleDvdEndSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DVD_END_SESSION

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID sessionId = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&sessionId,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG           transferSize = 0;
        CDB             cdb;
        DVD_SESSION_ID  currentSession = 0;
        DVD_SESSION_ID  limitSession = 0;

        if(*sessionId == DVD_END_ALL_SESSIONS)
        {
            currentSession = 0;
            limitSession = MAX_COPY_PROTECT_AGID - 1;
        }
        else
        {
            currentSession = *sessionId;
            limitSession = *sessionId;
        }

        ScratchBuffer_BeginUse(DeviceExtension);

        do
        {
            RtlZeroMemory(&cdb, sizeof(CDB));
            // Set up the CDB
            cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
            cdb.SEND_KEY.AGID = (UCHAR)(currentSession);
            cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;

            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);

            currentSession++;
        } while ((currentSession <= limitSession) && NT_SUCCESS(status));

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleDvdStartSessionReadKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DVD_START_SESSION
                     IOCTL_DVD_READ_KEY

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
    PVOID                   outputBuffer = NULL;

    PAGED_CODE ();

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                &outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status) && RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY)
    {
        status = WdfRequestRetrieveInputBuffer(Request,
                                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                               (PVOID*)&keyParameters,
                                               NULL);
    }

    if (NT_SUCCESS(status))
    {
        status = DvdStartSessionReadKey(DeviceExtension,
                                        RequestParameters.Parameters.DeviceIoControl.IoControlCode,
                                        Request,
                                        keyParameters,
                                        RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                        outputBuffer,
                                        RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                        DataLength);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DvdStartSessionReadKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  ULONG                    IoControlCode,
    _In_opt_  WDFREQUEST           OriginalRequest,
    _In_opt_  PVOID                InputBuffer,
    _In_  size_t                   InputBufferLength,
    _In_  PVOID                    OutputBuffer,
    _In_  size_t                   OutputBufferLength,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   base function to handle request of IOCTL_DVD_START_SESSION
                                      IOCTL_DVD_READ_KEY

Arguments:

    DeviceExtension - device context
    IoControlCode - IOCTL_DVD_READ_KEY or IOCTL_DVD_START_SESSION
    OriginalRequest - original request to be handled
    InputBuffer - input buffer
    InputBufferLength - length of input buffer
    OutputBuffer - output buffer
    OutputBufferLength - length of output buffer

Return Value:

    NTSTATUS
    DataLength - returned data length

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    ULONG                   keyLength = 0;
    ULONG                   result = 0;
    ULONG                   allocationLength;
    PFOUR_BYTE              fourByte;
    PDVD_COPY_PROTECT_KEY   keyParameters = (PDVD_COPY_PROTECT_KEY)InputBuffer;

    PAGED_CODE ();

    UNREFERENCED_PARAMETER(InputBufferLength);

    *DataLength = 0;

    fourByte = (PFOUR_BYTE)&allocationLength;

    if (IoControlCode == IOCTL_DVD_READ_KEY)
    {
        if (keyParameters == NULL)
        {
            status = STATUS_INTERNAL_ERROR;
        }

        // First of all, initialize the DVD region of the drive, if it has not been set yet
        if (NT_SUCCESS(status) &&
            (keyParameters->KeyType == DvdGetRpcKey) &&
            DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
        {
            DevicePickDvdRegion(DeviceExtension->Device);
        }

        if (NT_SUCCESS(status) &&
            (keyParameters->KeyType == DvdDiskKey))
        {
            // Special case - need to use READ DVD STRUCTURE command to get the disk key.
            PDVD_COPY_PROTECT_KEY   keyHeader = NULL;
            PDVD_READ_STRUCTURE     readStructureRequest = (PDVD_READ_STRUCTURE)keyParameters;

            // save the key header so we can restore the interesting parts later
            keyHeader = ExAllocatePoolWithTag(NonPagedPoolNx,
                                              sizeof(DVD_COPY_PROTECT_KEY),
                                              DVD_TAG_READ_KEY);

            if(keyHeader == NULL)
            {
                // Can't save the context so return an error
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
                            "DvdDeviceControl - READ_KEY: unable to allocate context\n"));
                status = STATUS_INSUFFICIENT_RESOURCES;
            }

            if (NT_SUCCESS(status))
            {
                PREAD_DVD_STRUCTURES_HEADER rawKey = OutputBuffer;
                PDVD_COPY_PROTECT_KEY       outputKey = OutputBuffer;

                // save input parameters
                RtlCopyMemory(keyHeader,
                              InputBuffer,
                              sizeof(DVD_COPY_PROTECT_KEY));

                readStructureRequest->Format = DvdDiskKeyDescriptor;
                readStructureRequest->BlockByteOffset.QuadPart = 0;
                readStructureRequest->LayerNumber = 0;
                readStructureRequest->SessionId = keyHeader->SessionId;

                status = ReadDvdStructure(DeviceExtension,
                                          OriginalRequest,
                                          InputBuffer,
                                          sizeof(DVD_READ_STRUCTURE),
                                          OutputBuffer,
                                          sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR),
                                          DataLength);

                // fill the output buffer, it's not touched in DeviceHandleReadDvdStructure()
                // for this specific request type: DvdDiskKeyDescriptor
                if (NT_SUCCESS(status))
                {
                    // Shift the data down to its new position.
                    RtlMoveMemory(outputKey->KeyData,
                                  rawKey->Data,
                                  sizeof(DVD_DISK_KEY_DESCRIPTOR));

                    RtlCopyMemory(outputKey,
                                  keyHeader,
                                  sizeof(DVD_COPY_PROTECT_KEY));

                    outputKey->KeyLength = DVD_DISK_KEY_LENGTH;

                    *DataLength = DVD_DISK_KEY_LENGTH;
                }
                else
                {
                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                                "StartSessionReadKey Failed with status %x, %xI64 (%x) bytes\n",
                                status,
                                (unsigned int)*DataLength,
                                ((rawKey->Length[0] << 16) | rawKey->Length[1]) ));
                }

                FREE_POOL(keyHeader);
            }

            // special process finished. return from here.
            return status;
        }

        if (NT_SUCCESS(status))
        {
            status = RtlULongSub((ULONG)OutputBufferLength,
                                 (ULONG)sizeof(DVD_COPY_PROTECT_KEY), &result);
        }

        if (NT_SUCCESS(status))
        {
            status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
        }

        if (NT_SUCCESS(status))
        {
            //The data length field of REPORT KEY Command occupies two bytes
            keyLength = min(keyLength, MAXUSHORT);
        }
    }
    else    //IOCTL_DVD_START_SESSION
    {
        keyParameters = NULL;
        keyLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA);
        status = STATUS_SUCCESS;
    }

    if (NT_SUCCESS(status))
    {
        CDB cdb;

        allocationLength = keyLength;

        // Defensive coding. Prefix cannot recognize this usage.
        UNREFERENCED_PARAMETER(allocationLength);

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
        cdb.REPORT_KEY.AllocationLength[0] = fourByte->Byte1;
        cdb.REPORT_KEY.AllocationLength[1] = fourByte->Byte0;

        // set the specific parameters....
        if(IoControlCode == IOCTL_DVD_READ_KEY)
        {
            if(keyParameters->KeyType == DvdTitleKey)
            {
                ULONG logicalBlockAddress;

                logicalBlockAddress = (ULONG)(keyParameters->Parameters.TitleOffset.QuadPart >>
                                              DeviceExtension->SectorShift);

                fourByte = (PFOUR_BYTE)&(logicalBlockAddress);

                cdb.REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3;
                cdb.REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2;
                cdb.REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1;
                cdb.REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0;
            }

            cdb.REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType;
            cdb.REPORT_KEY.AGID = (UCHAR)keyParameters->SessionId;
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                        "DvdStartSessionReadKey => sending irp %p (%s)\n",
                        OriginalRequest, "READ_KEY"));
        }
        else
        {
            cdb.REPORT_KEY.KeyFormat = DVD_REPORT_AGID;
            cdb.REPORT_KEY.AGID = 0;
            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                        "DvdStartSessionReadKey => sending irp %p (%s)\n",
                        OriginalRequest, "START_SESSION"));
        }

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, TRUE, &cdb, 12);

        if (NT_SUCCESS(status))
        {
            if(IoControlCode == IOCTL_DVD_READ_KEY)
            {
                NTSTATUS                tempStatus;
                PDVD_COPY_PROTECT_KEY   copyProtectKey = (PDVD_COPY_PROTECT_KEY)OutputBuffer;
                PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
                ULONG                   dataLength;
                ULONG                   transferLength;

                tempStatus = RtlULongSub(DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
                                         FIELD_OFFSET(CDVD_KEY_HEADER, Data),
                                         &transferLength);

                dataLength = (keyHeader->DataLength[0] << 8) + keyHeader->DataLength[1];

                if (NT_SUCCESS(tempStatus) && (dataLength >= 2))
                {
                    // Adjust the data length to ignore the two reserved bytes in the
                    // header.
                    dataLength -= 2;

                    // take the minimum of the transferred length and the
                    // length as specified in the header.
                    if(dataLength < transferLength)
                    {
                        transferLength = dataLength;
                    }

                    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                                "DvdDeviceControlCompletion: [%p] - READ_KEY with "
                                "transfer length of (%d or %d) bytes\n",
                                OriginalRequest,
                                dataLength,
                                DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - 2));

                    // Copy the key data into the return buffer
                    if(copyProtectKey->KeyType == DvdTitleKey)
                    {
                        RtlMoveMemory(copyProtectKey->KeyData,
                                      keyHeader->Data + 1,
                                      transferLength - 1);

                        copyProtectKey->KeyData[transferLength - 1] = 0;

                        // If this is a title key then we need to copy the CGMS flags
                        // as well.
                        copyProtectKey->KeyFlags = *(keyHeader->Data);

                    }
                    else
                    {
                        RtlMoveMemory(copyProtectKey->KeyData,
                                      keyHeader->Data,
                                      transferLength);
                    }

                    copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY);
                    copyProtectKey->KeyLength += transferLength;

                    *DataLength = copyProtectKey->KeyLength;
                }
                else
                {
                    //There is no valid data from drive.
                    //This may happen when Key Format = 0x3f that does not require data back from drive.
                    status = STATUS_SUCCESS;
                    *DataLength = 0;
                }
            }
            else
            {
                PDVD_SESSION_ID         sessionId = (PDVD_SESSION_ID)OutputBuffer;
                PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
                PCDVD_REPORT_AGID_DATA  keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data;

                *sessionId = keyData->AGID;

                *DataLength = sizeof(DVD_SESSION_ID);
            }
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleDvdSendKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_DVD_SEND_KEY
                     IOCTL_DVD_SEND_KEY2

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PVOID                   inputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           &inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = DvdSendKey(DeviceExtension,
                            Request,
                            inputBuffer,
                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                            DataLength);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DvdSendKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_opt_  WDFREQUEST           OriginalRequest,
    _In_  PVOID                    InputBuffer,
    _In_  size_t                   InputBufferLength,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   base function to handle request of IOCTL_DVD_SEND_KEY(2)
   NOTE: cdrom does not process this IOCTL if the input buffer length is bigger than Port transfer length.

Arguments:

    DeviceExtension - device context
    OriginalRequest - original request to be handled
    InputBuffer - input buffer
    InputBufferLength - length of input buffer

Return Value:

    NTSTATUS
    DataLength - returned data length

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PDVD_COPY_PROTECT_KEY   key = (PDVD_COPY_PROTECT_KEY)InputBuffer;

    ULONG                   keyLength = 0;
    ULONG                   result = 0;
    PFOUR_BYTE              fourByte;

    PAGED_CODE ();

    UNREFERENCED_PARAMETER(InputBufferLength);

    *DataLength = 0;

    if (NT_SUCCESS(status))
    {
        if ((key->KeyLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
            ((key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) > DeviceExtension->ScratchContext.ScratchBufferSize))
        {
            NT_ASSERT(FALSE);
            status = STATUS_INTERNAL_ERROR;
        }
    }

    if (NT_SUCCESS(status))
    {
        status = RtlULongSub(key->KeyLength, sizeof(DVD_COPY_PROTECT_KEY), &result);
    }

    if (NT_SUCCESS(status))
    {
        status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
    }

    if (NT_SUCCESS(status))
    {
        keyLength = min(keyLength, DeviceExtension->ScratchContext.ScratchBufferSize);

        if (keyLength < 2)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (NT_SUCCESS(status))
    {
        PCDVD_KEY_HEADER            keyBuffer = NULL;
        CDB                         cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        // prepare the input buffer
        keyBuffer = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer;

        // keylength is decremented here by two because the
        // datalength does not include the header, which is two
        // bytes.  keylength is immediately incremented later
        // by the same amount.
        keyLength -= 2;
        fourByte = (PFOUR_BYTE)&keyLength;
        keyBuffer->DataLength[0] = fourByte->Byte1;
        keyBuffer->DataLength[1] = fourByte->Byte0;
        keyLength += 2;

        RtlMoveMemory(keyBuffer->Data,
                      key->KeyData,
                      key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY));

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_SEND_KEY;

        cdb.SEND_KEY.ParameterListLength[0] = fourByte->Byte1;
        cdb.SEND_KEY.ParameterListLength[1] = fourByte->Byte0;
        cdb.SEND_KEY.KeyFormat = (UCHAR)key->KeyType;
        cdb.SEND_KEY.AGID = (UCHAR)key->SessionId;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, FALSE, &cdb, 12);

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}


_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleSetReadAhead(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_STORAGE_SET_READ_AHEAD

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    PSTORAGE_SET_READ_AHEAD readAhead = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&readAhead,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG       transferSize = 0;
        CDB         cdb;
        ULONG       blockAddress;
        PFOUR_BYTE  fourByte = (PFOUR_BYTE)&blockAddress;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD;

        blockAddress = (ULONG)(readAhead->TriggerAddress.QuadPart >>
                               DeviceExtension->SectorShift);

        // Defensive coding. Prefix cannot recognize this usage.
        UNREFERENCED_PARAMETER(blockAddress);

        cdb.SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3;
        cdb.SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2;
        cdb.SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1;
        cdb.SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0;

        blockAddress = (ULONG)(readAhead->TargetAddress.QuadPart >>
                               DeviceExtension->SectorShift);

        cdb.SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3;
        cdb.SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2;
        cdb.SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1;
        cdb.SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0;

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);

        if (NT_SUCCESS(status))
        {
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleSetSpeed(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_  WDFREQUEST               Request,
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:

   Handle request of IOCTL_CDROM_SET_SPEED

Arguments:

    DeviceExtension - device context
    Request - request to be handled
    RequestParameters - request parameter
    DataLength - transferred data length

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
    PCDROM_SET_SPEED    inputBuffer = NULL;

    PAGED_CODE ();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&inputBuffer,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG               transferSize = 0;
        CDB                 cdb;
        CDROM_SPEED_REQUEST requestType = inputBuffer->RequestType;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        if (requestType == CdromSetSpeed)
        {
            PCDROM_SET_SPEED speed = inputBuffer;

            cdb.SET_CD_SPEED.OperationCode = SCSIOP_SET_CD_SPEED;
            cdb.SET_CD_SPEED.RotationControl = speed->RotationControl;
            REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.ReadSpeed, &speed->ReadSpeed);
            REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.WriteSpeed, &speed->WriteSpeed);
        }
        else
        {
            PCDROM_SET_STREAMING    stream = (PCDROM_SET_STREAMING)inputBuffer;
            PPERFORMANCE_DESCRIPTOR perfDescriptor;

            transferSize = sizeof(PERFORMANCE_DESCRIPTOR);

            perfDescriptor = DeviceExtension->ScratchContext.ScratchBuffer;
            RtlZeroMemory(perfDescriptor, transferSize);

            perfDescriptor->RandomAccess = stream->RandomAccess;
            perfDescriptor->Exact = stream->SetExact;
            perfDescriptor->RestoreDefaults = stream->RestoreDefaults;
            perfDescriptor->WriteRotationControl = stream->RotationControl;

            REVERSE_BYTES(&perfDescriptor->StartLba,  &stream->StartLba);
            REVERSE_BYTES(&perfDescriptor->EndLba,    &stream->EndLba);
            REVERSE_BYTES(&perfDescriptor->ReadSize,  &stream->ReadSize);
            REVERSE_BYTES(&perfDescriptor->ReadTime,  &stream->ReadTime);
            REVERSE_BYTES(&perfDescriptor->WriteSize, &stream->WriteSize);
            REVERSE_BYTES(&perfDescriptor->WriteTime, &stream->WriteTime);

            cdb.SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
            REVERSE_BYTES_SHORT(&cdb.SET_STREAMING.ParameterListLength, &transferSize);

            // set value in extension by user inputs.
            cdData->RestoreDefaults = stream->Persistent ? FALSE : TRUE;

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceHandleSetSpeed: Restore default speed on media change set to %s\n",
                       cdData->RestoreDefaults ? "true" : "false"));
        }

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);

        if (NT_SUCCESS(status))
        {
            *DataLength = 0;
        }

        // nothing to do after the command finishes.
        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}