Sample Code

Windows Driver Samples/ Disk Class Driver/ C++/ src/ disk.c/

/*++

Copyright (C) Microsoft Corporation, 1991 - 2010

Module Name:

    disk.c

Abstract:

    SCSI disk class driver

Environment:

    kernel mode only

Notes:

Revision History:

--*/

#define DEBUG_MAIN_SOURCE   1
#include "disk.h"


//
// Now instantiate the GUIDs
//

#include "initguid.h"
#include "ntddstor.h"
#include "ntddvol.h"
#include "ioevent.h"

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

#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, DiskUnload)
#pragma alloc_text(PAGE, DiskCreateFdo)
#pragma alloc_text(PAGE, DiskDetermineMediaTypes)
#pragma alloc_text(PAGE, DiskModeSelect)
#pragma alloc_text(PAGE, DisableWriteCache)
#pragma alloc_text(PAGE, DiskSetSpecialHacks)
#pragma alloc_text(PAGE, DiskGetCacheInformation)
#pragma alloc_text(PAGE, DiskSetCacheInformation)
#pragma alloc_text(PAGE, DiskLogCacheInformation)
#pragma alloc_text(PAGE, DiskSetInfoExceptionInformation)
#pragma alloc_text(PAGE, DiskGetInfoExceptionInformation)
#pragma alloc_text(PAGE, DiskIoctlGetCacheSetting)
#pragma alloc_text(PAGE, DiskIoctlSetCacheSetting)
#pragma alloc_text(PAGE, DiskIoctlGetLengthInfo)
#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometry)
#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometryEx)
#pragma alloc_text(PAGE, DiskIoctlGetCacheInformation)
#pragma alloc_text(PAGE, DiskIoctlSetCacheInformation)
#pragma alloc_text(PAGE, DiskIoctlGetMediaTypesEx)
#pragma alloc_text(PAGE, DiskIoctlPredictFailure)
#pragma alloc_text(PAGE, DiskIoctlEnableFailurePrediction)
#pragma alloc_text(PAGE, DiskIoctlReassignBlocks)
#pragma alloc_text(PAGE, DiskIoctlReassignBlocksEx)
#pragma alloc_text(PAGE, DiskIoctlIsWritable)
#pragma alloc_text(PAGE, DiskIoctlUpdateDriveSize)
#pragma alloc_text(PAGE, DiskIoctlGetVolumeDiskExtents)
#pragma alloc_text(PAGE, DiskIoctlSmartGetVersion)
#pragma alloc_text(PAGE, DiskIoctlSmartReceiveDriveData)
#pragma alloc_text(PAGE, DiskIoctlSmartSendDriveCommand)
#pragma alloc_text(PAGE, DiskIoctlVerifyThread)

#endif

//
//  ETW related globals
//
BOOLEAN DiskETWEnabled = FALSE;

BOOLEAN DiskIsPastReinit = FALSE;

const GUID GUID_NULL = { 0 };
#define DiskCompareGuid(_First,_Second) \
    (memcmp ((_First),(_Second), sizeof (GUID)))

//
// This macro is used to work around a bug in the definition of
// DISK_CACHE_RETENTION_PRIORITY.  The value KeepReadData should be
// assigned 0xf rather than 0x2.  Since the interface was already published
// when this was discovered the disk driver has been modified to translate
// between the interface value and the correct scsi value.
//
// 0x2 is turned into 0xf
// 0xf is turned into 0x2 - this ensures that future SCSI defintions can be
//                          accomodated.
//

#define TRANSLATE_RETENTION_PRIORITY(_x)\
        ((_x) == 0xf ?  0x2 :           \
            ((_x) == 0x2 ? 0xf : _x)    \
        )

#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN  CTL_CODE(IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_READ_ACCESS)

VOID
DiskDriverReinit(
    IN PDRIVER_OBJECT DriverObject,
    IN PVOID Nothing,
    IN ULONG Count
    )
{
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(Nothing);
    UNREFERENCED_PARAMETER(Count);

    DiskIsPastReinit = TRUE;
}

VOID
DiskBootDriverReinit(
    IN PDRIVER_OBJECT DriverObject,
    IN PVOID Nothing,
    IN ULONG Count
    )
{
    IoRegisterDriverReinitialization(DriverObject, DiskDriverReinit, NULL);

#if defined(_X86_) || defined(_AMD64_)

    DiskDriverReinitialization(DriverObject, Nothing, Count);

#else

    UNREFERENCED_PARAMETER(Nothing);
    UNREFERENCED_PARAMETER(Count);

#endif

}

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    This routine initializes the SCSI hard disk class driver.

Arguments:

    DriverObject - Pointer to driver object created by system.

    RegistryPath - Pointer to the name of the services node for this driver.

Return Value:

    The function value is the final status from the initialization operation.

--*/

{
    CLASS_INIT_DATA InitializationData = { 0 };
    CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList = { 0 };
    GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
    GUID guidSrbSupport = GUID_CLASSPNP_SRB_SUPPORT;
    ULONG srbSupport;

    NTSTATUS status;

    //
    // Initializes tracing
    //
    WPP_INIT_TRACING(DriverObject, RegistryPath);

#if defined(_X86_) || defined(_AMD64_)

    //
    // Read the information NtDetect squirreled away about the disks in this
    // system.
    //

    DiskSaveDetectInfo(DriverObject);

#endif

    InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);

    //
    // Setup sizes and entry points for functional device objects
    //

    InitializationData.FdoData.DeviceExtensionSize   = FUNCTIONAL_EXTENSION_SIZE;
    InitializationData.FdoData.DeviceType            = FILE_DEVICE_DISK;
    InitializationData.FdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN;

    InitializationData.FdoData.ClassInitDevice    = DiskInitFdo;
    InitializationData.FdoData.ClassStartDevice   = DiskStartFdo;
    InitializationData.FdoData.ClassStopDevice    = DiskStopDevice;
    InitializationData.FdoData.ClassRemoveDevice  = DiskRemoveDevice;
    InitializationData.FdoData.ClassPowerDevice   = ClassSpinDownPowerHandler;

    InitializationData.FdoData.ClassError         = DiskFdoProcessError;
    InitializationData.FdoData.ClassReadWriteVerification = DiskReadWriteVerification;
    InitializationData.FdoData.ClassDeviceControl = DiskDeviceControl;
    InitializationData.FdoData.ClassShutdownFlush = DiskShutdownFlush;
    InitializationData.FdoData.ClassCreateClose   = NULL;


    InitializationData.FdoData.ClassWmiInfo.GuidCount               = 7;
    InitializationData.FdoData.ClassWmiInfo.GuidRegInfo             = DiskWmiFdoGuidList;
    InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo    = DiskFdoQueryWmiRegInfo;
    InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock  = DiskFdoQueryWmiDataBlock;
    InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock    = DiskFdoSetWmiDataBlock;
    InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem     = DiskFdoSetWmiDataItem;
    InitializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod   = DiskFdoExecuteWmiMethod;
    InitializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl;

    InitializationData.ClassAddDevice = DiskAddDevice;
    InitializationData.ClassUnload = DiskUnload;

    //
    // Initialize regregistration data structures
    //

    DiskInitializeReregistration();

    //
    // Call the class init routine
    //

    status = ClassInitialize(DriverObject, RegistryPath, &InitializationData);

    if (NT_SUCCESS(status)) {

        IoRegisterBootDriverReinitialization(DriverObject,
                                             DiskBootDriverReinit,
                                             NULL);
    }

    //
    // Call class init Ex routine to register a
    // PCLASS_QUERY_WMI_REGINFO_EX routine
    //

    classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST);
    classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx;

    (VOID)ClassInitializeEx(DriverObject,
                            &guidQueryRegInfoEx,
                            &classQueryWmiRegInfoExList);

    //
    // Call class init Ex routine to register SRB support
    //
    srbSupport = CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK;
    if (!NT_SUCCESS(ClassInitializeEx(DriverObject,
                                      &guidSrbSupport,
                                      &srbSupport))) {
        //
        // Should not fail
        //
        NT_ASSERT(FALSE);
    }


    return status;

} // end DriverEntry()


VOID
DiskUnload(
    IN PDRIVER_OBJECT DriverObject
    )
{
    PAGED_CODE();

#if defined(_X86_) || defined(_AMD64_)
    DiskCleanupDetectInfo(DriverObject);
#else
    // NB: Need to use UNREFERENCED_PARAMETER to prevent build error
    //     DriverObject is not referenced below in WPP_CLEANUP.
    //     WPP_CLEANUP is currently an "NOOP" marco.
    UNREFERENCED_PARAMETER(DriverObject);
#endif


    //
    // Cleans up tracing
    //
    WPP_CLEANUP(DriverObject);

    return;
}


NTSTATUS
DiskCreateFdo(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN PULONG DeviceCount,
    IN BOOLEAN DasdAccessOnly
    )

/*++

Routine Description:

    This routine creates an object for the functional device

Arguments:

    DriverObject - Pointer to driver object created by system.

    PhysicalDeviceObject - Lower level driver we should attach to

    DeviceCount  - Number of previously installed devices.

    DasdAccessOnly - indicates whether or not a file system is allowed to mount
                     on this device object.  Used to avoid double-mounting of
                     file systems on super-floppies (which can unfortunately be
                     fixed disks).  If set the i/o system will only allow rawfs
                     to be mounted.

Return Value:

    NTSTATUS

--*/

{
    PCCHAR deviceName = NULL;
    HANDLE handle = NULL;
    PDEVICE_OBJECT lowerDevice  = NULL;
    PDEVICE_OBJECT deviceObject = NULL;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
    NTSTATUS status;

    PAGED_CODE();

    *DeviceCount = 0;

    //
    // Set up an object directory to contain the objects for this
    // device and all its partitions.
    //

    do {

        WCHAR dirBuffer[64] = { 0 };
        UNICODE_STRING dirName;
        OBJECT_ATTRIBUTES objectAttribs;

        status = RtlStringCchPrintfW(dirBuffer, sizeof(dirBuffer) / sizeof(dirBuffer[0]) - 1, L"\\Device\\Harddisk%d", *DeviceCount);
        if (!NT_SUCCESS(status)) {
            TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Format symbolic link failed with error: 0x%X\n", status));
            return status;
        }

        RtlInitUnicodeString(&dirName, dirBuffer);

        InitializeObjectAttributes(&objectAttribs,
                                   &dirName,
                                   OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE,
                                   NULL,
                                   NULL);

        status = ZwCreateDirectoryObject(&handle,
                                         DIRECTORY_ALL_ACCESS,
                                         &objectAttribs);

        (*DeviceCount)++;

    } while((status == STATUS_OBJECT_NAME_COLLISION) ||
            (status == STATUS_OBJECT_NAME_EXISTS));

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Could not create directory - %lx\n", status));

        return(status);
    }

    //
    // When this loop exits the count is inflated by one - fix that.
    //

    (*DeviceCount)--;

    //
    // Claim the device.
    //

    lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);

    status = ClassClaimDevice(lowerDevice, FALSE);

    if (!NT_SUCCESS(status)) {
        ZwMakeTemporaryObject(handle);
        ZwClose(handle);
        ObDereferenceObject(lowerDevice);
        return status;
    }

    //
    // Create a device object for this device. Each physical disk will
    // have at least one device object. The required device object
    // describes the entire device. Its directory path is
    // \Device\HarddiskN\Partition0, where N = device number.
    //

    status = DiskGenerateDeviceName(*DeviceCount, &deviceName);

    if(!NT_SUCCESS(status)) {
        TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo - couldn't create name %lx\n", status));

        goto DiskCreateFdoExit;

    }

    status = ClassCreateDeviceObject(DriverObject,
                                     deviceName,
                                     PhysicalDeviceObject,
                                     TRUE,
                                     &deviceObject);

    if (!NT_SUCCESS(status)) {
        TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Can not create device object %s\n", deviceName));
        goto DiskCreateFdoExit;
    }

    FREE_POOL(deviceName);

    //
    // Indicate that IRPs should include MDLs for data transfers.
    //

    SET_FLAG(deviceObject->Flags, DO_DIRECT_IO);

    fdoExtension = deviceObject->DeviceExtension;

    if(DasdAccessOnly) {

        //
        // Inidicate that only RAW should be allowed to mount on the root
        // partition object.  This ensures that a file system can't doubly
        // mount on a super-floppy by mounting once on P0 and once on P1.
        //

#pragma prefast(suppress:28175);
        SET_FLAG(deviceObject->Vpb->Flags, VPB_RAW_MOUNT);
    }

    //
    // Initialize lock count to zero. The lock count is used to
    // disable the ejection mechanism on devices that support
    // removable media. Only the lock count in the physical
    // device extension is used.
    //

    fdoExtension->LockCount = 0;

    //
    // Save system disk number.
    //

    fdoExtension->DeviceNumber = *DeviceCount;

    //
    // Set the alignment requirements for the device based on the
    // host adapter requirements
    //

    if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
        deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
    }

    //
    // Finally, attach to the pdo
    //

    fdoExtension->LowerPdo = PhysicalDeviceObject;

    fdoExtension->CommonExtension.LowerDeviceObject =
        IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);


    if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {

        //
        // Uh - oh, we couldn't attach
        // cleanup and return
        //

        status = STATUS_UNSUCCESSFUL;
        goto DiskCreateFdoExit;
    }

    //
    // Clear the init flag.
    //

    CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);

    //
    // Store a handle to the device object directory for this disk
    //

    fdoExtension->DeviceDirectory = handle;

    ObDereferenceObject(lowerDevice);

    return STATUS_SUCCESS;

DiskCreateFdoExit:

    if (deviceObject != NULL)
    {
        IoDeleteDevice(deviceObject);
    }

    FREE_POOL(deviceName);

    ObDereferenceObject(lowerDevice);

    ZwMakeTemporaryObject(handle);
    ZwClose(handle);

    return status;
}


NTSTATUS
DiskReadWriteVerification(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    I/O System entry for read and write requests to SCSI disks.

Arguments:

    DeviceObject - Pointer to driver object created by system.
    Irp - IRP involved.

Return Value:

    NT Status

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    ULONG residualBytes;
    NTSTATUS status = STATUS_SUCCESS;

    //
    // Make sure that the request is within the bounds of the  partition and
    // that the number of bytes to transfer is a multiple of the sector size
    //

    residualBytes = irpSp->Parameters.Read.Length & (commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);

    if ((irpSp->Parameters.Read.ByteOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
        (irpSp->Parameters.Read.ByteOffset.QuadPart < 0) ||
        (residualBytes != 0))
    {
        status = STATUS_INVALID_PARAMETER;
    }
    else
    {
        ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - irpSp->Parameters.Read.ByteOffset.QuadPart;

        if ((ULONGLONG)irpSp->Parameters.Read.Length > bytesRemaining)
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    if (!NT_SUCCESS(status))
    {
        //
        // This error may be caused by the fact that the drive is not ready.
        //

        status = ((PDISK_DATA) commonExtension->DriverData)->ReadyStatus;

        if (!NT_SUCCESS(status)) {

            //
            // Flag this as a user error so that a popup is generated.
            //

            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "DiskReadWriteVerification: ReadyStatus is %lx\n", status));

            if (IoIsErrorUserInduced(status) && Irp->Tail.Overlay.Thread != NULL) {
                IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
            }

            //
            // status will keep the current error
            //

        } else if (residualBytes == 0) {

            //
            // This failed because we think the physical disk is too small.
            // Send it down to the drive and let the hardware decide for
            // itself.
            //

            status = STATUS_SUCCESS;

        } else {

            //
            // Note fastfat depends on this parameter to determine when to
            // remount due to a sector size change.
            //

            status = STATUS_INVALID_PARAMETER;
        }
    }

    Irp->IoStatus.Status = status;

    return status;

} // end DiskReadWrite()


NTSTATUS
DiskDetermineMediaTypes(
    IN PDEVICE_OBJECT Fdo,
    IN PIRP     Irp,
    IN UCHAR    MediumType,
    IN UCHAR    DensityCode,
    IN BOOLEAN  MediaPresent,
    IN BOOLEAN  IsWritable
    )

/*++

Routine Description:

    Determines number of types based on the physical device, validates the user buffer
    and builds the MEDIA_TYPE information.

Arguments:

    DeviceObject - Pointer to functional device object created by system.
    Irp - IOCTL_STORAGE_GET_MEDIA_TYPES_EX Irp.
    MediumType - byte returned in mode data header.
    DensityCode - byte returned in mode data block descriptor.
    NumberOfTypes - pointer to be updated based on actual device.

Return Value:

    Status is returned.

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

    PGET_MEDIA_TYPES  mediaTypes = Irp->AssociatedIrp.SystemBuffer;
    PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0];
    BOOLEAN deviceMatched = FALSE;

    PAGED_CODE();

    //
    // this should be checked prior to calling into this routine
    // as we use the buffer as mediaTypes
    //

    NT_ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
           sizeof(GET_MEDIA_TYPES));

    //
    // Determine if this device is removable or fixed.
    //

    if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {

        //
        // Fixed disk.
        //

        mediaTypes->DeviceType = FILE_DEVICE_DISK;
        mediaTypes->MediaInfoCount = 1;

        mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart   = fdoExtension->DiskGeometry.Cylinders.QuadPart;
        mediaInfo->DeviceSpecific.DiskInfo.MediaType            = FixedMedia;
        mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder    = fdoExtension->DiskGeometry.TracksPerCylinder;
        mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack      = fdoExtension->DiskGeometry.SectorsPerTrack;
        mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector       = fdoExtension->DiskGeometry.BytesPerSector;
        mediaInfo->DeviceSpecific.DiskInfo.NumberMediaSides     = 1;
        mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE);

        if (!IsWritable) {

            SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics,
                     MEDIA_WRITE_PROTECTED);
        }

    } else {

        PCCHAR vendorId = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset;
        PCCHAR productId = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductIdOffset;
        PCCHAR productRevision = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductRevisionOffset;
        DISK_MEDIA_TYPES_LIST const *mediaListEntry;
        ULONG  currentMedia;
        ULONG  i;
        ULONG  j;
        ULONG  sizeNeeded;

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
                   "DiskDetermineMediaTypes: Vendor %s, Product %s\n",
                   vendorId,
                   productId));


        //
        // If there's an entry with such vendorId & ProductId in the DiskMediaTypesExclude list,
        // this device shouldn't be looked up in the DiskMediaTypes list to determine a medium type.
        // The exclude table allows to narrow down the set of devices described by the DiskMediaTypes
        // list (e.g.: DiskMediaTypes says "all HP devices" and DiskMediaTypesExlclude says
        // "except for HP RDX")
        //

        for (i = 0; DiskMediaTypesExclude[i].VendorId != NULL; i++) {
            mediaListEntry = &DiskMediaTypesExclude[i];

            if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
                continue;
            }

            if ((mediaListEntry->ProductId != NULL) &&
                 strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) {
                continue;
            }

            goto SkipTable;
        }

        //
        // Run through the list until we find the entry with a NULL Vendor Id.
        //

        for (i = 0; DiskMediaTypes[i].VendorId != NULL; i++) {

            mediaListEntry = &DiskMediaTypes[i];

            if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
                continue;
            }

            if ((mediaListEntry->ProductId != NULL) &&
                 strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) {
                continue;
            }

            if ((mediaListEntry->Revision != NULL) &&
                 strncmp(mediaListEntry->Revision, productRevision, strlen(mediaListEntry->Revision))) {
                continue;
            }

            deviceMatched = TRUE;

            mediaTypes->DeviceType = FILE_DEVICE_DISK;
            mediaTypes->MediaInfoCount = mediaListEntry->NumberOfTypes;

            //
            // Ensure that buffer is large enough.
            //

            sizeNeeded = FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
                         (mediaListEntry->NumberOfTypes *
                          sizeof(DEVICE_MEDIA_INFO)
                          );

            if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
                sizeNeeded) {

                //
                // Buffer too small
                //

                Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
                return STATUS_BUFFER_TOO_SMALL;
            }

            for (j = 0; j < mediaListEntry->NumberOfTypes; j++) {

                mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
                mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
                mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
                mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
                mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = mediaListEntry->NumberOfSides;

                //
                // Set the type.
                //

                mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = mediaListEntry->MediaTypes[j];

                if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == MO_5_WO) {
                    mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_WRITE_ONCE;
                } else {
                    mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
                }

                //
                // 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.
                //

                if (MediaPresent) {

                    //
                    // The usage of MediumType and DensityCode is device specific, so this may need
                    // to be extended to further key off of product/vendor ids.
                    // Currently, the MO units are the only devices that return this information.
                    //

                    if (MediumType == 2) {
                        currentMedia = MO_5_WO;
                    } else if (MediumType == 3) {
                        currentMedia = MO_5_RW;

                        if (DensityCode == 0x87) {

                            //
                            // Indicate that the pinnacle 4.6 G media
                            // is present. Other density codes will default to normal
                            // RW MO media.
                            //

                            currentMedia = PINNACLE_APEX_5_RW;
                        }
                    } else {
                        currentMedia = 0;
                    }

                    if (currentMedia) {
                        if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == (STORAGE_MEDIA_TYPE)currentMedia) {
                            SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
                        }

                    } else {
                        SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
                    }
                }

                if (!IsWritable) {
                    SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
                }

                //
                // Advance to next entry.
                //

                mediaInfo++;
            }
        }

SkipTable:

        if (!deviceMatched) {

            TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
                       "DiskDetermineMediaTypes: Unknown device. Vendor: %s Product: %s Revision: %s\n",
                                   vendorId,
                                   productId,
                                   productRevision));
            //
            // Build an entry for unknown.
            //

            mediaTypes->DeviceType = FILE_DEVICE_DISK;
            mediaTypes->MediaInfoCount = 1;

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

            if (MediaPresent) {

                SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
            }

            if (!IsWritable) {

                SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
            }
        }
    }

    Irp->IoStatus.Information =
        FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
        (mediaTypes->MediaInfoCount * sizeof(DEVICE_MEDIA_INFO));

    return STATUS_SUCCESS;
}


NTSTATUS
DiskDeviceControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )

/*++

Routine Description:

    I/O system entry for device controls to SCSI disks.

Arguments:

    Fdo - Pointer to functional device object created by system.
    Irp - IRP involved.

Return Value:

    Status is returned.

--*/

{
    PIO_STACK_LOCATION  irpStack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               ioctlCode;

    NT_ASSERT(DeviceObject != NULL);

    Irp->IoStatus.Information = 0;
    ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DiskDeviceControl: Received IOCTL 0x%X for device %p through IRP %p\n",
                ioctlCode, DeviceObject, Irp));


    switch (ioctlCode) {

        case IOCTL_DISK_GET_CACHE_INFORMATION: {
            status = DiskIoctlGetCacheInformation(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_SET_CACHE_INFORMATION: {
            status = DiskIoctlSetCacheInformation(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_GET_CACHE_SETTING: {
            status = DiskIoctlGetCacheSetting(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_SET_CACHE_SETTING: {
            status = DiskIoctlSetCacheSetting(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
            status = DiskIoctlGetDriveGeometry(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: {
            status = DiskIoctlGetDriveGeometryEx( DeviceObject, Irp );
            break;
        }

        case IOCTL_DISK_VERIFY: {
            status = DiskIoctlVerify(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_GET_LENGTH_INFO: {
            status = DiskIoctlGetLengthInfo(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_IS_WRITABLE: {
            status = DiskIoctlIsWritable(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_UPDATE_DRIVE_SIZE: {
            status = DiskIoctlUpdateDriveSize(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_REASSIGN_BLOCKS: {
            status = DiskIoctlReassignBlocks(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_REASSIGN_BLOCKS_EX: {
            status = DiskIoctlReassignBlocksEx(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_INTERNAL_SET_VERIFY: {
            status = DiskIoctlSetVerify(DeviceObject, Irp);
            break;
        }

        case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: {
            status = DiskIoctlClearVerify(DeviceObject, Irp);
            break;
        }

        case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
            status = DiskIoctlGetMediaTypesEx(DeviceObject, Irp);
            break;
        }

        case IOCTL_STORAGE_PREDICT_FAILURE : {
            status = DiskIoctlPredictFailure(DeviceObject, Irp);
            break;
        }

        #if (NTDDI_VERSION >= NTDDI_WINBLUE)
        case IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG : {
            status = DiskIoctlEnableFailurePrediction(DeviceObject, Irp);
            break;
        }
        #endif

        case SMART_GET_VERSION: {
            status = DiskIoctlSmartGetVersion(DeviceObject, Irp);
            break;
        }

        case SMART_RCV_DRIVE_DATA: {
            status = DiskIoctlSmartReceiveDriveData(DeviceObject, Irp);
            break;
        }

        case SMART_SEND_DRIVE_COMMAND: {
            status = DiskIoctlSmartSendDriveCommand(DeviceObject, Irp);
            break;
        }

        case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
        case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN: {
            status = DiskIoctlGetVolumeDiskExtents(DeviceObject, Irp);
            break;
        }

        default: {

            //
            // Pass the request to the common device control routine.
            //
            return(ClassDeviceControl(DeviceObject, Irp));
            break;
        }
    } // end switch

    if (!NT_SUCCESS(status)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskDeviceControl: IOCTL 0x%X to device %p failed with error 0x%X\n",
                    ioctlCode, DeviceObject, status));
        if (IoIsErrorUserInduced(status) &&
            (Irp->Tail.Overlay.Thread != NULL)) {
            IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
        }
    }

    //
    // DiskIoctlVerify() (IOCTL_DISK_VERIFY) function returns STATUS_PENDING
    // and completes the IRP in the work item. Do not touch or complete
    // the IRP if STATUS_PENDING is returned.
    //

    if (status != STATUS_PENDING) {


        Irp->IoStatus.Status = status;
        ClassReleaseRemoveLock(DeviceObject, Irp);
        ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
    }

    return(status);
} // end DiskDeviceControl()

NTSTATUS
DiskShutdownFlush(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine is the handler for shutdown and flush requests. It sends
    down a synch cache command to the device if its cache is enabled.  If
    the request is a  shutdown and the media is removable,  it sends down
    an unlock request

    Finally,  an SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH is sent down
    the stack

Arguments:

    DeviceObject - The device object processing the request
    Irp - The shutdown | flush request being serviced

Return Value:

    STATUS_PENDING if successful, an error code otherwise

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
    PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData;
    PIO_STACK_LOCATION irpStack;
    NTSTATUS status = STATUS_SUCCESS;
    ULONG srbSize;
    PSCSI_REQUEST_BLOCK srb;
    PSTORAGE_REQUEST_BLOCK srbEx = NULL;
    PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
    PCDB cdb;
    KIRQL irql;

    //
    // Flush requests are combined and need to be handled in a special manner
    //

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) {

        if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED)) {

            //
            // We've been assured that both the disk
            // and adapter caches are battery-backed
            //

            Irp->IoStatus.Status = STATUS_SUCCESS;
            ClassReleaseRemoveLock(DeviceObject, Irp);
            ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
            return STATUS_SUCCESS;
        }

        KeAcquireSpinLock(&diskData->FlushContext.Spinlock, &irql);

        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: IRP %p flags = 0x%x\n", Irp, irpStack->Flags));

        //
        // This request will most likely be completed asynchronously
        //
        IoMarkIrpPending(Irp);

        //
        // Look to see if a flush is in progress
        //

        if (diskData->FlushContext.CurrIrp != NULL) {

            //
            // There is an outstanding flush. Queue this
            // request to the group that is next in line
            //

            if (diskData->FlushContext.NextIrp != NULL) {

                #if DBG
                    diskData->FlushContext.DbgTagCount++;
                #endif

                InsertTailList(&diskData->FlushContext.NextList, &Irp->Tail.Overlay.ListEntry);

                KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);

                //
                // This request will be completed by its representative
                //

            } else {

                #if DBG
                    if (diskData->FlushContext.DbgTagCount < 64) {

                        diskData->FlushContext.DbgRefCount[diskData->FlushContext.DbgTagCount]++;
                    }

                    diskData->FlushContext.DbgSavCount += diskData->FlushContext.DbgTagCount;
                    diskData->FlushContext.DbgTagCount  = 0;
                #endif

                diskData->FlushContext.NextIrp = Irp;
                NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));


                KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);


                    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: waiting for event\n"));

                    //
                    // Wait for the outstanding flush to complete
                    //
                    KeWaitForSingleObject(&diskData->FlushContext.Event, Executive, KernelMode, FALSE, NULL);

                    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: event signal\n"));

                    //
                    // Make this group the outstanding one and free up the next slot
                    //

                    KeAcquireSpinLock(&diskData->FlushContext.Spinlock, &irql);

                    NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));

                    while (!IsListEmpty(&diskData->FlushContext.NextList)) {

                        PLIST_ENTRY listEntry = RemoveHeadList(&diskData->FlushContext.NextList);
                        InsertTailList(&diskData->FlushContext.CurrList, listEntry);
                    }

                    NT_ASSERT(diskData->FlushContext.CurrIrp != diskData->FlushContext.NextIrp);
                    diskData->FlushContext.CurrIrp = diskData->FlushContext.NextIrp;
                    diskData->FlushContext.NextIrp = NULL;

                    KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);

                    //
                    // Send this request down to the device
                    //
                    DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
            }

        } else {

            diskData->FlushContext.CurrIrp = Irp;
            NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));

            NT_ASSERT(diskData->FlushContext.NextIrp == NULL);
            NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));


            KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);

                DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
        }

    } else {

        //
        // Allocate SCSI request block.
        //

        if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
            srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
        } else {
            srbSize = sizeof(SCSI_REQUEST_BLOCK);
        }

        srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                    srbSize,
                                    DISK_TAG_SRB);
        if (srb == NULL) {

            //
            // Set the status and complete the request.
            //

            Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
            ClassReleaseRemoveLock(DeviceObject, Irp);
            ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
            return(STATUS_INSUFFICIENT_RESOURCES);
        }

        RtlZeroMemory(srb, srbSize);
        if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {

            srbEx = (PSTORAGE_REQUEST_BLOCK)srb;

            //
            // Set up STORAGE_REQUEST_BLOCK fields
            //

            srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
            srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
            srbEx->Signature = SRB_SIGNATURE;
            srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
            srbEx->SrbLength = srbSize;
            srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
            srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
            srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
            srbEx->NumSrbExData = 1;

            // Set timeout value and mark the request as not being a tagged request.
            srbEx->TimeOutValue = fdoExtension->TimeOutValue * 4;
            srbEx->RequestTag = SP_UNTAGGED;
            srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
            srbEx->SrbFlags = fdoExtension->SrbFlags;

            //
            // Set up address fields
            //

            storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
            storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
            storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

            //
            // Set up SCSI SRB extended data fields
            //

            srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
                sizeof(STOR_ADDR_BTL8);
            if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
                srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
                srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
                srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;

                cdb = (PCDB)srbExDataCdb16->Cdb;
            } else {
                // Should not happen
                NT_ASSERT(FALSE);

                //
                // Set the status and complete the request.
                //

                Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
                ClassReleaseRemoveLock(DeviceObject, Irp);
                ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
                return(STATUS_INTERNAL_ERROR);
            }

        } else {

            //
            // Write length to SRB.
            //

            srb->Length = SCSI_REQUEST_BLOCK_SIZE;

            //
            // Set timeout value and mark the request as not being a tagged request.
            //

            srb->TimeOutValue = fdoExtension->TimeOutValue * 4;
            srb->QueueTag = SP_UNTAGGED;
            srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
            srb->SrbFlags = fdoExtension->SrbFlags;
            srb->Function = SRB_FUNCTION_EXECUTE_SCSI;

            cdb = (PCDB)srb->Cdb;
        }

        //
        // If the write cache is enabled then send a synchronize cache request.
        //

        if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) {

            if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
                srbExDataCdb16->CdbLength = 10;
            } else {
                srb->CdbLength = 10;
            }

            cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;

            status = ClassSendSrbSynchronous(DeviceObject,
                                             srb,
                                             NULL,
                                             0,
                                             TRUE);

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status));
        }

        //
        // Unlock the device if it contains removable media
        //

        if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
        {

            if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {

                //
                // Reinitialize status fields to 0 in case there was a previous request
                //

                srbEx->SrbStatus = 0;
                srbExDataCdb16->ScsiStatus = 0;

                srbExDataCdb16->CdbLength = 6;

                //
                // Set timeout value
                //

                srbEx->TimeOutValue = fdoExtension->TimeOutValue;

            } else {

                //
                // Reinitialize status fields to 0 in case there was a previous request
                //

                srb->SrbStatus = 0;
                srb->ScsiStatus = 0;

                srb->CdbLength = 6;

                //
                // Set timeout value.
                //

                srb->TimeOutValue = fdoExtension->TimeOutValue;
            }

            cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
            cdb->MEDIA_REMOVAL.Prevent = FALSE;

            status = ClassSendSrbSynchronous(DeviceObject,
                                             srb,
                                             NULL,
                                             0,
                                             TRUE);

            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskShutdownFlush: Unlock device request sent. Status = %lx\n", status));
        }

        //
        // Set up a SHUTDOWN SRB
        //

        if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
            srbEx->NumSrbExData = 0;
            srbEx->SrbExDataOffset[0] = 0;
            srbEx->SrbFunction = SRB_FUNCTION_SHUTDOWN;
            srbEx->OriginalRequest = Irp;
            srbEx->SrbLength = CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE;
            srbEx->SrbStatus = 0;
        } else {
            srb->CdbLength = 0;
            srb->Function = SRB_FUNCTION_SHUTDOWN;
            srb->SrbStatus = 0;
            srb->OriginalRequest = Irp;
        }

        //
        // Set the retry count to zero.
        //

        irpStack->Parameters.Others.Argument4 = (PVOID) 0;

        //
        // Set up IoCompletion routine address.
        //

        IoSetCompletionRoutine(Irp, ClassIoComplete, srb, TRUE, TRUE, TRUE);

        //
        // Get next stack location and
        // set major function code.
        //

        irpStack = IoGetNextIrpStackLocation(Irp);

        irpStack->MajorFunction = IRP_MJ_SCSI;

        //
        // Set up SRB for execute scsi request.
        // Save SRB address in next stack for port driver.
        //

        irpStack->Parameters.Scsi.Srb = srb;

        //
        // Call the port driver to process the request.
        //

        IoMarkIrpPending(Irp);
        IoCallDriver(commonExtension->LowerDeviceObject, Irp);
    }

    return STATUS_PENDING;
}


VOID
DiskFlushDispatch(
    IN PDEVICE_OBJECT Fdo,
    IN PDISK_GROUP_CONTEXT FlushContext
    )

/*++

Routine Description:

    This routine is the handler for flush requests. It sends down a synch
    cache command to the device if its cache is enabled. This is followed
    by an SRB_FUNCTION_FLUSH

Arguments:

    Fdo - The device object processing the flush request
    FlushContext - The flush group context

Return Value:

    None

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
    PSCSI_REQUEST_BLOCK srb = &FlushContext->Srb.Srb;
    PSTORAGE_REQUEST_BLOCK srbEx = &FlushContext->Srb.SrbEx;
    PIO_STACK_LOCATION  irpSp = NULL;
    PSTOR_ADDR_BTL8 storAddrBtl8;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;

    //
    // Fill in the srb fields appropriately
    //
    if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        RtlZeroMemory(srbEx, sizeof(FlushContext->Srb.SrbExBuffer));

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = sizeof(FlushContext->Srb.SrbExBuffer);
        srbEx->RequestPriority = IoGetIoPriorityHint(FlushContext->CurrIrp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->TimeOutValue = fdoExt->TimeOutValue * 4;
        srbEx->RequestTag = SP_UNTAGGED;
        srbEx->RequestAttribute  = SRB_SIMPLE_TAG_REQUEST;
        srbEx->SrbFlags = fdoExt->SrbFlags;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

    } else {
        RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);

        srb->Length       = SCSI_REQUEST_BLOCK_SIZE;
        srb->TimeOutValue = fdoExt->TimeOutValue * 4;
        srb->QueueTag     = SP_UNTAGGED;
        srb->QueueAction  = SRB_SIMPLE_TAG_REQUEST;
        srb->SrbFlags     = fdoExt->SrbFlags;
    }

    //
    // If write caching is enabled then send down a synchronize cache request
    //
    if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE))
    {

        if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
            srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
            srbEx->NumSrbExData = 1;

            //
            // Set up SCSI SRB extended data fields
            //

            srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
                sizeof(STOR_ADDR_BTL8);
            if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
                srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
                srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
                srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
                srbExDataCdb16->CdbLength = 10;
                srbExDataCdb16->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
            } else {
                // Should not happen
                NT_ASSERT(FALSE);
                return;
            }

        } else {
            srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
            srb->CdbLength = 10;
            srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
        }

        TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch: sending sync cache\n"));

        ClassSendSrbSynchronous(Fdo, srb, NULL, 0, TRUE);
    }

    //
    // Set up a FLUSH SRB
    //
    if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx->SrbFunction = SRB_FUNCTION_FLUSH;
        srbEx->NumSrbExData = 0;
        srbEx->SrbExDataOffset[0] = 0;
        srbEx->OriginalRequest = FlushContext->CurrIrp;
        srbEx->SrbStatus = 0;

        //
        // Make sure that this srb does not get freed
        //
        SET_FLAG(srbEx->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);

   } else {
        srb->Function  = SRB_FUNCTION_FLUSH;
        srb->CdbLength = 0;
        srb->OriginalRequest = FlushContext->CurrIrp;
        srb->SrbStatus = 0;
        srb->ScsiStatus = 0;

        //
        // Make sure that this srb does not get freed
        //
        SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
    }

    //
    // Make sure that this request does not get retried
    //
    irpSp = IoGetCurrentIrpStackLocation(FlushContext->CurrIrp);

    irpSp->Parameters.Others.Argument4 = (PVOID) 0;

    //
    // Fill in the irp fields appropriately
    //
    irpSp = IoGetNextIrpStackLocation(FlushContext->CurrIrp);

    irpSp->MajorFunction       = IRP_MJ_SCSI;
    irpSp->Parameters.Scsi.Srb = srb;

    IoSetCompletionRoutine(FlushContext->CurrIrp, DiskFlushComplete, NULL, TRUE, TRUE, TRUE);

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch: sending srb flush on irp %p\n", FlushContext->CurrIrp));

    //
    // Send down the flush request
    //
    IoCallDriver(((PCOMMON_DEVICE_EXTENSION)fdoExt)->LowerDeviceObject, FlushContext->CurrIrp);
}



NTSTATUS
DiskFlushComplete(
    IN PDEVICE_OBJECT Fdo,
    IN PIRP Irp,
    IN PVOID Context
    )

/*++

Routine Description:

    This completion routine is a wrapper around ClassIoComplete. It
    will complete all the flush requests that are tagged to it, set
    an event to signal the next group to proceed and return

Arguments:

    Fdo - The device object which requested the completion routine
    Irp - The irp that is being completed
    Context - The flush group context

Return Value:

    STATUS_SUCCESS if successful, an error code otherwise

--*/

{
    PDISK_GROUP_CONTEXT FlushContext;
    NTSTATUS status;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
    PDISK_DATA diskData;

    UNREFERENCED_PARAMETER(Context);

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "DiskFlushComplete: %p %p\n", Fdo, Irp));

    //
    // Get the flush context from the device extension
    //
    fdoExt = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
    diskData = (PDISK_DATA)fdoExt->CommonExtension.DriverData;
    NT_ASSERT(diskData != NULL);
    _Analysis_assume_(diskData != NULL);

    FlushContext = &diskData->FlushContext;

    //
    // Make sure everything is in order
    //
    NT_ASSERT(Irp == FlushContext->CurrIrp);

    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: completing irp %p\n", Irp));
    status = ClassIoComplete(Fdo, Irp, &FlushContext->Srb.Srb);

    //
    // Make sure that ClassIoComplete did not decide to retry this request
    //
    NT_ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);

    //
    // Complete the flush requests tagged to this one
    //

    while (!IsListEmpty(&FlushContext->CurrList)) {

        PLIST_ENTRY listEntry = RemoveHeadList(&FlushContext->CurrList);
        PIRP tempIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);

        InitializeListHead(&tempIrp->Tail.Overlay.ListEntry);
        tempIrp->IoStatus = Irp->IoStatus;

        ClassReleaseRemoveLock(Fdo, tempIrp);
        ClassCompleteRequest(Fdo, tempIrp, IO_NO_INCREMENT);
    }


        //
        // Notify the next group's representative that it may go ahead now
        //
        KeSetEvent(&FlushContext->Event, IO_NO_INCREMENT, FALSE);


    TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: irp %p status = 0x%x\n", Irp, status));

    return status;
}



NTSTATUS
DiskModeSelect(
    IN PDEVICE_OBJECT Fdo,
    _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
    IN ULONG Length,
    IN BOOLEAN SavePage
    )

/*++

Routine Description:

    This routine sends a mode select command.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    ModeSelectBuffer - Supplies a buffer containing the page data.

    Length - Supplies the length in bytes of the mode select buffer.

    SavePage - Indicates that parameters should be written to disk.

Return Value:

    Length of the transferred data is returned.

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
    PCDB cdb;
    SCSI_REQUEST_BLOCK srb = {0};
    ULONG retries = 1;
    ULONG length2;
    NTSTATUS status;
    PULONG buffer;
    PMODE_PARAMETER_BLOCK blockDescriptor;
    UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
    PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
    PSTOR_ADDR_BTL8 storAddrBtl8;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
    PSCSI_REQUEST_BLOCK srbPtr;

    PAGED_CODE();

    //
    // Check whether block length is available
    //

    if (fdoExtension->DiskGeometry.BytesPerSector == 0) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect: Block length is not available. Unable to send mode select\n"));
        NT_ASSERT(fdoExtension->DiskGeometry.BytesPerSector != 0);
        return STATUS_INVALID_PARAMETER;
    }



    length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);

    //
    // Allocate buffer for mode select header, block descriptor, and mode page.
    //

    buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                   length2,
                                   DISK_TAG_MODE_DATA);

    if (buffer == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(buffer, length2);

    //
    // Set length in header to size of mode page.
    //

    ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);

    blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);

    //
    // Set block length from the cached disk geometry
    //

    blockDescriptor->BlockLength[2] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector >> 16);
    blockDescriptor->BlockLength[1] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector >> 8);
    blockDescriptor->BlockLength[0] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector);

    //
    // Copy mode page to buffer.
    //

    RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length);

    //
    // Build the MODE SELECT CDB.
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = sizeof(srbExBuffer);
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoPriorityNormal;
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        // Set timeout value from device extension.
        srbEx->TimeOutValue = fdoExtension->TimeOutValue * 2;

       //
       // Set up address fields
       //

       storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
       storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
       storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

       //
       // Set up SCSI SRB extended data fields
       //

       srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
           sizeof(STOR_ADDR_BTL8);
       if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
           srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
           srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
           srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
           srbExDataCdb16->CdbLength = 6;

           cdb = (PCDB)srbExDataCdb16->Cdb;
       } else {
           // Should not happen
           NT_ASSERT(FALSE);

           FREE_POOL(buffer);
           TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect: Insufficient extended SRB size\n"));
           return STATUS_INTERNAL_ERROR;
       }

       srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;

    } else {

        srb.CdbLength = 6;
        cdb = (PCDB)srb.Cdb;

        //
        // Set timeout value from device extension.
        //

        srb.TimeOutValue = fdoExtension->TimeOutValue * 2;

        srbPtr = &srb;
    }

    cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
    cdb->MODE_SELECT.SPBit = SavePage;
    cdb->MODE_SELECT.PFBit = 1;
    cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);

Retry:

    status = ClassSendSrbSynchronous(Fdo,
                                     srbPtr,
                                     buffer,
                                     length2,
                                     TRUE);

    if (status == STATUS_VERIFY_REQUIRED) {

        //
        // Routine ClassSendSrbSynchronous does not retry requests returned with
        // this status.
        //

        if (retries--) {

            //
            // Retry request.
            //

            goto Retry;
        }

    } else if (SRB_STATUS(srbPtr->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
        status = STATUS_SUCCESS;
    }

    FREE_POOL(buffer);

    return status;
} // end DiskModeSelect()


//
// This routine is structured as a work-item routine
//
VOID
DisableWriteCache(
    IN PDEVICE_OBJECT Fdo,
    IN PVOID Context
    )

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
    DISK_CACHE_INFORMATION cacheInfo = { 0 };
    NTSTATUS status;
    PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;

    PAGED_CODE();

    NT_ASSERT(WorkItem != NULL);
    _Analysis_assume_(WorkItem != NULL);
    
    status = DiskGetCacheInformation(fdoExtension, &cacheInfo);

    if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) {

        cacheInfo.WriteCacheEnabled = FALSE;

        DiskSetCacheInformation(fdoExtension, &cacheInfo);
    }

    IoFreeWorkItem(WorkItem);
}


//
// This routine is structured as a work-item routine
//
VOID
DiskIoctlVerifyThread(
    IN PDEVICE_OBJECT Fdo,
    IN PVOID Context
    )
{
    PDISK_VERIFY_WORKITEM_CONTEXT WorkContext = (PDISK_VERIFY_WORKITEM_CONTEXT)Context;
    PIRP Irp = NULL;
    PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
    PDISK_DATA DiskData = (PDISK_DATA)FdoExtension->CommonExtension.DriverData;
    PVERIFY_INFORMATION verifyInfo = NULL;
    PSCSI_REQUEST_BLOCK Srb = NULL;
    PCDB Cdb = NULL;
    LARGE_INTEGER byteOffset;
    LARGE_INTEGER sectorOffset;
    ULONG sectorCount;
    NTSTATUS status = STATUS_SUCCESS;
    PSTORAGE_REQUEST_BLOCK srbEx = NULL;
    PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;

    PAGED_CODE();

    NT_ASSERT(WorkContext != NULL);
    _Analysis_assume_(WorkContext != NULL);

    Srb = WorkContext->Srb;
    Irp = WorkContext->Irp;
    verifyInfo = (PVERIFY_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

    //
    // We don't need to hold on to this memory as
    // the following operation may take some time
    //

    IoFreeWorkItem(WorkContext->WorkItem);

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerifyThread: Spliting up the request\n"));

    //
    // Add disk offset to starting the sector
    //

    byteOffset.QuadPart = FdoExtension->CommonExtension.StartingOffset.QuadPart +
                          verifyInfo->StartingOffset.QuadPart;

    //
    // Convert byte offset to the sector offset
    //

    sectorOffset.QuadPart = byteOffset.QuadPart >> FdoExtension->SectorShift;

    //
    // Convert byte count to sector count.
    //

    sectorCount = verifyInfo->Length >> FdoExtension->SectorShift;

    //
    // Make sure  that all previous verify requests have indeed completed
    // This greatly reduces the possibility of a Denial-of-Service attack
    //

    KeWaitForMutexObject(&DiskData->VerifyMutex,
                         Executive,
                         KernelMode,
                         FALSE,
                         NULL);

    //
    // Initialize SCSI SRB for a verify CDB
    //
    if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        RtlZeroMemory(Srb, CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE);
        srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

        //
        // Set up SCSI SRB extended data fields
        //

        srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
               sizeof(STOR_ADDR_BTL8);
        if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
            srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
            srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
            srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;

            Cdb = (PCDB)srbExDataCdb16->Cdb;
            if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
                srbExDataCdb16->CdbLength = 16;
                Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
            } else {
                srbExDataCdb16->CdbLength = 10;
                Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
            }
        } else {
            // Should not happen
            NT_ASSERT(FALSE);

            FREE_POOL(Srb);
            FREE_POOL(WorkContext);
            status = STATUS_INTERNAL_ERROR;
        }

    } else {
        RtlZeroMemory(Srb, SCSI_REQUEST_BLOCK_SIZE);

        Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
        Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;

        Cdb = (PCDB)Srb->Cdb;
        if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
            Srb->CdbLength = 16;
            Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
        } else {
            Srb->CdbLength = 10;
            Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
        }

    }

    while (NT_SUCCESS(status) && (sectorCount != 0)) {

        USHORT numSectors = (USHORT) min(sectorCount, MAX_SECTORS_PER_VERIFY);

        if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {

            //
            // Reset status fields
            //

            srbEx->SrbStatus = 0;
            srbExDataCdb16->ScsiStatus = 0;

            //
            // Calculate the request timeout value based
            // on  the number of sectors  being verified
            //

            srbEx->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue;
        } else {

            //
            // Reset status fields
            //

            Srb->SrbStatus = 0;
            Srb->ScsiStatus = 0;

            //
            // Calculate the request timeout value based
            // on  the number of sectors  being verified
            //

            Srb->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue;
        }

        //
        // Update verify CDB info.
        // NOTE - CDB opcode and length has been initialized prior to entering
        // the while loop
        //

        if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {

            REVERSE_BYTES_QUAD(&Cdb->CDB16.LogicalBlock, &sectorOffset);
            REVERSE_BYTES_SHORT(&Cdb->CDB16.TransferLength[2], &numSectors);
        } else {

            //
            // 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)&numSectors)->Byte1;
            Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numSectors)->Byte0;
        }

        status = ClassSendSrbSynchronous(Fdo,
                                         Srb,
                                         NULL,
                                         0,
                                         FALSE);

        NT_ASSERT(status != STATUS_NONEXISTENT_SECTOR);

        sectorCount  -= numSectors;
        sectorOffset.QuadPart += numSectors;
    }

    KeReleaseMutex(&DiskData->VerifyMutex, FALSE);

    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;

    ClassReleaseRemoveLock(Fdo, Irp);
    ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);

    FREE_POOL(Srb);
    FREE_POOL(WorkContext);
}


VOID
DiskFdoProcessError(
    PDEVICE_OBJECT Fdo,
    PSCSI_REQUEST_BLOCK Srb,
    NTSTATUS *Status,
    BOOLEAN *Retry
    )

/*++

Routine Description:

   This routine checks the type of error.  If the error indicates an underrun
   then indicate the request should be retried.

Arguments:

    Fdo - Supplies a pointer to the functional device object.

    Srb - Supplies a pointer to the failing Srb.

    Status - Status with which the IRP will be completed.

    Retry - Indication of whether the request will be retried.

Return Value:

    None.

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
    PSTORAGE_REQUEST_BLOCK srbEx;
    PCDB cdb = NULL;
    UCHAR scsiStatus = 0;
    UCHAR senseBufferLength = 0;
    PVOID senseBuffer = NULL;
    CDB noOp = {0};

    //
    // Get relevant fields from SRB
    //
    if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {

        srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;

        //
        // Look for SCSI SRB specific fields
        //
        if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
            (srbEx->NumSrbExData > 0)) {
            cdb = GetSrbScsiData(srbEx, NULL, NULL, &scsiStatus, &senseBuffer, &senseBufferLength);

            //
            // cdb and sense buffer should not be NULL
            //
            NT_ASSERT(cdb != NULL);
            NT_ASSERT(senseBuffer != NULL);

        }

        if (cdb == NULL) {

            //
            // Use a cdb that is all 0s
            //
            cdb = &noOp;
        }

    } else {

        cdb = (PCDB)(Srb->Cdb);
        scsiStatus = Srb->ScsiStatus;
        senseBufferLength = Srb->SenseInfoBufferLength;
        senseBuffer = Srb->SenseInfoBuffer;
    }

    if (*Status == STATUS_DATA_OVERRUN &&
        (cdb != NULL) &&
        (IS_SCSIOP_READWRITE(cdb->CDB10.OperationCode))) {

            *Retry = TRUE;

            //
            // Update the error count for the device.
            //

            fdoExtension->ErrorCount++;

    } else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
               scsiStatus == SCSISTAT_BUSY) {

        //
        // a disk drive should never be busy this long. Reset the scsi bus
        // maybe this will clear the condition.
        //

        ResetBus(Fdo);

        //
        // Update the error count for the device.
        //

        fdoExtension->ErrorCount++;

    } else {

        BOOLEAN invalidatePartitionTable = FALSE;

        //
        // See if this might indicate that something on the drive has changed.
        //

        if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
            (senseBuffer != NULL) && (cdb != NULL)) {

            BOOLEAN validSense = FALSE;
            UCHAR senseKey = 0;
            UCHAR asc = 0;
            UCHAR ascq = 0;

            validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
                                                 senseBufferLength,
                                                 SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
                                                 &senseKey,
                                                 &asc,
                                                 &ascq);

            if (validSense) {

                switch (senseKey) {

                    case SCSI_SENSE_ILLEGAL_REQUEST: {

                        switch (asc) {

                            case SCSI_ADSENSE_INVALID_CDB:
                            {
                                //
                                // Look to see if this is an Io request with the ForceUnitAccess flag set
                                //
                                if (((cdb->CDB10.OperationCode == SCSIOP_WRITE)   ||
                                    (cdb->CDB10.OperationCode == SCSIOP_WRITE16)) &&
                                    (cdb->CDB10.ForceUnitAccess))
                                {
                                    PDISK_DATA diskData = (PDISK_DATA)fdoExtension->CommonExtension.DriverData;

                                    if (diskData->WriteCacheOverride == DiskWriteCacheEnable)
                                    {
                                        PIO_ERROR_LOG_PACKET logEntry = NULL;

                                        //
                                        // The user has explicitly requested that write caching be turned on.
                                        // Warn the user that writes with FUA enabled are not working and that
                                        // they should disable write cache.
                                        //

                                        logEntry = IoAllocateErrorLogEntry(fdoExtension->DeviceObject,
                                                                           sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));

                                        if (logEntry != NULL)
                                        {
                                            logEntry->FinalStatus       = *Status;
                                            logEntry->ErrorCode         = IO_WARNING_WRITE_FUA_PROBLEM;
                                            logEntry->SequenceNumber    = 0;
                                            logEntry->MajorFunctionCode = IRP_MJ_SCSI;
                                            logEntry->IoControlCode     = 0;
                                            logEntry->RetryCount        = 0;
                                            logEntry->UniqueErrorValue  = 0;
                                            logEntry->DumpDataSize      = 4 * sizeof(ULONG);

                                            logEntry->DumpData[0] = diskData->ScsiAddress.PortNumber;
                                            logEntry->DumpData[1] = diskData->ScsiAddress.PathId;
                                            logEntry->DumpData[2] = diskData->ScsiAddress.TargetId;
                                            logEntry->DumpData[3] = diskData->ScsiAddress.Lun;

                                            //
                                            // Write the error log packet.
                                            //

                                            IoWriteErrorLogEntry(logEntry);
                                        }
                                    }
                                    else
                                    {
                                        //
                                        // Turn off write caching on this device. This is so that future
                                        // critical requests need not be sent down with  ForceUnitAccess
                                        //
                                        PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo);

                                        if (workItem)
                                        {
                                            IoQueueWorkItem(workItem, DisableWriteCache, CriticalWorkQueue, workItem);
                                        }
                                    }

                                    SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED);
                                    ADJUST_FUA_FLAG(fdoExtension);


                                    cdb->CDB10.ForceUnitAccess = FALSE;
                                    *Retry = TRUE;

                                } else if ((cdb->CDB6FORMAT.OperationCode == SCSIOP_MODE_SENSE) &&
                                           (cdb->MODE_SENSE.PageCode == MODE_SENSE_RETURN_ALL)) {

                                    //
                                    // Mode sense for all pages failed. This command could fail with
                                    // SCSI_SENSE_ILLEGAL_REQUEST / SCSI_ADSENSE_INVALID_CDB if the data
                                    // to be returned is more than 256 bytes. In which case, try to get
                                    // only MODE_PAGE_CACHING since we only need the block descriptor.
                                    //
                                    // Simply change the page code and retry the request
                                    //

                                    cdb->MODE_SENSE.PageCode = MODE_PAGE_CACHING;
                                    *Retry = TRUE;
                                }

                                break;
                            }
                        } // end switch(asc)
                        break;
                    }

                    case SCSI_SENSE_NOT_READY: {

                        switch (asc) {
                        case SCSI_ADSENSE_LUN_NOT_READY: {
                            switch (ascq) {
                            case SCSI_SENSEQ_BECOMING_READY:
                            case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
                            case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
                                invalidatePartitionTable = TRUE;
                                break;
                            }
                            } // end switch(ascq)
                            break;
                        }

                        case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
                            invalidatePartitionTable = TRUE;
                            break;
                        }
                        } // end switch(asc)
                        break;
                    }

                    case SCSI_SENSE_MEDIUM_ERROR: {
                        invalidatePartitionTable = TRUE;
                        break;
                    }

                    case SCSI_SENSE_HARDWARE_ERROR: {
                        invalidatePartitionTable = TRUE;
                        break;
                    }

                    case SCSI_SENSE_UNIT_ATTENTION:
                    {
                        invalidatePartitionTable = TRUE;
                        break;
                    }

                    case SCSI_SENSE_RECOVERED_ERROR: {
                        invalidatePartitionTable = TRUE;
                        break;
                    }

                } // end switch(senseKey)
            } // end if (validSense)
        } else {

            //
            // On any exceptional scsi condition which might indicate that the
            // device was changed we will flush out the state of the partition
            // table.
            //

            switch (SRB_STATUS(Srb->SrbStatus)) {
                case SRB_STATUS_INVALID_LUN:
                case SRB_STATUS_INVALID_TARGET_ID:
                case SRB_STATUS_NO_DEVICE:
                case SRB_STATUS_NO_HBA:
                case SRB_STATUS_INVALID_PATH_ID:
                case SRB_STATUS_COMMAND_TIMEOUT:
                case SRB_STATUS_TIMEOUT:
                case SRB_STATUS_SELECTION_TIMEOUT:
                case SRB_STATUS_REQUEST_FLUSHED:
                case SRB_STATUS_UNEXPECTED_BUS_FREE:
                case SRB_STATUS_PARITY_ERROR:
                {
                    invalidatePartitionTable = TRUE;
                    break;
                }

                case SRB_STATUS_ERROR:
                {
                    if (scsiStatus == SCSISTAT_RESERVATION_CONFLICT)
                    {
                        invalidatePartitionTable = TRUE;
                    }

                    break;
                }
            } // end switch(Srb->SrbStatus)
        }

        if (invalidatePartitionTable && TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {

            //
            // Inform the upper layers that the volume
            // on this disk is in need of verification
            //

            SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
        }
    }

    return;
}


VOID
DiskSetSpecialHacks(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN ULONG_PTR Data
    )

/*++

Routine Description:

    This function checks to see if an SCSI logical unit requires speical
    flags to be set.

Arguments:

    Fdo - Supplies the device object to be tested.

    InquiryData - Supplies the inquiry data returned by the device of interest.

    AdapterDescriptor - Supplies the capabilities of the device object.

Return Value:

    None.

--*/

{
    PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;

    PAGED_CODE();

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "Disk SetSpecialHacks, Setting Hacks %p\n", (void*) Data));

    //
    // Found a listed controller.  Determine what must be done.
    //

    if (TEST_FLAG(Data, HackDisableTaggedQueuing)) {

        //
        // Disable tagged queuing.
        //

        CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
    }

    if (TEST_FLAG(Data, HackDisableSynchronousTransfers)) {

        //
        // Disable synchronous data transfers.
        //

        SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);

    }

    if (TEST_FLAG(Data, HackDisableSpinDown)) {

        //
        // Disable spinning down of drives.
        //

        SET_FLAG(FdoExtension->ScanForSpecialFlags,
                 CLASS_SPECIAL_DISABLE_SPIN_DOWN);

    }

    if (TEST_FLAG(Data, HackDisableWriteCache)) {

        //
        // Disable the drive's write cache
        //

        SET_FLAG(FdoExtension->ScanForSpecialFlags,
                 CLASS_SPECIAL_DISABLE_WRITE_CACHE);

    }

    if (TEST_FLAG(Data, HackCauseNotReportableHack)) {

        SET_FLAG(FdoExtension->ScanForSpecialFlags,
                 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK);
    }

    if (TEST_FLAG(fdo->Characteristics, FILE_REMOVABLE_MEDIA) &&
        TEST_FLAG(Data, HackRequiresStartUnitCommand)
        ) {

        //
        // this is a list of vendors who require the START_UNIT command
        //

        TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DiskScanForSpecial (%p) => This unit requires "
                    " START_UNITS\n", fdo));
        SET_FLAG(FdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);

    }

    return;
}


VOID
ResetBus(
    IN PDEVICE_OBJECT Fdo
    )

/*++

Routine Description:

    This command sends a reset bus command to the SCSI port driver.

Arguments:

    Fdo - The functional device object for the logical unit with hardware problem.

Return Value:

    None.

--*/

{
    PIO_STACK_LOCATION irpStack;
    PIRP irp;

    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
    PSCSI_REQUEST_BLOCK srb;
    PCOMPLETION_CONTEXT context;
    PSTORAGE_REQUEST_BLOCK srbEx = NULL;
    PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "Disk ResetBus: Sending reset bus request to port driver.\n"));

    //
    // Allocate Srb from nonpaged pool.
    //

    context = ExAllocatePoolWithTag(NonPagedPoolNx,
                                    sizeof(COMPLETION_CONTEXT),
                                    DISK_TAG_CCONTEXT);

    if(context == NULL) {
        return;
    }

    //
    // Save the device object in the context for use by the completion
    // routine.
    //

    context->DeviceObject = Fdo;
    srb = &context->Srb.Srb;

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = &context->Srb.SrbEx;

        //
        // Zero out srb
        //

        RtlZeroMemory(srbEx, sizeof(context->Srb.SrbExBuffer));

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = sizeof(context->Srb.SrbExBuffer);
        srbEx->SrbFunction = SRB_FUNCTION_RESET_BUS;
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

    } else {

        //
        // Zero out srb.
        //

        RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);

        //
        // Write length to SRB.
        //

        srb->Length = SCSI_REQUEST_BLOCK_SIZE;

        srb->Function = SRB_FUNCTION_RESET_BUS;

    }

    //
    // Build the asynchronous request to be sent to the port driver.
    // Since this routine is called from a DPC the IRP should always be
    // available.
    //

    irp = IoAllocateIrp(Fdo->StackSize, FALSE);

    if (irp == NULL) {
        FREE_POOL(context);
        return;
    }

    ClassAcquireRemoveLock(Fdo, irp);

    IoSetCompletionRoutine(irp,
                           (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
                           context,
                           TRUE,
                           TRUE,
                           TRUE);

    irpStack = IoGetNextIrpStackLocation(irp);

    irpStack->MajorFunction = IRP_MJ_SCSI;

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx->RequestPriority = IoGetIoPriorityHint(irp);
        srbEx->OriginalRequest = irp;
    } else {
        srb->OriginalRequest = irp;
    }

    //
    // Store the SRB address in next stack for port driver.
    //

    irpStack->Parameters.Scsi.Srb = srb;

    //
    // Call the port driver with the IRP.
    //

    IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);

    return;

} // end ResetBus()



VOID
DiskLogCacheInformation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PDISK_CACHE_INFORMATION CacheInfo,
    IN NTSTATUS Status
    )
{
    PIO_ERROR_LOG_PACKET logEntry = NULL;

    PAGED_CODE();

    logEntry = IoAllocateErrorLogEntry(FdoExtension->DeviceObject, sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));

    if (logEntry != NULL)
    {
        PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
        BOOLEAN bIsEnabled  = TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);

        logEntry->FinalStatus       = Status;
        logEntry->ErrorCode         = (bIsEnabled) ? IO_WRITE_CACHE_ENABLED : IO_WRITE_CACHE_DISABLED;
        logEntry->SequenceNumber    = 0;
        logEntry->MajorFunctionCode = IRP_MJ_SCSI;
        logEntry->IoControlCode     = 0;
        logEntry->RetryCount        = 0;
        logEntry->UniqueErrorValue  = 0x1;
        logEntry->DumpDataSize      = 4 * sizeof(ULONG);

        logEntry->DumpData[0] = diskData->ScsiAddress.PathId;
        logEntry->DumpData[1] = diskData->ScsiAddress.TargetId;
        logEntry->DumpData[2] = diskData->ScsiAddress.Lun;
        logEntry->DumpData[3] = CacheInfo->WriteCacheEnabled;

        //
        // Write the error log packet.
        //

        IoWriteErrorLogEntry(logEntry);
    }
}

NTSTATUS
DiskGetInfoExceptionInformation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMODE_INFO_EXCEPTIONS ReturnPageData
    )
{
    PMODE_PARAMETER_HEADER modeData;
    PMODE_INFO_EXCEPTIONS pageData;
    ULONG length;

    NTSTATUS status;

    PAGED_CODE();

    //
    // ReturnPageData is allocated by the caller
    //

    modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                         MODE_DATA_SIZE,
                                         DISK_TAG_INFO_EXCEPTION);

    if (modeData == NULL) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: Unable to allocate mode "
                       "data buffer\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeData, MODE_DATA_SIZE);

    length = ClassModeSense(FdoExtension->DeviceObject,
                            (PCHAR) modeData,
                            MODE_DATA_SIZE,
                            MODE_PAGE_FAULT_REPORTING);

    if (length < sizeof(MODE_PARAMETER_HEADER)) {

        //
        // Retry the request in case of a check condition.
        //

        length = ClassModeSense(FdoExtension->DeviceObject,
                                (PCHAR) modeData,
                                MODE_DATA_SIZE,
                                MODE_PAGE_FAULT_REPORTING);

        if (length < sizeof(MODE_PARAMETER_HEADER)) {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: Mode Sense failed\n"));
            FREE_POOL(modeData);
            return STATUS_IO_DEVICE_ERROR;
        }
    }

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

    //
    // Find the mode page for info exceptions
    //

    pageData = ClassFindModePage((PCHAR) modeData,
                                 length,
                                 MODE_PAGE_FAULT_REPORTING,
                                 TRUE);

    if (pageData != NULL) {
        RtlCopyMemory(ReturnPageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
        status =  STATUS_SUCCESS;
    } else {
        status = STATUS_NOT_SUPPORTED;
    }

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: %s support SMART for device %p\n",
                  NT_SUCCESS(status) ? "does" : "does not",
                  FdoExtension->DeviceObject));

    FREE_POOL(modeData);

    return(status);
}


NTSTATUS
DiskSetInfoExceptionInformation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PMODE_INFO_EXCEPTIONS PageData
    )

{
    ULONG i;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    //
    // We will attempt (twice) to issue the mode select with the page.
    // Make the setting persistant so that we don't have to turn it back
    // on after a bus reset.
    //

    for (i = 0; i < 2; i++)
    {
        status = DiskModeSelect(FdoExtension->DeviceObject,
                                (PCHAR) PageData,
                                sizeof(MODE_INFO_EXCEPTIONS),
                                TRUE);
    }

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskSetInfoExceptionInformation: %s for device %p\n",
                        NT_SUCCESS(status) ? "succeeded" : "failed",
                        FdoExtension->DeviceObject));

    return status;
}

NTSTATUS
DiskGetCacheInformation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PDISK_CACHE_INFORMATION CacheInfo
    )
/*++

Routine Description:

    This function gets the caching mode page from the drive. This function
    is called from DiskIoctlGetCacheInformation() in response to the IOCTL
    IOCTL_DISK_GET_CACHE_INFORMATION. This is also called from the
    DisableWriteCache() worker thread to disable caching when write commands fail.

Arguments:

    FdoExtension - The device extension for this device.

    CacheInfo - Buffer to receive the Cache Information.

Return Value:

    NTSTATUS code

--*/

{
    PMODE_PARAMETER_HEADER modeData;
    PMODE_CACHING_PAGE pageData;

    ULONG length;

    PAGED_CODE();



    modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                     MODE_DATA_SIZE,
                                     DISK_TAG_DISABLE_CACHE);

    if (modeData == NULL) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetSetCacheInformation: Unable to allocate mode "
                       "data buffer\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeData, MODE_DATA_SIZE);

    length = ClassModeSense(FdoExtension->DeviceObject,
                            (PCHAR) modeData,
                            MODE_DATA_SIZE,
                            MODE_PAGE_CACHING);

    if (length < sizeof(MODE_PARAMETER_HEADER)) {

        //
        // Retry the request in case of a check condition.
        //

        length = ClassModeSense(FdoExtension->DeviceObject,
                                (PCHAR) modeData,
                                MODE_DATA_SIZE,
                                MODE_PAGE_CACHING);

        if (length < sizeof(MODE_PARAMETER_HEADER)) {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetCacheInformation: Mode Sense failed\n"));

            FREE_POOL(modeData);
            return STATUS_IO_DEVICE_ERROR;
        }
    }

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

    //
    // Check to see if the write cache is enabled.
    //

    pageData = ClassFindModePage((PCHAR) modeData,
                                 length,
                                 MODE_PAGE_CACHING,
                                 TRUE);

    //
    // Check if valid caching page exists.
    //

    if (pageData == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetCacheInformation: Unable to find caching mode page.\n"));
        FREE_POOL(modeData);
        return STATUS_NOT_SUPPORTED;
    }

    //
    // Copy the parameters over.
    //

    RtlZeroMemory(CacheInfo, sizeof(DISK_CACHE_INFORMATION));

    CacheInfo->ParametersSavable = pageData->PageSavable;

    CacheInfo->ReadCacheEnabled = !(pageData->ReadDisableCache);
    CacheInfo->WriteCacheEnabled = pageData->WriteCacheEnable;


    //
    // Translate the values in the mode page into the ones defined in
    // ntdddisk.h.
    //

    CacheInfo->ReadRetentionPriority =
        TRANSLATE_RETENTION_PRIORITY(pageData->ReadRetensionPriority);
    CacheInfo->WriteRetentionPriority =
        TRANSLATE_RETENTION_PRIORITY(pageData->WriteRetensionPriority);

    CacheInfo->DisablePrefetchTransferLength =
        ((pageData->DisablePrefetchTransfer[0] << 8) +
         pageData->DisablePrefetchTransfer[1]);

    CacheInfo->ScalarPrefetch.Minimum =
        ((pageData->MinimumPrefetch[0] << 8) + pageData->MinimumPrefetch[1]);

    CacheInfo->ScalarPrefetch.Maximum =
        ((pageData->MaximumPrefetch[0] << 8) + pageData->MaximumPrefetch[1]);

    if(pageData->MultiplicationFactor) {
        CacheInfo->PrefetchScalar = TRUE;
        CacheInfo->ScalarPrefetch.MaximumBlocks =
            ((pageData->MaximumPrefetchCeiling[0] << 8) +
             pageData->MaximumPrefetchCeiling[1]);
    }


    FREE_POOL(modeData);
    return STATUS_SUCCESS;
}


NTSTATUS
DiskSetCacheInformation(
    IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
    IN PDISK_CACHE_INFORMATION CacheInfo
    )
/*++

Routine Description:

    This function sets the caching mode page in the drive. This function
    is also called from the DisableWriteCache() worker thread to disable
    caching when write commands fail.

Arguments:

    FdoExtension - The device extension for this device.

    CacheInfo - Buffer the contains the Cache Information to be set on the drive.

Return Value:

    NTSTATUS code

--*/
{
    PMODE_PARAMETER_HEADER modeData;
    ULONG length;
    PMODE_CACHING_PAGE pageData;
    ULONG i;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                     MODE_DATA_SIZE,
                                     DISK_TAG_DISABLE_CACHE);

    if (modeData == NULL) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskSetCacheInformation: Unable to allocate mode "
                       "data buffer\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeData, MODE_DATA_SIZE);

    length = ClassModeSense(FdoExtension->DeviceObject,
                            (PCHAR) modeData,
                            MODE_DATA_SIZE,
                            MODE_PAGE_CACHING);

    if (length < sizeof(MODE_PARAMETER_HEADER)) {

        //
        // Retry the request in case of a check condition.
        //

        length = ClassModeSense(FdoExtension->DeviceObject,
                                (PCHAR) modeData,
                                MODE_DATA_SIZE,
                                MODE_PAGE_CACHING);

        if (length < sizeof(MODE_PARAMETER_HEADER)) {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskSetCacheInformation: Mode Sense failed\n"));

            FREE_POOL(modeData);
            return STATUS_IO_DEVICE_ERROR;
        }
    }

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

    //
    // Check to see if the write cache is enabled.
    //

    pageData = ClassFindModePage((PCHAR) modeData,
                                 length,
                                 MODE_PAGE_CACHING,
                                 TRUE);

    //
    // Check if valid caching page exists.
    //

    if (pageData == NULL) {
        FREE_POOL(modeData);
        return STATUS_NOT_SUPPORTED;
    }

    //
    // Don't touch any of the normal parameters - not all drives actually
    // use the correct size of caching mode page.  Just change the things
    // which the user could have modified.
    //

    pageData->PageSavable = FALSE;

    pageData->ReadDisableCache = !(CacheInfo->ReadCacheEnabled);
    pageData->MultiplicationFactor = CacheInfo->PrefetchScalar;
    pageData->WriteCacheEnable = CacheInfo->WriteCacheEnabled;

    pageData->WriteRetensionPriority = (UCHAR)
        TRANSLATE_RETENTION_PRIORITY(CacheInfo->WriteRetentionPriority);
    pageData->ReadRetensionPriority = (UCHAR)
        TRANSLATE_RETENTION_PRIORITY(CacheInfo->ReadRetentionPriority);

    pageData->DisablePrefetchTransfer[0] =
        (UCHAR) (CacheInfo->DisablePrefetchTransferLength >> 8);
    pageData->DisablePrefetchTransfer[1] =
        (UCHAR) (CacheInfo->DisablePrefetchTransferLength & 0x00ff);

    pageData->MinimumPrefetch[0] =
        (UCHAR) (CacheInfo->ScalarPrefetch.Minimum >> 8);
    pageData->MinimumPrefetch[1] =
        (UCHAR) (CacheInfo->ScalarPrefetch.Minimum & 0x00ff);

    pageData->MaximumPrefetch[0] =
        (UCHAR) (CacheInfo->ScalarPrefetch.Maximum >> 8);
    pageData->MaximumPrefetch[1] =
        (UCHAR) (CacheInfo->ScalarPrefetch.Maximum & 0x00ff);

    if(pageData->MultiplicationFactor) {

        pageData->MaximumPrefetchCeiling[0] =
            (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks >> 8);
        pageData->MaximumPrefetchCeiling[1] =
            (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks & 0x00ff);
    }

    //
    // We will attempt (twice) to issue the mode select with the page.
    //

    for (i = 0; i < 2; i++) {

        status = DiskModeSelect(FdoExtension->DeviceObject,
                                (PCHAR) pageData,
                                (pageData->PageLength + 2),
                                CacheInfo->ParametersSavable);

        if (NT_SUCCESS(status)) {

            if (CacheInfo->WriteCacheEnabled)
            {
                SET_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
            }
            else
            {
                CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
            }
            ADJUST_FUA_FLAG(FdoExtension);

            break;
        }
    }

    if (NT_SUCCESS(status))
    {
    } else {

        //
        // We were unable to modify the disk write cache setting
        //

        SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL);
    }

    FREE_POOL(modeData);
    return status;
}

NTSTATUS
DiskIoctlGetCacheSetting(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine description:

    This routine services IOCTL_DISK_GET_CACHE_SETTING. It looks to
    see if there are any issues with the disk cache and whether the
    user had previously indicated that the cache is power-protected

Arguments:

    Fdo - The functional device object processing the request
    Irp - The ioctl to be processed

Return Value:

    STATUS_SUCCESS if successful, an error code otherwise

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_CACHE_SETTING))
    {
        status = STATUS_BUFFER_TOO_SMALL;
    }
    else
    {
        PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;

        cacheSetting->Version = sizeof(DISK_CACHE_SETTING);
        cacheSetting->State   = DiskCacheNormal;

        //
        // Determine whether it is safe to turn on the cache
        //
        if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED))
        {
            cacheSetting->State = DiskCacheWriteThroughNotSupported;
        }

        //
        // Determine whether it is possible to modify the cache setting
        //
        if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL))
        {
            cacheSetting->State = DiskCacheModifyUnsuccessful;
        }

        cacheSetting->IsPowerProtected = TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);

        Irp->IoStatus.Information = sizeof(DISK_CACHE_SETTING);
    }

    return status;
}


NTSTATUS
DiskIoctlSetCacheSetting(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine description:

    This routine services IOCTL_DISK_SET_CACHE_SETTING. It allows
    the user to specify whether the disk cache is power-protected
    or not

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    Fdo - The functional device object processing the request
    Irp - The ioctl to be processed

Return Value:

    STATUS_SUCCESS if successful, an error code otherwise

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status = STATUS_SUCCESS;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_CACHE_SETTING))
    {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }
    else
    {
        PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;

        if (cacheSetting->Version == sizeof(DISK_CACHE_SETTING))
        {
            ULONG isPowerProtected;

            //
            // Save away the user-defined override in our extension and the registry
            //
            if (cacheSetting->IsPowerProtected)
            {
                SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
                isPowerProtected = 1;
            }
            else
            {
                CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
                isPowerProtected = 0;
            }
            ADJUST_FUA_FLAG(fdoExtension);

            ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, isPowerProtected);
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }

    return status;
}

NTSTATUS
DiskIoctlGetLengthInfo(
    IN OUT PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_GET_LENGTH_INFO. It returns
    the disk geometry to the caller.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    NTSTATUS status;
    PIO_STACK_LOCATION irpStack;
    PGET_LENGTH_INFORMATION lengthInfo;
    PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
    PCOMMON_DEVICE_EXTENSION commonExtension;
    PDISK_DATA partitionZeroData;
    NTSTATUS oldReadyStatus;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Initialization
    //

    commonExtension = DeviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation(Irp);
    p0Extension = commonExtension->PartitionZeroExtension;
    partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData);

    //
    // Check that the buffer is large enough.
    //

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) {
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Update the geometry in case it has changed
    //

    status = DiskReadDriveCapacity(p0Extension->DeviceObject);

    //
    // Note whether the drive is ready.  If the status has changed then
    // notify pnp.
    //

    oldReadyStatus = InterlockedExchange(&(partitionZeroData->ReadyStatus), status);

    if(partitionZeroData->ReadyStatus != oldReadyStatus) {
        IoInvalidateDeviceRelations(p0Extension->LowerPdo, BusRelations);
    }

    if(!NT_SUCCESS(status)) {
        return status;
    }
    lengthInfo = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer;

    lengthInfo->Length = commonExtension->PartitionLength;

    status = STATUS_SUCCESS;
    Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);

    return status;
}

NTSTATUS
DiskIoctlGetDriveGeometry(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY. It returns
    the disk geometry to the caller.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - IRP with a return buffer large enough to receive the
            extended geometry information.

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetDriveGeometry: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {

        //
        // Issue ReadCapacity to update device extension
        // with information for current media.
        //

        status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);

        //
        // Note whether the drive is ready.
        //

        diskData->ReadyStatus = status;

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

    //
    // Copy drive geometry information from device extension.
    //

    RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
                  &(fdoExtension->DiskGeometry),
                  sizeof(DISK_GEOMETRY));

    if (((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector == 0) {
        ((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector = 512;
    }
    Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
    return STATUS_SUCCESS;
}

typedef struct _DISK_GEOMETRY_EX_INTERNAL {
    DISK_GEOMETRY Geometry;
    LARGE_INTEGER DiskSize;
    DISK_PARTITION_INFO Partition;
    DISK_DETECTION_INFO Detection;
} DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL;

NTSTATUS
DiskIoctlGetDriveGeometryEx(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. It returns
    the extended disk geometry to the caller.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - The device object to obtain the geometry for.

    Irp - IRP with a return buffer large enough to receive the
            extended geometry information.

Return Value:

    NTSTATUS code

--*/

{
    NTSTATUS status;
    PIO_STACK_LOCATION irpStack;
    PCOMMON_DEVICE_EXTENSION commonExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
    PDISK_DATA diskData;
    PDISK_GEOMETRY_EX_INTERNAL geometryEx;
    ULONG OutputBufferLength;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Setup parameters
    //

    commonExtension = DeviceObject->DeviceExtension;
    fdoExtension = DeviceObject->DeviceExtension;
    diskData = (PDISK_DATA)(commonExtension->DriverData);
    irpStack = IoGetCurrentIrpStackLocation ( Irp );
    geometryEx = NULL;
    OutputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

    //
    // Check that the buffer is large enough. It must be large enough
    // to hold at lest the Geometry and DiskSize fields of of the
    // DISK_GEOMETRY_EX structure.
    //

    if ( OutputBufferLength < FIELD_OFFSET (DISK_GEOMETRY_EX, Data) ) {

        //
        // Buffer too small. Bail out, telling the caller the required
        // size.
        //

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetDriveGeometryEx: Output buffer too small.\n"));
        status = STATUS_BUFFER_TOO_SMALL;
        return status;
    }

    if (TEST_FLAG (DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {

        //
        // Issue a ReadCapacity to update device extension
        // with information for the current media.
        //

        status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);

        diskData->ReadyStatus = status;

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

    //
    // Copy drive geometry.
    //

    geometryEx = (PDISK_GEOMETRY_EX_INTERNAL)Irp->AssociatedIrp.SystemBuffer;
    geometryEx->Geometry = fdoExtension->DiskGeometry;
    if (geometryEx->Geometry.BytesPerSector == 0) {
        geometryEx->Geometry.BytesPerSector = 512;
    }
    geometryEx->DiskSize = commonExtension->PartitionZeroExtension->CommonExtension.PartitionLength;

    //
    // If the user buffer is large enough to hold the partition information
    // then add that as well.
    //

    if (OutputBufferLength >=  FIELD_OFFSET (DISK_GEOMETRY_EX_INTERNAL, Detection)) {

        geometryEx->Partition.SizeOfPartitionInfo = sizeof (geometryEx->Partition);
        geometryEx->Partition.PartitionStyle = diskData->PartitionStyle;

        switch ( diskData->PartitionStyle ) {

            case PARTITION_STYLE_GPT:

                //
                // Copy GPT signature.
                //

                geometryEx->Partition.Gpt.DiskId = diskData->Efi.DiskId;
                break;

            case PARTITION_STYLE_MBR:

                //
                // Copy MBR signature and checksum.
                //

                geometryEx->Partition.Mbr.Signature = diskData->Mbr.Signature;
                geometryEx->Partition.Mbr.CheckSum = diskData->Mbr.MbrCheckSum;
                break;

            default:

                //
                // This is a raw disk. Zero out the signature area so
                // nobody gets confused.
                //

                RtlZeroMemory(&geometryEx->Partition, sizeof (geometryEx->Partition));
        }
    }

    //
    // If the buffer is large enough to hold the detection information,
    // then also add that.
    //

    if (OutputBufferLength >= sizeof (DISK_GEOMETRY_EX_INTERNAL)) {

        geometryEx->Detection.SizeOfDetectInfo = sizeof (geometryEx->Detection);

        status = DiskGetDetectInfo(fdoExtension, &geometryEx->Detection);

        //
        // Failed to obtain detection information, set to none.
        //

        if (!NT_SUCCESS (status)) {
            geometryEx->Detection.DetectionType = DetectNone;
        }
    }

    status = STATUS_SUCCESS;
    Irp->IoStatus.Information = min (OutputBufferLength, sizeof (DISK_GEOMETRY_EX_INTERNAL));

    return status;
}

NTSTATUS
DiskIoctlGetCacheInformation(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_GET_CACHE_INFORMATION. It reads
    the caching mode page from the device and returns information to
    the caller. After validating the user parameter it calls the
    DiskGetCacheInformation() function to get the mode page.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
    NTSTATUS status;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_CACHE_INFORMATION)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetCacheInformation: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    status = DiskGetCacheInformation(fdoExtension, cacheInfo);

    if (NT_SUCCESS(status)) {
        Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION);

        //
        // Make sure write cache setting is reflected in device extension
        //
        if (cacheInfo->WriteCacheEnabled)
        {
            SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
        }
        else
        {
            CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
        }
        ADJUST_FUA_FLAG(fdoExtension);

    }
    return status;
}


NTSTATUS
DiskIoctlSetCacheInformation(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_SET_CACHE_INFORMATION. It allows
    the caller to set the caching mode page on the device. This function
    validates the user parameter and calls the DiskSetCacheInformation()
    function to set the mode page. It also stores the cache value in the
    device extension and registry.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
    NTSTATUS status;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL is equal or above DISPATCH_LEVEL.
    //

    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_CACHE_INFORMATION)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSetCacheInformation: Input buffer length mismatch.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    status = DiskSetCacheInformation(fdoExtension, cacheInfo);

    //
    // Save away the user-defined override in our extension and the registry
    //
    if (cacheInfo->WriteCacheEnabled) {
        diskData->WriteCacheOverride = DiskWriteCacheEnable;
    } else {
        diskData->WriteCacheOverride = DiskWriteCacheDisable;
    }

    ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey,
                            DiskDeviceUserWriteCacheSetting, diskData->WriteCacheOverride);

    DiskLogCacheInformation(fdoExtension, cacheInfo, status);

    return status;
}

NTSTATUS
DiskIoctlGetMediaTypesEx(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_STORAGE_GET_MEDIA_TYPES_EX. It returns
    the media type information to the caller. After validating the user
    parameter it calls DiskDetermineMediaTypes() to get the media type.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status;

    PMODE_PARAMETER_HEADER modeData;
    PMODE_PARAMETER_BLOCK blockDescriptor;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    ULONG modeLength;
    ULONG retries = 4;
    UCHAR densityCode = 0;
    BOOLEAN writable = TRUE;
    BOOLEAN mediaPresent = FALSE;
    ULONG srbSize;
    PSTORAGE_REQUEST_BLOCK srbEx = NULL;
    PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_MEDIA_TYPES)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
    } else {
        srbSize = SCSI_REQUEST_BLOCK_SIZE;
    }

    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                srbSize,
                                DISK_TAG_SRB);

    if (srb == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srb, srbSize);

    //
    // Send a TUR to determine if media is present.
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = (PSTORAGE_REQUEST_BLOCK)srb;

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = srbSize;
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        // Set timeout value.
        srbEx->TimeOutValue = fdoExtension->TimeOutValue;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

        //
        // Set up SCSI SRB extended data fields
        //

        srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
            sizeof(STOR_ADDR_BTL8);
        if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
            srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
            srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
            srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
            srbExDataCdb16->CdbLength = 6;

            cdb = (PCDB)srbExDataCdb16->Cdb;
        } else {
            // Should not happen
            NT_ASSERT(FALSE);

            FREE_POOL(srb);
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Insufficient extended SRB size.\n"));
            return STATUS_INTERNAL_ERROR;
        }

    } else {

        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->CdbLength = 6;
        cdb = (PCDB)srb->Cdb;

        //
        // Set timeout value.
        //

        srb->TimeOutValue = fdoExtension->TimeOutValue;

    }
    cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;

    status = ClassSendSrbSynchronous(DeviceObject,
                                     srb,
                                     NULL,
                                     0,
                                     FALSE);

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

    modeLength = MODE_DATA_SIZE;
    modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                     modeLength,
                                     DISK_TAG_MODE_DATA);

    if (modeData == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Unable to allocate memory.\n"));
        FREE_POOL(srb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeData, modeLength);

    //
    // Build the MODE SENSE CDB using previous SRB.
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx->SrbStatus = 0;
        srbExDataCdb16->ScsiStatus = 0;
        srbExDataCdb16->CdbLength = 6;

        //
        // Set timeout value from device extension.
        //

        srbEx->TimeOutValue = fdoExtension->TimeOutValue;
    } else {
        srb->SrbStatus = 0;
        srb->ScsiStatus = 0;
        srb->CdbLength = 6;

        //
        // Set timeout value from device extension.
        //

        srb->TimeOutValue = fdoExtension->TimeOutValue;
    }

    //
    // Page code of 0x3F will return all pages.
    // This command could fail if the data to be returned is
    // more than 256 bytes. In which case, we should get only
    // the caching page since we only need the block descriptor.
    // DiskFdoProcessError will change the page code to
    // MODE_PAGE_CACHING if there is an error.
    //

    cdb->MODE_SENSE.OperationCode    = SCSIOP_MODE_SENSE;
    cdb->MODE_SENSE.PageCode         = MODE_SENSE_RETURN_ALL;
    cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;

Retry:
    status = ClassSendSrbSynchronous(DeviceObject,
                                     srb,
                                     modeData,
                                     modeLength,
                                     FALSE);

    if (status == STATUS_VERIFY_REQUIRED) {

        if (retries--) {

            //
            // Retry request.
            //

            goto Retry;
        }
    } else if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
        status = STATUS_SUCCESS;
    }

    if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {

        //
        // Get the block descriptor.
        //

        if (modeData->BlockDescriptorLength != 0) {

            blockDescriptor = (PMODE_PARAMETER_BLOCK)((ULONG_PTR)modeData + sizeof(MODE_PARAMETER_HEADER));
            densityCode = blockDescriptor->DensityCode;
        }

        if (TEST_FLAG(modeData->DeviceSpecificParameter,
                      MODE_DSP_WRITE_PROTECT)) {

            writable = FALSE;
        }

        status = DiskDetermineMediaTypes(DeviceObject,
                                         Irp,
                                         modeData->MediumType,
                                         densityCode,
                                         mediaPresent,
                                         writable);
        //
        // If the buffer was too small, DetermineMediaTypes updated the status and information and the request will fail.
        //

    } else {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Mode sense for header/bd failed. %lx\n", status));
    }

    FREE_POOL(srb);
    FREE_POOL(modeData);

    return status;
}

NTSTATUS
DiskIoctlPredictFailure(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_STORAGE_PREDICT_FAILURE. If the device
    supports SMART then it returns any available failure data.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status = STATUS_SUCCESS;

    PSTORAGE_PREDICT_FAILURE checkFailure;
    STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
    IO_STATUS_BLOCK ioStatus = { 0 };
    KEVENT event;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_PREDICT_FAILURE)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // See if the disk is predicting failure
    //

    checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;

    if (diskData->FailurePredictionCapability == FailurePredictionSense) {
        ULONG readBufferSize;
        PUCHAR readBuffer;
        PIRP readIrp;
        PDEVICE_OBJECT topOfStack;

        checkFailure->PredictFailure = 0;

        KeInitializeEvent(&event, SynchronizationEvent, FALSE);

        topOfStack = IoGetAttachedDeviceReference(DeviceObject);

        //
        // SCSI disks need to have a read sent down to provoke any
        // failures to be reported.
        //
        // Issue a normal read operation.  The error-handling code in
        // classpnp will take care of a failure prediction by logging the
        // correct event.
        //

        readBufferSize = fdoExtension->DiskGeometry.BytesPerSector;
        readBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
                                           readBufferSize,
                                           DISK_TAG_SMART);

        if (readBuffer != NULL) {
            LARGE_INTEGER offset;

            offset.QuadPart = 0;
            readIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
                                                   topOfStack,
                                                   readBuffer,
                                                   readBufferSize,
                                                   &offset,
                                                   &event,
                                                   &ioStatus);

            if (readIrp != NULL) {

                status = IoCallDriver(topOfStack, readIrp);
                if (status == STATUS_PENDING) {
                    KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
                    status = ioStatus.Status;
                }


            } else {
                status = STATUS_INSUFFICIENT_RESOURCES;
            }

            FREE_POOL(readBuffer);
        } else {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }

        ObDereferenceObject(topOfStack);
    }

    if (status != STATUS_INSUFFICIENT_RESOURCES)
    {
        if ((diskData->FailurePredictionCapability == FailurePredictionSmart) ||
            (diskData->FailurePredictionCapability == FailurePredictionSense)) {

            status = DiskReadFailurePredictStatus(fdoExtension, &diskSmartStatus);

            if (NT_SUCCESS(status)) {

                status = DiskReadFailurePredictData(fdoExtension,
                                                    Irp->AssociatedIrp.SystemBuffer);

                if (diskSmartStatus.PredictFailure) {
                    checkFailure->PredictFailure = 1;
                } else {
                    checkFailure->PredictFailure = 0;
                }

                Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
            }
        } else {
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    }
    return status;
}

#if (NTDDI_VERSION >= NTDDI_WINBLUE)
NTSTATUS
DiskIoctlEnableFailurePrediction(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG. If the device
    supports SMART then it returns any available failure data.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status = STATUS_SUCCESS;
    PSTORAGE_FAILURE_PREDICTION_CONFIG enablePrediction;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG) ||
        irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    enablePrediction = (PSTORAGE_FAILURE_PREDICTION_CONFIG)Irp->AssociatedIrp.SystemBuffer;

    if (enablePrediction->Version != STORAGE_FAILURE_PREDICTION_CONFIG_V1 ||
        enablePrediction->Size < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Buffer version or size is incorrect.\n"));
        status = STATUS_INVALID_PARAMETER;
    }

    if (enablePrediction->Reserved != 0) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Reserved bytes are not zero!\n"));
        status = STATUS_INVALID_PARAMETER;
    }

    //
    // Default to success.  This might get overwritten on failure below.
    //
    status = STATUS_SUCCESS;

    //
    // If this is a "set" and the current state (enabled/disabled) is
    // different from the sender's desired state, 
    //
    if (enablePrediction->Set && enablePrediction->Enabled != diskData->FailurePredictionEnabled) {
        if (diskData->FailurePredictionCapability == FailurePredictionSmart ||
            diskData->FailurePredictionCapability == FailurePredictionIoctl) {
            //
            // SMART or IOCTL based failure prediction is being used so call
            // the generic function that is normally called in the WMI path.
            //
            status = DiskEnableDisableFailurePrediction(fdoExtension, enablePrediction->Enabled);
        } else if (diskData->ScsiInfoExceptionsSupported) {
            //
            // If we know that the device supports the Informational Exceptions
            // mode page, try to enable/disable failure prediction that way.
            //
            status = DiskEnableInfoExceptions(fdoExtension, enablePrediction->Enabled);
        }
    }

    //
    // Return the current state regardless if this was a "set" or a "get".
    //
    enablePrediction->Enabled = diskData->FailurePredictionEnabled;

    if (NT_SUCCESS(status)) {
        Irp->IoStatus.Information = sizeof(STORAGE_FAILURE_PREDICTION_CONFIG);
    }

    return status;
}
#endif //(NTDDI_VERSION >= NTDDI_WINBLUE)

NTSTATUS
DiskIoctlVerify(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_VERIFY. After verifying
    user input, it starts the worker thread DiskIoctlVerifyThread()
    to verify the device.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
    PDISK_VERIFY_WORKITEM_CONTEXT Context = NULL;
    PSCSI_REQUEST_BLOCK srb;
    LARGE_INTEGER byteOffset;
    ULONG srbSize;

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VERIFY_INFORMATION)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Input buffer length mismatch.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
    } else {
        srbSize = SCSI_REQUEST_BLOCK_SIZE;
    }
    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                srbSize,
                                DISK_TAG_SRB);

    if (srb == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srb, srbSize);

    //
    // Add disk offset to starting sector.
    //

    byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart +
                          verifyInfo->StartingOffset.QuadPart;

    //
    // Perform a bounds check on the sector range
    //

    if ((verifyInfo->StartingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
        (verifyInfo->StartingOffset.QuadPart < 0)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Verify request to invalid sector.\n"));
        FREE_POOL(srb)
        return STATUS_NONEXISTENT_SECTOR;
    } else {

        ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - verifyInfo->StartingOffset.QuadPart;

        if ((ULONGLONG)verifyInfo->Length > bytesRemaining) {

            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Verify request to invalid sector.\n"));
            FREE_POOL(srb)
            return STATUS_NONEXISTENT_SECTOR;
        }
    }

    Context = ExAllocatePoolWithTag(NonPagedPoolNx,
                                    sizeof(DISK_VERIFY_WORKITEM_CONTEXT),
                                    DISK_TAG_WI_CONTEXT);
    if (Context) {

        Context->Irp = Irp;
        Context->Srb = srb;
        Context->WorkItem = IoAllocateWorkItem(DeviceObject);

        if (Context->WorkItem) {

            //
            // Queue the work item and return.
            //

            IoMarkIrpPending(Irp);

            IoQueueWorkItem(Context->WorkItem,
                            DiskIoctlVerifyThread,
                            DelayedWorkQueue,
                            Context);

            return STATUS_PENDING;
        }
        FREE_POOL(Context);
    }
    FREE_POOL(srb)
    return STATUS_INSUFFICIENT_RESOURCES;
}

NTSTATUS
DiskIoctlReassignBlocks(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_REASSIGN_BLOCKS. This IOCTL
    allows the caller to remap the defective blocks to a new
    location on the disk.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status;
    PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    ULONG bufferSize;
    ULONG blockNumber;
    ULONG blockCount;
    ULONG srbSize;
    PSTORAGE_REQUEST_BLOCK srbEx;
    PSTOR_ADDR_BTL8 storAddrBtl8;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(REASSIGN_BLOCKS)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Input buffer length mismatch.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // Make sure we have some data in the input buffer.
    //

    if (badBlocks->Count == 0) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Invalid block count\n"));
        return STATUS_INVALID_PARAMETER;
    }

    bufferSize = sizeof(REASSIGN_BLOCKS) + ((badBlocks->Count - 1) * sizeof(ULONG));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < bufferSize) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Input buffer length mismatch for bad blocks.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
    } else {
        srbSize = SCSI_REQUEST_BLOCK_SIZE;
    }
    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                srbSize,
                                DISK_TAG_SRB);

    if (srb == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srb, srbSize);

    //
    // Build the data buffer to be transferred in the input buffer.
    // The format of the data to the device is:
    //
    //      2 bytes Reserved
    //      2 bytes Length
    //      x * 4 btyes Block Address
    //
    // All values are big endian.
    //

    badBlocks->Reserved = 0;
    blockCount = badBlocks->Count;

    //
    // Convert # of entries to # of bytes.
    //

    blockCount *= 4;
    badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
    badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);

    //
    // Convert back to number of entries.
    //

    blockCount /= 4;

    for (; blockCount > 0; blockCount--) {

        blockNumber = badBlocks->BlockNumber[blockCount-1];
        REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1], (PFOUR_BYTE) &blockNumber);
    }

    //
    // Build a SCSI SRB containing a SCSIOP_REASSIGN_BLOCKS cdb
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = (PSTORAGE_REQUEST_BLOCK)srb;

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = srbSize;
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        // Set timeout value.
        srbEx->TimeOutValue = fdoExtension->TimeOutValue;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

        //
        // Set up SCSI SRB extended data fields
        //

        srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
            sizeof(STOR_ADDR_BTL8);
        if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
            srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
            srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
            srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
            srbExDataCdb16->CdbLength = 6;

            cdb = (PCDB)srbExDataCdb16->Cdb;
        } else {
            // Should not happen
            NT_ASSERT(FALSE);

            FREE_POOL(srb);
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Insufficient extended SRB size.\n"));
            return STATUS_INTERNAL_ERROR;
        }

    } else {
        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->CdbLength = 6;

        //
        // Set timeout value.
        //

        srb->TimeOutValue = fdoExtension->TimeOutValue;

        cdb = (PCDB)srb->Cdb;
    }

    cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;

    status = ClassSendSrbSynchronous(DeviceObject,
                                     srb,
                                     badBlocks,
                                     bufferSize,
                                     TRUE);

    FREE_POOL(srb);
    return status;
}

NTSTATUS
DiskIoctlReassignBlocksEx(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_REASSIGN_BLOCKS_EX. This IOCTL
    allows the caller to remap the defective blocks to a new
    location on the disk. The input buffer contains 8-byte LBAs.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status;
    PREASSIGN_BLOCKS_EX badBlocks = Irp->AssociatedIrp.SystemBuffer;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    LARGE_INTEGER blockNumber;
    ULONG bufferSize;
    ULONG blockCount;
    ULONG srbSize;
    PSTORAGE_REQUEST_BLOCK srbEx;
    PSTOR_ADDR_BTL8 storAddrBtl8;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(REASSIGN_BLOCKS_EX)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Input buffer length mismatch.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // Make sure we have some data in the input buffer.
    //

    if (badBlocks->Count == 0) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Invalid block count\n"));
        return STATUS_INVALID_PARAMETER;
    }

    bufferSize = sizeof(REASSIGN_BLOCKS_EX) + ((badBlocks->Count - 1) * sizeof(LARGE_INTEGER));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < bufferSize) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Input buffer length mismatch for bad blocks.\n"));
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
    } else {
        srbSize = SCSI_REQUEST_BLOCK_SIZE;
    }
    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                srbSize,
                                DISK_TAG_SRB);

    if (srb == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srb, srbSize);

    //
    // Build the data buffer to be transferred in the input buffer.
    // The format of the data to the device is:
    //
    //      2 bytes Reserved
    //      2 bytes Length
    //      x * 8 btyes Block Address
    //
    // All values are big endian.
    //

    badBlocks->Reserved = 0;
    blockCount = badBlocks->Count;

    //
    // Convert # of entries to # of bytes.
    //

    blockCount *= 8;
    badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
    badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);

    //
    // Convert back to number of entries.
    //

    blockCount /= 8;

    for (; blockCount > 0; blockCount--) {

        blockNumber = badBlocks->BlockNumber[blockCount-1];
        REVERSE_BYTES_QUAD(&badBlocks->BlockNumber[blockCount-1], &blockNumber);
    }

    //
    // Build a SCSI SRB containing a SCSIOP_REASSIGN_BLOCKS cdb
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = (PSTORAGE_REQUEST_BLOCK)srb;

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = srbSize;
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        // Set timeout value.
        srbEx->TimeOutValue = fdoExtension->TimeOutValue;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

        //
        // Set up SCSI SRB extended data fields
        //

        srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
            sizeof(STOR_ADDR_BTL8);
        if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
            srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
            srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
            srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
            srbExDataCdb16->CdbLength = 6;

            cdb = (PCDB)srbExDataCdb16->Cdb;
        } else {
            // Should not happen
            NT_ASSERT(FALSE);

            FREE_POOL(srb);
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Insufficient extended SRB size.\n"));
            return STATUS_INTERNAL_ERROR;
        }

    } else {
        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->CdbLength = 6;

        //
        // Set timeout value.
        //

        srb->TimeOutValue = fdoExtension->TimeOutValue;

        cdb = (PCDB)srb->Cdb;
    }

    cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
    cdb->CDB6GENERIC.CommandUniqueBits =  1; // LONGLBA

    status = ClassSendSrbSynchronous(DeviceObject,
                                     srb,
                                     badBlocks,
                                     bufferSize,
                                     TRUE);

    FREE_POOL(srb);
    return status;
}

NTSTATUS
DiskIoctlIsWritable(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_IS_WRITABLE. This function
    returns whether the disk is writable. If the device is not
    writable then STATUS_MEDIA_WRITE_PROTECTED will be returned.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    NTSTATUS status = STATUS_SUCCESS;

    PMODE_PARAMETER_HEADER modeData;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb = NULL;
    ULONG modeLength;
    ULONG retries = 4;
    ULONG srbSize;
    PSTORAGE_REQUEST_BLOCK srbEx;
    PSTOR_ADDR_BTL8 storAddrBtl8;
    PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
    } else {
        srbSize = SCSI_REQUEST_BLOCK_SIZE;
    }
    srb = ExAllocatePoolWithTag(NonPagedPoolNx,
                                srbSize,
                                DISK_TAG_SRB);

    if (srb == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srb, srbSize);

    //
    // Allocate memory for a mode header and then some
    // for port drivers that need to convert to MODE10
    // or always return the MODE_PARAMETER_BLOCK (even
    // when memory was not allocated for this purpose)
    //

    modeLength = MODE_DATA_SIZE;
    modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
                                     modeLength,
                                     DISK_TAG_MODE_DATA);

    if (modeData == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: Unable to allocate memory.\n"));
        FREE_POOL(srb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(modeData, modeLength);

    //
    // Build the MODE SENSE CDB
    //

    if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
        srbEx = (PSTORAGE_REQUEST_BLOCK)srb;

        //
        // Set up STORAGE_REQUEST_BLOCK fields
        //

        srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
        srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
        srbEx->Signature = SRB_SIGNATURE;
        srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
        srbEx->SrbLength = srbSize;
        srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
        srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
        srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
        srbEx->NumSrbExData = 1;

        // Set timeout value.
        srbEx->TimeOutValue = fdoExtension->TimeOutValue;

        //
        // Set up address fields
        //

        storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
        storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
        storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;

        //
        // Set up SCSI SRB extended data fields
        //

        srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
            sizeof(STOR_ADDR_BTL8);
        if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
            srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
            srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
            srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
            srbExDataCdb16->CdbLength = 6;

            cdb = (PCDB)srbExDataCdb16->Cdb;
        } else {
            // Should not happen
            NT_ASSERT(FALSE);

            FREE_POOL(srb);
            FREE_POOL(modeData);
            return STATUS_INTERNAL_ERROR;
        }

    } else {
        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
        srb->CdbLength = 6;

        //
        // Set timeout value.
        //

        srb->TimeOutValue = fdoExtension->TimeOutValue;

        cdb = (PCDB)srb->Cdb;
    }

    //
    // Page code of 0x3F will return all pages.
    // This command could fail if the data to be returned is
    // more than 256 bytes. In which case, we should get only
    // the caching page since we only need the block descriptor.
    // DiskFdoProcessError will change the page code to
    // MODE_PAGE_CACHING if there is an error.
    //

    cdb->MODE_SENSE.OperationCode    = SCSIOP_MODE_SENSE;
    cdb->MODE_SENSE.PageCode         = MODE_SENSE_RETURN_ALL;
    cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;

    while (retries != 0) {

        status = ClassSendSrbSynchronous(DeviceObject,
                                         srb,
                                         modeData,
                                         modeLength,
                                         FALSE);

        if (status != STATUS_VERIFY_REQUIRED) {
            if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
                status = STATUS_SUCCESS;
            }
            break;
        }
        retries--;
    }

    if (NT_SUCCESS(status)) {

        if (TEST_FLAG(modeData->DeviceSpecificParameter, MODE_DSP_WRITE_PROTECT)) {
            status = STATUS_MEDIA_WRITE_PROTECTED;
        }
    }

    FREE_POOL(srb);
    FREE_POOL(modeData);
    return status;
}

NTSTATUS
DiskIoctlSetVerify(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_INTERNAL_SET_VERIFY.
    This is an internal function used to set the DO_VERIFY_VOLUME
    device object flag. Only a kernel mode component can send this
    IOCTL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    NTSTATUS status = STATUS_NOT_SUPPORTED;

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSetVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    //
    // If the caller is kernel mode, set the verify bit.
    //

    if (Irp->RequestorMode == KernelMode) {

        SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
        status = STATUS_SUCCESS;

    }
    return status;
}

NTSTATUS
DiskIoctlClearVerify(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_INTERNAL_CLEAR_VERIFY.
    This is an internal function used to clear the DO_VERIFY_VOLUME
    device object flag. Only a kernel mode component can send this
    IOCTL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    NTSTATUS status = STATUS_NOT_SUPPORTED;

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlClearVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    //
    // If the caller is kernel mode, set the verify bit.
    //

    if (Irp->RequestorMode == KernelMode) {

        CLEAR_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
        status = STATUS_SUCCESS;

    }
    return status;
}

NTSTATUS
DiskIoctlUpdateDriveSize(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_DISK_UPDATE_DRIVE_SIZE.
    This function is used to inform the disk driver to update
    the device geometry information cached in the device extension
    This is normally initiated from the drivers layers above disk
    driver.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
    NTSTATUS status;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlUpdateDriveSize: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {

        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlUpdateDriveSize: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    status = DiskReadDriveCapacity(DeviceObject);

    //
    // Note whether the drive is ready.
    //

    diskData->ReadyStatus = status;

    if (NT_SUCCESS(status)) {

        //
        // Copy drive geometry information from the device extension.
        //

        RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
                      &(fdoExtension->DiskGeometry),
                      sizeof(DISK_GEOMETRY));

        if (((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector == 0) {
            ((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector = 512;
        }
        Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
        status = STATUS_SUCCESS;

        //
        // Notify everyone that the disk layout may have changed
        //

        Notification.Event   = GUID_IO_DISK_LAYOUT_CHANGE;
        Notification.Version = 1;
        Notification.Size    = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
        Notification.FileObject = NULL;
        Notification.NameBufferOffset = -1;

        IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
                                               &Notification,
                                               NULL,
                                               NULL);
    }
    return status;
}

NTSTATUS
DiskIoctlGetVolumeDiskExtents(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
    and IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN. This function
    returns the physical location of a volume.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetVolumeDiskExtents: DeviceObject %p Irp %p\n", DeviceObject, Irp));


    if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {

        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS)) {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetVolumeDiskExtents: Output buffer too small.\n"));
            return STATUS_BUFFER_TOO_SMALL;
        }

        status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);

        //
        // Note whether the drive is ready.
        //

        diskData->ReadyStatus = status;

        if (NT_SUCCESS(status)) {

            PVOLUME_DISK_EXTENTS pVolExt = (PVOLUME_DISK_EXTENTS)Irp->AssociatedIrp.SystemBuffer;

            pVolExt->NumberOfDiskExtents = 1;
            pVolExt->Extents[0].DiskNumber     = commonExtension->PartitionZeroExtension->DeviceNumber;
            pVolExt->Extents[0].StartingOffset = commonExtension->StartingOffset;
            pVolExt->Extents[0].ExtentLength   = commonExtension->PartitionLength;

            Irp->IoStatus.Information = sizeof(VOLUME_DISK_EXTENTS);
        }
    }

    return status;
}

NTSTATUS
DiskIoctlSmartGetVersion(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services SMART_GET_VERSION. It returns the
    SMART version information.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status;

    PGETVERSIONINPARAMS versionParams;
    PSRB_IO_CONTROL srbControl;
    IO_STATUS_BLOCK ioStatus = { 0 };
    PUCHAR buffer;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: DeviceObject %p Irp %p\n", DeviceObject, Irp));


    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GETVERSIONINPARAMS)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
                                       sizeof(SRB_IO_CONTROL) +
                                       sizeof(GETVERSIONINPARAMS),
                                       DISK_TAG_SMART);

    if (srbControl == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: Unable to allocate memory.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(srbControl, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));

    //
    // fill in srbControl fields
    //

    srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
    RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
    srbControl->Timeout = fdoExtension->TimeOutValue;
    srbControl->Length = sizeof(GETVERSIONINPARAMS);
    srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;

    //
    // Point to the 'buffer' portion of the SRB_CONTROL
    //

    buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

    //
    // Ensure correct target is set in the cmd parameters.
    //

    versionParams = (PGETVERSIONINPARAMS)buffer;
    versionParams->bIDEDeviceMap = diskData->ScsiAddress.TargetId;

    ClassSendDeviceIoControlSynchronous(
                                    IOCTL_SCSI_MINIPORT,
                                    commonExtension->LowerDeviceObject,
                                    srbControl,
                                    sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
                                    sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
                                    FALSE,
                                    &ioStatus);

    status = ioStatus.Status;

    //
    // If successful, copy the data received into the output buffer.
    // This should only fail in the event that the IDE driver is older
    // than this driver.
    //

    if (NT_SUCCESS(status)) {

        buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

        RtlMoveMemory (Irp->AssociatedIrp.SystemBuffer, buffer, sizeof(GETVERSIONINPARAMS));
        Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
    }

    FREE_POOL(srbControl);

    return status;
}

NTSTATUS
DiskIoctlSmartReceiveDriveData(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services SMART_RCV_DRIVE_DATA. This function
    allows the caller to read SMART information from the device.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status = STATUS_INVALID_PARAMETER;

    PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
    PSRB_IO_CONTROL srbControl;
    IO_STATUS_BLOCK ioStatus = { 0 };
    ULONG controlCode = 0;
    PUCHAR buffer;
    PIRP irp2;
    KEVENT event;
    ULONG length = 0;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < (sizeof(SENDCMDINPARAMS) - 1)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Input buffer length invalid.\n"));
        return STATUS_INVALID_PARAMETER;
    }

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Create notification event object to be used to signal the
    // request completion.
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    //
    // use controlCode as a sort of 'STATUS_SUCCESS' to see if it's
    // a valid request type
    //

    if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) {

        length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
        controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;

    } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {

        switch (cmdInParameters->irDriveRegs.bFeaturesReg) {

            case READ_ATTRIBUTES:
                controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
                length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
                break;

            case READ_THRESHOLDS:
                controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
                length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
                break;

            case SMART_READ_LOG: {

                if (diskData->FailurePredictionCapability != FailurePredictionSmart) {
                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: SMART failure prediction not supported.\n"));
                    return STATUS_INVALID_DEVICE_REQUEST;
                }

                //
                // Calculate additional length based on number of sectors to be read.
                // Then verify the output buffer is large enough.
                //

                length = cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;

                //
                // Ensure at least 1 sector is going to be read
                //
                if (length == 0) {
                    return STATUS_INVALID_PARAMETER;
                }

                length += max(sizeof(SENDCMDOUTPARAMS), sizeof(SENDCMDINPARAMS));

                if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length - 1) {
                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Output buffer too small for SMART_READ_LOG.\n"));
                    return STATUS_BUFFER_TOO_SMALL;
                }

                controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
                break;
            }
        }
    }

    if (controlCode == 0) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Invalid request.\n"));
        return STATUS_INVALID_PARAMETER;
    }

    srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
                                       sizeof(SRB_IO_CONTROL) + length,
                                       DISK_TAG_SMART);

    if (srbControl == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Unable to allocate memory.\n"));
        return  STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // fill in srbControl fields
    //

    srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
    RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
    srbControl->Timeout = fdoExtension->TimeOutValue;
    srbControl->Length = length;
    srbControl->ControlCode = controlCode;

    //
    // Point to the 'buffer' portion of the SRB_CONTROL
    //

    buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

    //
    // Ensure correct target is set in the cmd parameters.
    //

    cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;

    //
    // Copy the IOCTL parameters to the srb control buffer area.
    //

    RtlMoveMemory(buffer,
                  Irp->AssociatedIrp.SystemBuffer,
                  sizeof(SENDCMDINPARAMS) - 1);

    irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
                                        commonExtension->LowerDeviceObject,
                                        srbControl,
                                        sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
                                        srbControl,
                                        sizeof(SRB_IO_CONTROL) + length,
                                        FALSE,
                                        &event,
                                        &ioStatus);

    if (irp2 == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Unable to allocate IRP.\n"));
        FREE_POOL(srbControl);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Call the port driver with the request and wait for it to complete.
    //

    status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    //
    // Copy the data received into the output buffer. Since the status buffer
    // contains error information also, always perform this copy. IO will
    // either pass this back to the app, or zero it, in case of error.
    //

    buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

    if (NT_SUCCESS(status)) {

        RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length - 1);
        Irp->IoStatus.Information = length - 1;

    } else {

        RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, (sizeof(SENDCMDOUTPARAMS) - 1));
        Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;

    }

    FREE_POOL(srbControl);

    return status;
}

NTSTATUS
DiskIoctlSmartSendDriveCommand(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )

/*++

Routine Description:

    This routine services SMART_SEND_DRIVE_COMMAND. This function
    allows the caller to send SMART commands to the device.

    This function must be called at IRQL < DISPATCH_LEVEL.

Arguments:

    DeviceObject - Supplies the device object associated with this request.

    Irp - The IRP to be processed

Return Value:

    NTSTATUS code

--*/

{
    PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
    NTSTATUS status = STATUS_INVALID_PARAMETER;

    PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
    PSRB_IO_CONTROL srbControl;
    IO_STATUS_BLOCK ioStatus = { 0 };
    ULONG controlCode = 0;
    PUCHAR buffer;
    PIRP irp2;
    KEVENT event;
    ULONG length = 0;

    //
    // This function must be called at less than dispatch level.
    // Fail if IRQL >= DISPATCH_LEVEL.
    //
    PAGED_CODE();
    CHECK_IRQL();

    //
    // Validate the request.
    //

    TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: DeviceObject %p Irp %p\n", DeviceObject, Irp));

    if (irpStack->Parameters.DeviceIoControl.InputBufferLength < (sizeof(SENDCMDINPARAMS) - 1)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Input buffer size invalid.\n"));
        return STATUS_INVALID_PARAMETER;
    }

    if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < (sizeof(SENDCMDOUTPARAMS) - 1)) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Output buffer too small.\n"));
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Create notification event object to be used to signal the
    // request completion.
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {

        switch (cmdInParameters->irDriveRegs.bFeaturesReg) {

            case SMART_WRITE_LOG: {

                if (diskData->FailurePredictionCapability != FailurePredictionSmart) {

                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: SMART failure prediction not supported.\n"));
                    return STATUS_INVALID_DEVICE_REQUEST;
                }

                //
                // Calculate additional length based on number of sectors to be written.
                // Then verify the input buffer is large enough.
                //

                length = cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;

                //
                // Ensure at least 1 sector is going to be written
                //

                if (length == 0) {
                    return STATUS_INVALID_PARAMETER;
                }

                if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
                        (sizeof(SENDCMDINPARAMS) - 1) + length) {

                    TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Input buffer too small for SMART_WRITE_LOG.\n"));
                    return STATUS_BUFFER_TOO_SMALL;
                }

                controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
                break;
            }

            case ENABLE_SMART:
                controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
                break;

            case DISABLE_SMART:
                controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
                break;

            case RETURN_SMART_STATUS:

                //
                // Ensure bBuffer is at least 2 bytes (to hold the values of
                // cylinderLow and cylinderHigh).
                //

                if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
                    (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {

                    return STATUS_BUFFER_TOO_SMALL;
                    break;
                }

                controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
                length = sizeof(IDEREGS);
                break;

            case ENABLE_DISABLE_AUTOSAVE:
                controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
                break;

            case SAVE_ATTRIBUTE_VALUES:
                controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
                break;

            case EXECUTE_OFFLINE_DIAGS:
                //
                // Validate that this is an ok self test command
                //
                if (DiskIsValidSmartSelfTest(cmdInParameters->irDriveRegs.bSectorNumberReg)) {

                    controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
                }
                break;

            case ENABLE_DISABLE_AUTO_OFFLINE:
                controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE;
                break;
        }
    }

    if (controlCode == 0) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Invalid request.\n"));
        return STATUS_INVALID_PARAMETER;
    }

    length += max(sizeof(SENDCMDOUTPARAMS), sizeof(SENDCMDINPARAMS));
    srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
                                       sizeof(SRB_IO_CONTROL) + length,
                                       DISK_TAG_SMART);

    if (srbControl == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Unable to allocate memory.\n"));
        return  STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // fill in srbControl fields
    //

    srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
    RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
    srbControl->Timeout = fdoExtension->TimeOutValue;
    srbControl->Length = length;
    srbControl->ControlCode = controlCode;

    //
    // Point to the 'buffer' portion of the SRB_CONTROL
    //

    buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

    //
    // Ensure correct target is set in the cmd parameters.
    //

    cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;

    //
    // Copy the IOCTL parameters to the srb control buffer area.
    //

    if (cmdInParameters->irDriveRegs.bFeaturesReg == SMART_WRITE_LOG) {
        RtlMoveMemory(buffer,
                     Irp->AssociatedIrp.SystemBuffer,
                     sizeof(SENDCMDINPARAMS) - 1 +
                        cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE);
    } else {
        RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
    }

    irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
                                        commonExtension->LowerDeviceObject,
                                        srbControl,
                                        sizeof(SRB_IO_CONTROL) + length,
                                        srbControl,
                                        sizeof(SRB_IO_CONTROL) + length,
                                        FALSE,
                                        &event,
                                        &ioStatus);

    if (irp2 == NULL) {
        TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Unable to allocate IRP.\n"));
        FREE_POOL(srbControl);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Call the port driver with the request and wait for it to complete.
    //

    status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    //
    // Copy the data received into the output buffer. Since the status buffer
    // contains error information also, always perform this copy. IO will
    // either pass this back to the app, or zero it, in case of error.
    //

    buffer = (PUCHAR)srbControl + srbControl->HeaderLength;

    //
    // Update the return buffer size based on the sub-command.
    //

    if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) {
        length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
    } else {
        length = sizeof(SENDCMDOUTPARAMS) - 1;
    }

    RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length);
    Irp->IoStatus.Information = length;

    FREE_POOL(srbControl);

    return status;
}


VOID
DiskEtwEnableCallback (
    _In_ LPCGUID                      SourceId,
    _In_ ULONG                        IsEnabled,
    _In_ UCHAR                        Level,
    _In_ ULONGLONG                    MatchAnyKeyword,
    _In_ ULONGLONG                    MatchAllKeyword,
    _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
    _In_opt_ PVOID                    CallbackContext
    )
/*+++

Routine Description:

    This routine is the enable callback routine for ETW. It gets called when
    tracing is enabled or disabled for our provider.

Arguments:

    As per the ETW callback.

Return Value:

    None.
--*/

{
    //
    // Initialize locals.
    //
    UNREFERENCED_PARAMETER(SourceId);
    UNREFERENCED_PARAMETER(Level);
    UNREFERENCED_PARAMETER(MatchAnyKeyword);
    UNREFERENCED_PARAMETER(MatchAllKeyword);
    UNREFERENCED_PARAMETER(FilterData);
    UNREFERENCED_PARAMETER(CallbackContext);

    //
    // Set the ETW tracing enable state.
    //
    DiskETWEnabled = IsEnabled ? TRUE : FALSE;

    return;
}


Our Services

  • What our customers say about us?

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

Privacy Policy. Terms of use. Valid XHTML & CSS