- Commit "new" storage stack drivers support based on NT4 DDK's sample drivers (with...
[reactos.git] / reactos / drivers / storage / cdrom / cdrom.c
index f1ad1da..c522365 100644 (file)
 /*
- *  ReactOS kernel
- *  Copyright (C) 2001, 2002 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/*
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
- * FILE:            services/storage/cdrom/cdrom.c
- * PURPOSE:         cdrom class driver
- * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
+ * PROJECT:         ReactOS Storage Stack
+ * LICENSE:         DDK - see license.txt in the root dir
+ * FILE:            drivers/storage/cdrom/cdrom.c
+ * PURPOSE:         CDROM driver
+ * PROGRAMMERS:     Based on a source code sample from Microsoft NT4 DDK
  */
 
-/*
- * TODO:
- *  - Add io timer routine for autorun support.
- *  - Add cdaudio support (cd player).
- */
+#include <ntddk.h>
+#include <scsi.h>
+#include <ntddscsi.h>
+#include <ntdddisk.h>
+#include <ntddcdrm.h>
+#include <include/class2.h>
+#include <stdio.h>
+
+//#define NDEBUG
+#include <debug.h>
+
+#define CDB12GENERIC_LENGTH 12
+
+typedef struct _XA_CONTEXT {
+
+    //
+    // Pointer to the device object.
+    //
+
+    PDEVICE_OBJECT DeviceObject;
+
+    //
+    // Pointer to the original request when
+    // a mode select must be sent.
+    //
+
+    PIRP OriginalRequest;
+
+    //
+    // Pointer to the mode select srb.
+    //
+
+    PSCSI_REQUEST_BLOCK Srb;
+} XA_CONTEXT, *PXA_CONTEXT;
+
+typedef struct _ERROR_RECOVERY_DATA {
+    MODE_PARAMETER_HEADER   Header;
+    MODE_PARAMETER_BLOCK BlockDescriptor;
+    MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
+} ERROR_RECOVERY_DATA, *PERROR_RECOVERY_DATA;
+
+typedef struct _ERROR_RECOVERY_DATA10 {
+    MODE_PARAMETER_HEADER10 Header10;
+    MODE_PARAMETER_BLOCK BlockDescriptor10;
+    MODE_READ_RECOVERY_PAGE ReadRecoveryPage10;
+} ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10;
+
+//
+// CdRom specific addition to device extension.
+//
+
+typedef struct _CDROM_DATA {
+
+    //
+    // Indicates whether an audio play operation
+    // is currently being performed.
+    //
+
+    BOOLEAN PlayActive;
+
+    //
+    // Indicates whether the blocksize used for user data
+    // is 2048 or 2352.
+    //
+
+    BOOLEAN RawAccess;
+
+    //
+    // Indicates whether 6 or 10 byte mode sense/select
+    // should be used.
+    //
+
+    USHORT XAFlags;
+
+    //
+    // Storage for the error recovery page. This is used
+    // as an easy method to switch block sizes.
+    //
+
+    union {
+        ERROR_RECOVERY_DATA u1;
+        ERROR_RECOVERY_DATA10 u2;
+    };
+
+
+    //
+    // Pointer to the original irp for the raw read.
+    //
+
+    PIRP SavedReadIrp;
+
+    //
+    // Used to protect accesses to the RawAccess flag.
+    //
+
+    KSPIN_LOCK FormSpinLock;
+
+    //
+    // Even if media change support is requested, there are some devices
+    // that are not supported.  This flag will indicate that such a device
+    // is present when it is FALSE.
+    //
+
+    BOOLEAN MediaChangeSupported;
+
+    //
+    // The media change event is being supported.  The media change timer
+    // should be running whenever this is true.
+    //
+
+    BOOLEAN MediaChange;
+
+    //
+    // The timer value to support media change events.  This is a countdown
+    // value used to determine when to poll the device for a media change.
+    // The max value for the timer is 255 seconds.
+    //
+
+    UCHAR MediaChangeCountDown;
+
+#ifdef DBG
+    //
+    // Second timer to keep track of how long the media change IRP has been
+    // in use.  If this value exceeds the timeout (#defined) then we should
+    // print out a message to the user and set the MediaChangeIrpLost flag
+    //
+
+    SHORT MediaChangeIrpTimeInUse;
+
+    //
+    // Set by CdRomTickHandler when we determine that the media change irp has
+    // been lost
+    //
+
+    BOOLEAN MediaChangeIrpLost;
+#endif
+
+    UCHAR PadReserve; // use this for new flags.
+
+    //
+    // An IRP is allocated and kept for the duration that media change
+    // detection is in effect.  If this is NULL and MediaChange is TRUE,
+    // the detection is in progress.  This should always be NULL when
+    // MediaChange is FALSE.
+    //
+
+    PIRP MediaChangeIrp;
+
+    //
+    // The timer work list is a collection of IRPS that are prepared for
+    // submission, but need to allow some time to pass before they are
+    // run.
+    //
+
+    LIST_ENTRY TimerIrpList;
+    KSPIN_LOCK TimerIrpSpinLock;
+
+} CDROM_DATA, *PCDROM_DATA;
+
+#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA)
+#define SCSI_CDROM_TIMEOUT          10
+#define SCSI_CHANGER_BONUS_TIMEOUT  10
+#define HITACHI_MODE_DATA_SIZE      12
+#define MODE_DATA_SIZE              64
+#define RAW_SECTOR_SIZE           2352
+#define COOKED_SECTOR_SIZE        2048
+#define MEDIA_CHANGE_DEFAULT_TIME    4
+#define CDROM_SRB_LIST_SIZE          4
+
+
+#ifdef DBG
+
+//
+// Used to detect the loss of the autorun irp.  The driver prints out a message
+// (debug level 0) if this timeout ever occurs
+//
+#define MEDIA_CHANGE_TIMEOUT_TIME  300
+
+#endif
+
+#define PLAY_ACTIVE(DeviceExtension) (((PCDROM_DATA)(DeviceExtension + 1))->PlayActive)
+
+#define MSF_TO_LBA(Minutes,Seconds,Frames) \
+                (ULONG)((60 * 75 * (Minutes)) + (75 * (Seconds)) + ((Frames) - 150))
+
+#define LBA_TO_MSF(Lba,Minutes,Seconds,Frames)               \
+{                                                            \
+    (Minutes) = (UCHAR)(Lba  / (60 * 75));                   \
+    (Seconds) = (UCHAR)((Lba % (60 * 75)) / 75);             \
+    (Frames)  = (UCHAR)((Lba % (60 * 75)) % 75);             \
+}
+
+#define DEC_TO_BCD(x) (((x / 10) << 4) + (x % 10))
+
+//
+// Define flags for XA, CDDA, and Mode Select/Sense
+//
+
+#define XA_USE_6_BYTE             0x01
+#define XA_USE_10_BYTE            0x02
+#define XA_USE_READ_CD            0x04
+#define XA_NOT_SUPPORTED          0x08
+
+#define PLEXTOR_CDDA              0x10
+#define NEC_CDDA                  0x20
+
+//
+// Sector types for READ_CD
+//
+
+#define ANY_SECTOR                0
+#define CD_DA_SECTOR              1
+#define YELLOW_MODE1_SECTOR       2
+#define YELLOW_MODE2_SECTOR       3
+#define FORM2_MODE1_SECTOR        4
+#define FORM2_MODE2_SECTOR        5
+
+
+#ifdef POOL_TAGGING
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#endif
+#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'CscS')
+#endif
+
+\f
+NTSTATUS
+STDCALL
+DriverEntry(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PUNICODE_STRING RegistryPath
+    );
+
+BOOLEAN
+STDCALL
+ScsiCdRomFindDevices(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PUNICODE_STRING RegistryPath,
+    IN PCLASS_INIT_DATA InitializationData,
+    IN PDEVICE_OBJECT PortDeviceObject,
+    IN ULONG PortNumber
+    );
+
+NTSTATUS
+STDCALL
+ScsiCdRomOpenClose(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    );
+
+NTSTATUS
+STDCALL
+ScsiCdRomReadVerification(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    );
+
+NTSTATUS
+STDCALL
+ScsiCdRomSwitchMode(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG SectorSize,
+    IN PIRP  OriginalRequest
+    );
+
+NTSTATUS
+STDCALL
+CdRomDeviceControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    );
+
+NTSTATUS
+STDCALL
+CdRomDeviceControlCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    );
+
+NTSTATUS
+STDCALL
+CdRomSetVolumeIntermediateCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    );
+
+NTSTATUS
+STDCALL
+CdRomSwitchModeCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    );
+
+NTSTATUS
+STDCALL
+CdRomXACompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    );
+
+NTSTATUS
+STDCALL
+CdRomClassIoctlCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    );
+
+VOID
+STDCALL
+ScsiCdRomStartIo(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    );
+
+VOID
+STDCALL
+CdRomTickHandler(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PVOID Context
+    );
+
+BOOLEAN
+STDCALL
+CdRomCheckRegistryForMediaChangeValue(
+    IN PUNICODE_STRING RegistryPath,
+    IN ULONG DeviceNumber
+    );
+
+NTSTATUS
+STDCALL
+CdRomUpdateCapacity(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PIRP IrpToComplete,
+    IN OPTIONAL PKEVENT IoctlEvent
+    );
+
+NTSTATUS
+STDCALL
+CreateCdRomDeviceObject(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PDEVICE_OBJECT PortDeviceObject,
+    IN ULONG PortNumber,
+    IN PULONG DeviceCount,
+    PIO_SCSI_CAPABILITIES PortCapabilities,
+    IN PSCSI_INQUIRY_DATA LunInfo,
+    IN PCLASS_INIT_DATA   InitializationData,
+    IN PUNICODE_STRING    RegistryPath
+    );
+
+VOID
+STDCALL
+ScanForSpecial(
+    PDEVICE_OBJECT DeviceObject,
+    PINQUIRYDATA InquiryData,
+    PIO_SCSI_CAPABILITIES PortCapabilities
+    );
+
+BOOLEAN
+STDCALL
+CdRomIsPlayActive(
+    IN PDEVICE_OBJECT DeviceObject
+    );
+
+VOID
+STDCALL
+HitachProcessError(
+    PDEVICE_OBJECT DeviceObject,
+    PSCSI_REQUEST_BLOCK Srb,
+    NTSTATUS *Status,
+    BOOLEAN *Retry
+    );
+
+VOID
+STDCALL
+ToshibaProcessError(
+    PDEVICE_OBJECT DeviceObject,
+    PSCSI_REQUEST_BLOCK Srb,
+    NTSTATUS *Status,
+    BOOLEAN *Retry
+    );
+
+BOOLEAN
+STDCALL
+IsThisAnAtapiChanger(
+    IN  PDEVICE_OBJECT DeviceObject,
+    OUT PULONG         DiscsPresent
+    );
+
+BOOLEAN
+STDCALL
+IsThisASanyo(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  UCHAR          PathId,
+    IN  UCHAR          TargetId
+    );
+
+BOOLEAN
+STDCALL
+IsThisAMultiLunDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PDEVICE_OBJECT PortDeviceObject
+    );
+
+VOID
+STDCALL
+CdRomCreateNamedEvent(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN ULONG DeviceNumber
+    );
+
+#ifdef _PPC_
+NTSTATUS
+FindScsiAdapter (
+    IN HANDLE KeyHandle,
+    IN UNICODE_STRING ScsiUnicodeString[],
+    OUT PUCHAR IntermediateController
+    );
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, DriverEntry)
+#pragma alloc_text(PAGE, ScsiCdRomFindDevices)
+#pragma alloc_text(PAGE, CreateCdRomDeviceObject)
+#pragma alloc_text(PAGE, ScanForSpecial)
+//#pragma alloc_text(PAGE, CdRomDeviceControl)
+#pragma alloc_text(PAGE, HitachProcessError)
+#pragma alloc_text(PAGE, CdRomIsPlayActive)
+#pragma alloc_text(PAGE, ScsiCdRomReadVerification)
+#pragma alloc_text(INIT, CdRomCheckRegistryForMediaChangeValue)
+#pragma alloc_text(INIT, IsThisAnAtapiChanger)
+#pragma alloc_text(INIT, IsThisASanyo)
+#pragma alloc_text(INIT, IsThisAMultiLunDevice)
+#pragma alloc_text(INIT, CdRomCreateNamedEvent)
+#ifdef _PPC_
+#pragma alloc_text(PAGE, FindScsiAdapter)
+#endif
+#endif
+
+ULONG NoLoad = 0;
+
+\f
+NTSTATUS
+STDCALL
+DriverEntry(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PUNICODE_STRING RegistryPath
+    )
+
+/*++
+
+Routine Description:
+
+    This routine initializes the cdrom 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;
+
+    if(NoLoad) {
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    //
+    // Zero InitData
+    //
+
+    RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
+
+    //
+    // Set sizes
+    //
+
+    InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
+    InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
+
+    InitializationData.DeviceType = FILE_DEVICE_CD_ROM;
+    InitializationData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
+
+    //
+    // Set entry points
+    //
+
+    InitializationData.ClassReadWriteVerification = ScsiCdRomReadVerification;
+    InitializationData.ClassDeviceControl = CdRomDeviceControl;
+    InitializationData.ClassFindDevices = ScsiCdRomFindDevices;
+    InitializationData.ClassShutdownFlush = NULL;
+    InitializationData.ClassCreateClose = NULL;
+    InitializationData.ClassStartIo = ScsiCdRomStartIo;
+
+    //
+    // Call the class init routine
+    //
+
+    return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData);
+
+} // end DriverEntry()
+
+\f
+BOOLEAN
+STDCALL
+ScsiCdRomFindDevices(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PUNICODE_STRING RegistryPath,
+    IN PCLASS_INIT_DATA InitializationData,
+    IN PDEVICE_OBJECT PortDeviceObject,
+    IN ULONG PortNumber
+    )
+
+/*++
+
+Routine Description:
+
+    Connect to SCSI port driver. Get adapter capabilities and
+    SCSI bus configuration information. Search inquiry data
+    for CDROM devices to process.
+
+Arguments:
+
+    DriverObject - CDROM class driver object.
+    PortDeviceObject - SCSI port driver device object.
+    PortNumber - The system ordinal for this scsi adapter.
+
+Return Value:
+
+    TRUE if CDROM device present on this SCSI adapter.
+
+--*/
+
+{
+    PIO_SCSI_CAPABILITIES portCapabilities;
+    PULONG cdRomCount;
+    PCHAR buffer;
+    PSCSI_INQUIRY_DATA lunInfo;
+    PSCSI_ADAPTER_BUS_INFO  adapterInfo;
+    PINQUIRYDATA inquiryData;
+    ULONG scsiBus;
+    NTSTATUS status;
+    BOOLEAN foundDevice = FALSE;
+
+    //
+    // Call port driver to get adapter capabilities.
+    //
+
+    status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
+
+    if (!NT_SUCCESS(status)) {
+        DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
+        return foundDevice;
+    }
+
+    //
+    // Call port driver to get inquiry information to find cdroms.
+    //
+
+    status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
+
+    if (!NT_SUCCESS(status)) {
+        DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
+        return foundDevice;
+    }
+
+    //
+    // Get the address of the count of the number of cdroms already initialized.
+    //
+
+    cdRomCount = &IoGetConfigurationInformation()->CdRomCount;
+    adapterInfo = (PVOID) buffer;
+
+    //
+    // For each SCSI bus this adapter supports ...
+    //
+
+    for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {
+
+        //
+        // Get the SCSI bus scan data for this bus.
+        //
+
+        lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
+
+        //
+        // Search list for unclaimed disk devices.
+        //
+
+        while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
+
+            inquiryData = (PVOID)lunInfo->InquiryData;
+
+            if ((inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
+                (inquiryData->DeviceTypeQualifier == 0) &&
+                (!lunInfo->DeviceClaimed)) {
+
+                DebugPrint((1,"FindScsiDevices: Vendor string is %.24s\n",
+                            inquiryData->VendorId));
+
+                //
+                // Create device objects for cdrom
+                //
+
+                status = CreateCdRomDeviceObject(DriverObject,
+                                                 PortDeviceObject,
+                                                 PortNumber,
+                                                 cdRomCount,
+                                                 portCapabilities,
+                                                 lunInfo,
+                                                 InitializationData,
+                                                 RegistryPath);
+
+                if (NT_SUCCESS(status)) {
+
+                    //
+                    // Increment system cdrom device count.
+                    //
+
+                    (*cdRomCount)++;
+
+                    //
+                    // Indicate that a cdrom device was found.
+                    //
+
+                    foundDevice = TRUE;
+                }
+            }
+
+            //
+            // Get next LunInfo.
+            //
+
+            if (lunInfo->NextInquiryDataOffset == 0) {
+                break;
+            }
+
+            lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
+        }
+    }
+
+    ExFreePool(buffer);
+
+
+    return foundDevice;
+
+} // end FindScsiCdRoms()
+
+\f
+VOID
+STDCALL
+CdRomCreateNamedEvent(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN ULONG DeviceNumber
+    )
+
+/*++
+
+Routine Description:
+
+    Create the named synchronization event for notification of media change
+    events to the system.  The event is reset before this function returns.
+
+Arguments:
+
+    DeviceExtension - the device extension pointer for storage of the event pointer.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+    UNICODE_STRING    unicodeString;
+    OBJECT_ATTRIBUTES objectAttributes;
+    CCHAR             eventNameBuffer[MAXIMUM_FILENAME_LENGTH];
+    STRING            eventNameString;
+    HANDLE            handle;
+    NTSTATUS          status;
+
+
+    sprintf(eventNameBuffer,"\\Device\\MediaChangeEvent%d",
+            DeviceNumber);
+
+    RtlInitString(&eventNameString,
+                  eventNameBuffer);
+
+    status = RtlAnsiStringToUnicodeString(&unicodeString,
+                                          &eventNameString,
+                                          TRUE);
+
+    if (!NT_SUCCESS(status)) {
+        return;
+    }
+
+    InitializeObjectAttributes(&objectAttributes,
+                               &unicodeString,
+                               OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+                               NULL,
+                               NULL);
+
+    DeviceExtension->MediaChangeEvent = IoCreateSynchronizationEvent(&unicodeString,
+                                                                     &handle);
+    DeviceExtension->MediaChangeEventHandle = handle;
+
+    KeClearEvent(DeviceExtension->MediaChangeEvent);
+
+    RtlFreeUnicodeString(&unicodeString);
+}
+
+\f
+NTSTATUS
+STDCALL
+CreateCdRomDeviceObject(
+    IN PDRIVER_OBJECT DriverObject,
+    IN PDEVICE_OBJECT PortDeviceObject,
+    IN ULONG          PortNumber,
+    IN PULONG         DeviceCount,
+    IN PIO_SCSI_CAPABILITIES PortCapabilities,
+    IN PSCSI_INQUIRY_DATA LunInfo,
+    IN PCLASS_INIT_DATA   InitializationData,
+    IN PUNICODE_STRING    RegistryPath
+    )
+
+/*++
+
+Routine Description:
+
+    This routine creates an object for the device and then calls the
+    SCSI port driver for media capacity and sector size.
+
+Arguments:
+
+    DriverObject - Pointer to driver object created by system.
+    PortDeviceObject - to connect to SCSI port driver.
+    DeviceCount - Number of previously installed CDROMs.
+    PortCapabilities - Pointer to structure returned by SCSI port
+        driver describing adapter capabilites (and limitations).
+    LunInfo - Pointer to configuration information for this device.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+{
+    UCHAR ntNameBuffer[64];
+    STRING ntNameString;
+    UNICODE_STRING ntUnicodeString;
+    NTSTATUS status;
+    BOOLEAN changerDevice;
+    SCSI_REQUEST_BLOCK srb;
+    ULONG          length;
+    PCDROM_DATA    cddata;
+    PCDB           cdb;
+    PVOID          senseData = NULL;
+    PDEVICE_OBJECT deviceObject = NULL;
+    PDEVICE_EXTENSION deviceExtension = NULL;
+    PUCHAR         buffer;
+    ULONG          bps;
+    ULONG          lastBit;
+    ULONG          timeOut;
+    BOOLEAN        srbListInitialized = FALSE;
+
+    //
+    // Claim the device. Note that any errors after this
+    // will goto the generic handler, where the device will
+    // be released.
+    //
+
+    status = ScsiClassClaimDevice(PortDeviceObject,
+                                  LunInfo,
+                                  FALSE,
+                                  &PortDeviceObject);
+
+    if (!NT_SUCCESS(status)) {
+        return(status);
+    }
+
+    //
+    // Create device object for this device.
+    //
+
+    sprintf(ntNameBuffer,
+            "\\Device\\CdRom%d",
+            *DeviceCount);
+
+    status = ScsiClassCreateDeviceObject(DriverObject,
+                                         ntNameBuffer,
+                                         NULL,
+                                         &deviceObject,
+                                         InitializationData);
+
+    if (!NT_SUCCESS(status)) {
+        DebugPrint((1,"CreateCdRomDeviceObjects: Can not create device %s\n",
+                    ntNameBuffer));
+
+        RtlFreeUnicodeString(&ntUnicodeString);
+        goto CreateCdRomDeviceObjectExit;
+    }
+
+    //
+    // Indicate that IRPs should include MDLs.
+    //
+
+    deviceObject->Flags |= DO_DIRECT_IO;
+
+    //
+    // Set up required stack size in device object.
+    //
+
+    deviceObject->StackSize = PortDeviceObject->StackSize + 2;
+
+    deviceExtension = deviceObject->DeviceExtension;
+
+    //
+    // Allocate spinlock for split request completion.
+    //
+
+    KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
+
+    //
+    // This is the physical device.
+    //
+
+    deviceExtension->PhysicalDevice = deviceObject;
+
+    //
+    // Initialize lock count to zero. The lock count is used to
+    // disable the ejection mechanism when media is mounted.
+    //
+
+    deviceExtension->LockCount = 0;
+
+    //
+    // Save system cdrom number
+    //
+
+    deviceExtension->DeviceNumber = *DeviceCount;
+
+    //
+    // Copy port device object to device extension.
+    //
+
+    deviceExtension->PortDeviceObject = PortDeviceObject;
+
+    //
+    // Set the alignment requirements for the device based on the
+    // host adapter requirements
+    //
+
+    if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
+        deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
+    }
+
+    //
+    // Save address of port driver capabilities.
+    //
+
+    deviceExtension->PortCapabilities = PortCapabilities;
+
+    //
+    // Clear SRB flags.
+    //
+
+    deviceExtension->SrbFlags = 0;
+    deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+    //
+    // Allocate request sense buffer.
+    //
+
+    senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+
+    if (senseData == NULL) {
+
+        //
+        // The buffer cannot be allocated.
+        //
+
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto CreateCdRomDeviceObjectExit;
+    }
+
+    //
+    // Set the sense data pointer in the device extension.
+    //
+
+    deviceExtension->SenseData = senseData;
+
+    //
+    // CDROMs are not partitionable so starting offset is 0.
+    //
+
+    deviceExtension->StartingOffset.LowPart = 0;
+    deviceExtension->StartingOffset.HighPart = 0;
+
+    //
+    // Path/TargetId/LUN describes a device location on the SCSI bus.
+    // This information comes from the LunInfo buffer.
+    //
+
+    deviceExtension->PortNumber = (UCHAR)PortNumber;
+    deviceExtension->PathId = LunInfo->PathId;
+    deviceExtension->TargetId = LunInfo->TargetId;
+    deviceExtension->Lun = LunInfo->Lun;
+
+    //
+    // Set timeout value in seconds.
+    //
+
+    timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath);
+    if (timeOut) {
+        deviceExtension->TimeOutValue = timeOut;
+    } else {
+        deviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
+    }
+
+    //
+    // Build the lookaside list for srb's for the physical disk. Should only
+    // need a couple.
+    //
+
+    ScsiClassInitializeSrbLookasideList(deviceExtension,
+                                        CDROM_SRB_LIST_SIZE);
+
+    srbListInitialized = TRUE;
+
+    //
+    // Back pointer to device object.
+    //
+
+    deviceExtension->DeviceObject = deviceObject;
+
+    //
+    // Allocate buffer for drive geometry.
+    //
+
+    deviceExtension->DiskGeometry =
+        ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY));
+
+    if (deviceExtension->DiskGeometry == NULL) {
+
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto CreateCdRomDeviceObjectExit;
+    }
+
+    //
+    // Set up media change support defaults.
+    //
+
+    cddata = (PCDROM_DATA)(deviceExtension + 1);
+
+    KeInitializeSpinLock(&cddata->FormSpinLock);
+    KeInitializeSpinLock(&cddata->TimerIrpSpinLock);
+    InitializeListHead(&cddata->TimerIrpList);
+
+    cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
+    cddata->MediaChangeSupported = FALSE;
+    cddata->MediaChange = FALSE;
+
+    //
+    // Assume that there is initially no media in the device
+    // only notify upper layers if there is something there
+    //
+
+    deviceExtension->MediaChangeNoMedia = TRUE;
+    cddata->MediaChangeIrp = NULL;
+#ifdef DBG
+    cddata->MediaChangeIrpTimeInUse = 0;
+    cddata->MediaChangeIrpLost = FALSE;
+#endif
+
+    //
+    // Scan for Scsi controllers that require special processing.
+    //
+
+    ScanForSpecial(deviceObject,
+                   (PINQUIRYDATA) LunInfo->InquiryData,
+                   PortCapabilities);
+
+    //
+    // Do READ CAPACITY. This SCSI command
+    // returns the last sector address on the device
+    // and the bytes per sector.
+    // These are used to calculate the drive capacity
+    // in bytes.
+    //
+
+    status = ScsiClassReadDriveCapacity(deviceObject);
+    bps = deviceExtension->DiskGeometry->BytesPerSector;
+
+    if (!NT_SUCCESS(status) || !bps) {
+
+        DebugPrint((1,
+                "CreateCdRomDeviceObjects: Can't read capacity for device %s\n",
+                ntNameBuffer));
+
+        //
+        // Set disk geometry to default values (per ISO 9660).
+        //
+
+        bps = 2048;
+        deviceExtension->SectorShift = 11;
+        deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
+    } else {
+
+        //
+        // Insure that bytes per sector is a power of 2
+        // This corrects a problem with the HP 4020i CDR where it
+        // returns an incorrect number for bytes per sector.
+        //
+
+        lastBit = (ULONG) -1;
+        while (bps) {
+            lastBit++;
+            bps = bps >> 1;
+        }
+
+        bps = 1 << lastBit;
+    }
+    deviceExtension->DiskGeometry->BytesPerSector = bps;
+    DebugPrint((2, "CreateCdRomDeviceObject: Calc'd bps = %x\n", bps));
+
+    //
+    // Check to see if this is some sort of changer device
+    //
+
+    changerDevice = FALSE;
+
+    //
+    // Search for devices that have special requirements for media
+    // change support.
+    //
+
+    if (deviceExtension->Lun > 0) {
+        changerDevice = TRUE;
+    }
+
+    if (!changerDevice) {
+        changerDevice = IsThisASanyo(deviceObject, deviceExtension->PathId,
+                                     deviceExtension->TargetId);
+    }
+
+    if (!changerDevice) {
+        ULONG tmp;
+        changerDevice = IsThisAnAtapiChanger(deviceObject, &tmp);
+    }
+
+    if (!changerDevice) {
+        changerDevice = IsThisAMultiLunDevice(deviceObject, PortDeviceObject);
+    }
+
+    //
+    // If it is a changer device, increment the timeout to take platter-swapping
+    // time into account
+    //
+
+    if(changerDevice) {
+        deviceExtension->TimeOutValue += SCSI_CHANGER_BONUS_TIMEOUT;
+    }
+
+    //
+    // Create the media change named event.  If this succeeds then continue
+    // initializing the media change support data items.
+    //
+
+    CdRomCreateNamedEvent(deviceExtension,*DeviceCount);
+    if (deviceExtension->MediaChangeEvent) {
+
+        //
+        // If this is not a changer, get an IRP for the timer request
+        // and initialize the timer.
+        //
+
+        if (!changerDevice) {
+
+            //
+            // Not a changer device - continue with media change initialization.
+            // Determine if the user actually wants media change events.
+            //
+
+            if (CdRomCheckRegistryForMediaChangeValue(RegistryPath, *DeviceCount)) {
+                PIO_STACK_LOCATION irpStack;
+                PSCSI_REQUEST_BLOCK srb;
+                PIRP irp;
+
+                //
+                // User wants it - preallocate IRP and SRB.
+                //
+
+                irp = IoAllocateIrp((CCHAR)(deviceObject->StackSize+1),
+                                    FALSE);
+                if (irp) {
+                    PVOID buffer;
+
+                    srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
+                    buffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+
+                    if (srb && buffer) {
+                        PCDB cdb;
+
+                        //
+                        // All resources have been allocated set up the IRP.
+                        //
+
+                        IoSetNextIrpStackLocation(irp);
+                        irpStack = IoGetCurrentIrpStackLocation(irp);
+                        irpStack->DeviceObject = deviceObject;
+                        irpStack = IoGetNextIrpStackLocation(irp);
+                        cddata->MediaChangeIrp = irp;
+                        irpStack->Parameters.Scsi.Srb = srb;
+
+                        //
+                        // Initialize the SRB
+                        //
+
+                        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+                        srb->CdbLength = 6;
+                        srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
+                        srb->QueueTag = SP_UNTAGGED;
+                        srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+                        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+                        srb->PathId = deviceExtension->PathId;
+                        srb->TargetId = deviceExtension->TargetId;
+                        srb->Lun = deviceExtension->Lun;
+                        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+                        //
+                        // Initialize and set up the sense information buffer
+                        //
+
+                        RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
+                        srb->SenseInfoBuffer = buffer;
+                        srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+                        //
+                        // Initialize the CDB
+                        //
+
+                        cdb = (PCDB)&srb->Cdb[0];
+                        cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+                        cdb->CDB6GENERIC.LogicalUnitNumber = deviceExtension->Lun;
+
+                        //
+                        // It is ok to support media change events on this device.
+                        //
+
+                        cddata->MediaChangeSupported = TRUE;
+                        cddata->MediaChange = TRUE;
+
+                    } else {
+
+                        if (srb) {
+                            ExFreePool(srb);
+                        }
+                        if (buffer) {
+                            ExFreePool(buffer);
+                        }
+                        IoFreeIrp(irp);
+                    }
+                }
+            } else {
+                deviceExtension->MediaChangeEvent = NULL;
+            }
+        } else {
+            deviceExtension->MediaChangeEvent = NULL;
+        }
+    }
+
+    //
+    // Assume use of 6-byte mode sense/select for now.
+    //
+
+    cddata->XAFlags |= XA_USE_6_BYTE;
+
+    //
+    // Build and issue mode sense with Read error recovery page. This will be used to change
+    // block size in case of any raw reads (Mode 2, Form 2).
+    //
+
+    length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH);
+
+    RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+    srb.CdbLength = 6;
+    cdb = (PCDB)srb.Cdb;
+
+    //
+    // Set timeout value from device extension.
+    //
+
+    srb.TimeOutValue = deviceExtension->TimeOutValue;
+
+    //
+    // Build the MODE SENSE CDB. The data returned will be kept in the device extension
+    // and used to set block size.
+    //
+
+    cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+    cdb->MODE_SENSE.PageCode = 0x1;
+    cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
+
+    buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10));
+    if (!buffer) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto CreateCdRomDeviceObjectExit;
+    }
+
+    status = ScsiClassSendSrbSynchronous(deviceObject,
+                                         &srb,
+                                         buffer,
+                                         length,
+                                         FALSE);
+    if (!NT_SUCCESS(status)) {
+
+        //
+        // May be Atapi, try 10-byte.
+        //
+
+        length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10);
+
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        //
+        // Build the MODE SENSE CDB.
+        //
+
+        srb.CdbLength = 10;
+        cdb = (PCDB)srb.Cdb;
+
+        //
+        // Set timeout value from device extension.
+        //
+
+        srb.TimeOutValue = deviceExtension->TimeOutValue;
+
+        cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+        cdb->MODE_SENSE10.PageCode = 0x1;
+
+        cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8);
+        cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF);
+
+        status = ScsiClassSendSrbSynchronous(deviceObject,
+                                             &srb,
+                                             buffer,
+                                             length,
+                                             FALSE);
+        if (status == STATUS_DATA_OVERRUN) {
+
+            //
+            // Build and issue the ReadCd command to ensure that this device supports it.
+            //
+
+            RtlZeroMemory(cdb, 12);
+
+            cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
+
+            status = ScsiClassSendSrbSynchronous(deviceObject,
+                                                 &srb,
+                                                 NULL,
+                                                 0,
+                                                 FALSE);
+
+            //
+            // If the command wasn't rejected then support the READ_CD.
+            //
+
+            if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {
+
+                //
+                // Using Read CD precludes issueing a mode select to
+                // set the user data size. So, no buffer copy is
+                // necessary.
+                //
+
+                cddata->XAFlags &= ~XA_USE_6_BYTE;
+                cddata->XAFlags = XA_USE_READ_CD | XA_USE_10_BYTE;
+            } else {
+
+                RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10));
+                cddata->u1.Header.ModeDataLength = 0;
+
+                cddata->XAFlags &= ~XA_USE_6_BYTE;
+                cddata->XAFlags |= XA_USE_10_BYTE;
+            }
+
+        } else if (NT_SUCCESS(status)) {
+
+            RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10));
+            cddata->u1.Header.ModeDataLength = 0;
+
+            cddata->XAFlags &= ~XA_USE_6_BYTE;
+            cddata->XAFlags |= XA_USE_10_BYTE;
+
+        } else {
+            cddata->XAFlags |= XA_NOT_SUPPORTED;
+        }
+    } else {
+        RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA));
+        cddata->u1.Header.ModeDataLength = 0;
+    }
+
+    ExFreePool(buffer);
+
+    //
+    // Start the timer now regardless of if Autorun is enabled.
+    // The timer must run forever since IoStopTimer faults.
+    //
+
+    IoInitializeTimer(deviceObject, CdRomTickHandler, NULL);
+    IoStartTimer(deviceObject);
+
+    return(STATUS_SUCCESS);
+
+CreateCdRomDeviceObjectExit:
+
+    //
+    // Release the device since an error occured.
+    //
+
+    ScsiClassClaimDevice(PortDeviceObject,
+                         LunInfo,
+                         TRUE,
+                         NULL);
+
+    if (senseData != NULL) {
+        ExFreePool(senseData);
+    }
+
+    if (deviceExtension->DiskGeometry != NULL) {
+        ExFreePool(deviceExtension->DiskGeometry);
+    }
+
+    if (deviceObject != NULL) {
+        if (srbListInitialized) {
+            ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead);
+        }
+        IoDeleteDevice(deviceObject);
+    }
+
+
+    return status;
+
+} // end CreateCdRomDeviceObject()
+
+\f
+VOID
+STDCALL
+ScsiCdRomStartIo(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    )
+{
+
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+    PIO_STACK_LOCATION  nextIrpStack = IoGetNextIrpStackLocation(Irp);
+    PIO_STACK_LOCATION  irpStack;
+    PIRP                irp2 = NULL;
+    ULONG               transferPages;
+    ULONG               transferByteCount = currentIrpStack->Parameters.Read.Length;
+    ULONG               maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
+    LARGE_INTEGER       startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
+    PCDROM_DATA         cdData;
+    PSCSI_REQUEST_BLOCK srb = NULL;
+    PCDB                cdb;
+    PUCHAR              senseBuffer = NULL;
+    PVOID               dataBuffer;
+    NTSTATUS            status;
+    BOOLEAN             use6Byte;
+
+    //
+    // Mark IRP with status pending.
+    //
+
+    IoMarkIrpPending(Irp);
+
+    //
+    // If the flag is set in the device object, force a verify.
+    //
+
+    if (DeviceObject->Flags & DO_VERIFY_VOLUME) {
+        DebugPrint((2, "ScsiCdRomStartIo: [%lx] Volume needs verified\n", Irp));
+        if (!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
+
+            if (Irp->Tail.Overlay.Thread) {
+                IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+            }
+
+            Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] Calling UpdateCapcity - "
+                           "ioctl event = %lx\n",
+                        Irp,
+                        nextIrpStack->Parameters.Others.Argument1
+                      ));
+
+            //
+            // our device control dispatch routine stores an event in the next
+            // stack location to signal when startio has completed.  We need to
+            // pass this in so that the update capacity completion routine can
+            // set it rather than completing the Irp.
+            //
+
+            status = CdRomUpdateCapacity(deviceExtension,
+                                         Irp,
+                                         nextIrpStack->Parameters.Others.Argument1
+                                         );
+
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] UpdateCapacity returned %lx\n", Irp, status));
+            ASSERT(status == STATUS_PENDING);
+            return;
+        }
+    }
+
+    cdData = (PCDROM_DATA)(deviceExtension + 1);
+    use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
+
+    if (currentIrpStack->MajorFunction == IRP_MJ_READ) {
+
+        //
+        // Add partition byte offset to make starting byte relative to
+        // beginning of disk. In addition, add in skew for DM Driver, if any.
+        //
+
+        currentIrpStack->Parameters.Read.ByteOffset.QuadPart += (deviceExtension->StartingOffset.QuadPart);
+
+        //
+        // Calculate number of pages in this transfer.
+        //
+
+        transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
+                                                       currentIrpStack->Parameters.Read.Length);
+
+        //
+        // Check if request length is greater than the maximum number of
+        // bytes that the hardware can transfer.
+        //
+
+        if (cdData->RawAccess) {
+
+            ASSERT(!(cdData->XAFlags & XA_USE_READ_CD));
+
+            //
+            // Fire off a mode select to switch back to cooked sectors.
+            //
+
+            irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1),
+                                  FALSE);
+
+            if (!irp2) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
+            if (!srb) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+            cdb = (PCDB)srb->Cdb;
+
+            //
+            // Allocate sense buffer.
+            //
+
+            senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+
+            if (!senseBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Set up the irp.
+            //
+
+            IoSetNextIrpStackLocation(irp2);
+            irp2->IoStatus.Status = STATUS_SUCCESS;
+            irp2->IoStatus.Information = 0;
+            irp2->Flags = 0;
+            irp2->UserBuffer = NULL;
+
+            //
+            // Save the device object and irp in a private stack location.
+            //
+
+            irpStack = IoGetCurrentIrpStackLocation(irp2);
+            irpStack->DeviceObject = deviceExtension->DeviceObject;
+            irpStack->Parameters.Others.Argument2 = (PVOID) Irp;
+
+            //
+            // The retry count will be in the real Irp, as the retry logic will
+            // recreate our private irp.
+            //
+
+            if (!(nextIrpStack->Parameters.Others.Argument1)) {
+
+                //
+                // Only jam this in if it doesn't exist. The completion routines can
+                // call StartIo directly in the case of retries and resetting it will
+                // cause infinite loops.
+                //
+
+                nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+            }
+
+            //
+            // Construct the IRP stack for the lower level driver.
+            //
+
+            irpStack = IoGetNextIrpStackLocation(irp2);
+            irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+            irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+            irpStack->Parameters.Scsi.Srb = srb;
+
+            srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+            srb->PathId = deviceExtension->PathId;
+            srb->TargetId = deviceExtension->TargetId;
+            srb->Lun = deviceExtension->Lun;
+            srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+            srb->Cdb[1] |= deviceExtension->Lun << 5;
+            srb->SrbStatus = srb->ScsiStatus = 0;
+            srb->NextSrb = 0;
+            srb->OriginalRequest = irp2;
+            srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+            srb->SenseInfoBuffer = senseBuffer;
+
+            transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10);
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount );
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            transferByteCount,
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+
+            srb->DataBuffer = dataBuffer;
+
+            //
+            // Set the new block size in the descriptor.
+            //
+
+            cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF;
+            cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >>  8) & 0xFF;
+            cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF);
+
+            //
+            // Move error page into dataBuffer.
+            //
+
+            RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount);
+
+            //
+            // Build and send a mode select to switch into raw mode.
+            //
+
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT);
+            srb->DataTransferLength = transferByteCount;
+            srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
+
+            if (use6Byte) {
+                srb->CdbLength = 6;
+                cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+                cdb->MODE_SELECT.PFBit = 1;
+                cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount;
+            } else {
+
+                srb->CdbLength = 10;
+                cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
+                cdb->MODE_SELECT10.PFBit = 1;
+                cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8);
+                cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF);
+            }
+
+            //
+            // Update completion routine.
+            //
+
+            IoSetCompletionRoutine(irp2,
+                                   CdRomSwitchModeCompletion,
+                                   srb,
+                                   TRUE,
+                                   TRUE,
+                                   TRUE);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        if ((currentIrpStack->Parameters.Read.Length > maximumTransferLength) ||
+            (transferPages >
+                deviceExtension->PortCapabilities->MaximumPhysicalPages)) {
+
+            //
+            // Request needs to be split. Completion of each portion of the
+            // request will fire off the next portion. The final request will
+            // signal Io to send a new request.
+            //
+
+            transferPages =
+                deviceExtension->PortCapabilities->MaximumPhysicalPages - 1;
+
+            if(maximumTransferLength > transferPages << PAGE_SHIFT) {
+                maximumTransferLength = transferPages << PAGE_SHIFT;
+            }
+
+            //
+            // Check that the maximum transfer size is not zero
+            //
+
+            if(maximumTransferLength == 0) {
+                maximumTransferLength = PAGE_SIZE;
+            }
+
+            ScsiClassSplitRequest(DeviceObject, Irp, maximumTransferLength);
+            return;
+
+        } else {
+
+            //
+            // Build SRB and CDB for this IRP.
+            //
+
+            ScsiClassBuildRequest(DeviceObject, Irp);
+
+        }
+
+
+    } else if (currentIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+
+        //
+        // Allocate an irp, srb and associated structures.
+        //
+
+        irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1),
+                              FALSE);
+
+        if (!irp2) {
+            Irp->IoStatus.Information = 0;
+            Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+            IoStartNextPacket(DeviceObject, FALSE);
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+            return;
+        }
+
+        srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
+        if (!srb) {
+            Irp->IoStatus.Information = 0;
+            Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+            IoFreeIrp(irp2);
+            IoStartNextPacket(DeviceObject, FALSE);
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+            return;
+        }
+
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        cdb = (PCDB)srb->Cdb;
+
+        //
+        // Allocate sense buffer.
+        //
+
+        senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+
+        if (!senseBuffer) {
+            Irp->IoStatus.Information = 0;
+            Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+            ExFreePool(srb);
+            IoFreeIrp(irp2);
+            IoStartNextPacket(DeviceObject, FALSE);
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+            return;
+        }
+
+        //
+        // Set up the irp.
+        //
+
+        IoSetNextIrpStackLocation(irp2);
+        irp2->IoStatus.Status = STATUS_SUCCESS;
+        irp2->IoStatus.Information = 0;
+        irp2->Flags = 0;
+        irp2->UserBuffer = NULL;
+
+        //
+        // Save the device object and irp in a private stack location.
+        //
+
+        irpStack = IoGetCurrentIrpStackLocation(irp2);
+        irpStack->DeviceObject = deviceExtension->DeviceObject;
+        irpStack->Parameters.Others.Argument2 = (PVOID) Irp;
+
+        //
+        // The retry count will be in the real Irp, as the retry logic will
+        // recreate our private irp.
+        //
+
+        if (!(nextIrpStack->Parameters.Others.Argument1)) {
+
+            //
+            // Only jam this in if it doesn't exist. The completion routines can
+            // call StartIo directly in the case of retries and resetting it will
+            // cause infinite loops.
+            //
+
+            nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+        }
+
+        //
+        // Construct the IRP stack for the lower level driver.
+        //
+
+        irpStack = IoGetNextIrpStackLocation(irp2);
+        irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+        irpStack->Parameters.Scsi.Srb = srb;
+
+        IoSetCompletionRoutine(irp2,
+                               CdRomDeviceControlCompletion,
+                               srb,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+        //
+        // Setup those fields that are generic to all requests.
+        //
+
+        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+        srb->PathId = deviceExtension->PathId;
+        srb->TargetId = deviceExtension->TargetId;
+        srb->Lun = deviceExtension->Lun;
+        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->Cdb[1] |= deviceExtension->Lun << 5;
+        srb->SrbStatus = srb->ScsiStatus = 0;
+        srb->NextSrb = 0;
+        srb->OriginalRequest = irp2;
+        srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+        srb->SenseInfoBuffer = senseBuffer;
+
+        switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
+
+        case IOCTL_CDROM_RAW_READ: {
+
+            //
+            // Determine whether the drive is currently in raw or cooked mode,
+            // and which command to use to read the data.
+            //
+
+            if (!(cdData->XAFlags & XA_USE_READ_CD)) {
+
+                PRAW_READ_INFO rawReadInfo =
+                                   (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+                ULONG          maximumTransferLength;
+                ULONG          transferPages;
+
+                if (cdData->RawAccess) {
+
+                    ULONG  startingSector;
+                    UCHAR  min, sec, frame;
+
+                    //
+                    // Free the recently allocated irp, as we don't need it.
+                    //
+
+                    IoFreeIrp(irp2);
+
+                    cdb = (PCDB)srb->Cdb;
+                    RtlZeroMemory(cdb, 12);
+
+                    //
+                    // Calculate starting offset.
+                    //
+
+                    startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
+                    transferByteCount  = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
+                    maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
+                    transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
+                                                                   transferByteCount);
+
+                    //
+                    // Determine if request is within limits imposed by miniport.
+                    //
+
+                    if (transferByteCount > maximumTransferLength ||
+                        transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) {
+
+                        //
+                        // The claim is that this won't happen, and is backed up by
+                        // ActiveMovie usage, which does unbuffered XA reads of 0x18000, yet
+                        // we get only 4 sector requests.
+                        //
+
+
+                        Irp->IoStatus.Information = 0;
+                        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+                        IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                        ExFreePool(senseBuffer);
+                        ExFreePool(srb);
+                        IoStartNextPacket(DeviceObject, FALSE);
+                        return;
+
+                    }
+
+                    srb->OriginalRequest = Irp;
+                    srb->SrbFlags = deviceExtension->SrbFlags;
+                    srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+                    srb->DataTransferLength = transferByteCount;
+                    srb->TimeOutValue = deviceExtension->TimeOutValue;
+                    srb->CdbLength = 10;
+                    srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
+
+                    if (rawReadInfo->TrackMode == CDDA) {
+                        if (cdData->XAFlags & PLEXTOR_CDDA) {
+
+                            srb->CdbLength = 12;
+
+                            cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun;
+                            cdb->PLXTR_READ_CDDA.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                            cdb->PLXTR_READ_CDDA.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                            cdb->PLXTR_READ_CDDA.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                            cdb->PLXTR_READ_CDDA.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                            cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+                            cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8);
+                            cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0;
+                            cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0;
+
+                            cdb->PLXTR_READ_CDDA.SubCode = 0;
+                            cdb->PLXTR_READ_CDDA.OperationCode = 0xD8;
+
+                        } else if (cdData->XAFlags & NEC_CDDA) {
+
+                            cdb->NEC_READ_CDDA.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                            cdb->NEC_READ_CDDA.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                            cdb->NEC_READ_CDDA.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                            cdb->NEC_READ_CDDA.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                            cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+                            cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8);
+
+                            cdb->NEC_READ_CDDA.OperationCode = 0xD4;
+                        }
+                    } else {
+
+                        cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun;
+
+                        cdb->CDB10.TransferBlocksMsb  = (UCHAR) (rawReadInfo->SectorCount >> 8);
+                        cdb->CDB10.TransferBlocksLsb  = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+
+                        cdb->CDB10.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                        cdb->CDB10.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                        cdb->CDB10.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                        cdb->CDB10.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                        cdb->CDB10.OperationCode = SCSIOP_READ;
+                    }
+
+                    srb->SrbStatus = srb->ScsiStatus = 0;
+
+                    nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+                    nextIrpStack->Parameters.Scsi.Srb = srb;
+
+                    if (!(nextIrpStack->Parameters.Others.Argument1)) {
+
+                        //
+                        // Only jam this in if it doesn't exist. The completion routines can
+                        // call StartIo directly in the case of retries and resetting it will
+                        // cause infinite loops.
+                        //
+
+                        nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+                    }
+
+                    //
+                    // Set up IoCompletion routine address.
+                    //
+
+                    IoSetCompletionRoutine(Irp,
+                                           CdRomXACompletion,
+                                           srb,
+                                           TRUE,
+                                           TRUE,
+                                           TRUE);
+
+                    IoCallDriver(deviceExtension->PortDeviceObject, Irp);
+                    return;
+
+                } else {
+
+                    transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10);
+                    dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount );
+                    if (!dataBuffer) {
+                        Irp->IoStatus.Information = 0;
+                        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                        IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                        ExFreePool(senseBuffer);
+                        ExFreePool(srb);
+                        IoFreeIrp(irp2);
+                        IoStartNextPacket(DeviceObject, FALSE);
+                        return;
+
+                    }
+
+                    irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                                    transferByteCount,
+                                                    FALSE,
+                                                    FALSE,
+                                                    (PIRP) NULL);
+
+                    if (!irp2->MdlAddress) {
+                        Irp->IoStatus.Information = 0;
+                        Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                        IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                        ExFreePool(senseBuffer);
+                        ExFreePool(srb);
+                        ExFreePool(dataBuffer);
+                        IoFreeIrp(irp2);
+                        IoStartNextPacket(DeviceObject, FALSE);
+                        return;
+                    }
+
+                    //
+                    // Prepare the MDL
+                    //
+
+                    MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+
+                    srb->DataBuffer = dataBuffer;
+
+                    //
+                    // Set the new block size in the descriptor.
+                    //
+
+                    cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF;
+                    cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >>  8) & 0xFF;
+                    cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF);
+
+
+                    //
+                    // TODO: Set density code, based on operation
+                    //
+
+                    cdData->u1.BlockDescriptor.DensityCode = 0;
+
+
+                    //
+                    // Move error page into dataBuffer.
+                    //
+
+                    RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount);
+
+
+                    //
+                    // Build and send a mode select to switch into raw mode.
+                    //
+
+                    srb->SrbFlags = deviceExtension->SrbFlags;
+                    srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT);
+                    srb->DataTransferLength = transferByteCount;
+                    srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
+
+                    if (use6Byte) {
+                        srb->CdbLength = 6;
+                        cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+                        cdb->MODE_SELECT.PFBit = 1;
+                        cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount;
+                    } else {
+
+                        srb->CdbLength = 10;
+                        cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
+                        cdb->MODE_SELECT10.PFBit = 1;
+                        cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8);
+                        cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF);
+                    }
+
+                    //
+                    // Update completion routine.
+                    //
+
+                    IoSetCompletionRoutine(irp2,
+                                           CdRomSwitchModeCompletion,
+                                           srb,
+                                           TRUE,
+                                           TRUE,
+                                           TRUE);
+
+                }
+
+            } else {
+
+                PRAW_READ_INFO rawReadInfo =
+                                   (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+                ULONG  startingSector;
+
+                //
+                // Free the recently allocated irp, as we don't need it.
+                //
+
+                IoFreeIrp(irp2);
+
+                cdb = (PCDB)srb->Cdb;
+                RtlZeroMemory(cdb, 12);
+
+
+                //
+                // Calculate starting offset.
+                //
+
+                startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
+                transferByteCount  = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
+
+
+                srb->OriginalRequest = Irp;
+                srb->SrbFlags = deviceExtension->SrbFlags;
+                srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+                srb->DataTransferLength = transferByteCount;
+                srb->TimeOutValue = deviceExtension->TimeOutValue;
+                srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
+                srb->CdbLength = 12;
+                srb->SrbStatus = srb->ScsiStatus = 0;
+
+                //
+                // Fill in CDB fields.
+                //
+
+                cdb = (PCDB)srb->Cdb;
+
+
+                cdb->READ_CD.TransferBlocks[2]  = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+                cdb->READ_CD.TransferBlocks[1]  = (UCHAR) (rawReadInfo->SectorCount >> 8 );
+                cdb->READ_CD.TransferBlocks[0]  = (UCHAR) (rawReadInfo->SectorCount >> 16);
+
+
+                cdb->READ_CD.StartingLBA[3]  = (UCHAR) (startingSector & 0xFF);
+                cdb->READ_CD.StartingLBA[2]  = (UCHAR) ((startingSector >>  8));
+                cdb->READ_CD.StartingLBA[1]  = (UCHAR) ((startingSector >> 16));
+                cdb->READ_CD.StartingLBA[0]  = (UCHAR) ((startingSector >> 24));
+
+                //
+                // Setup cdb depending upon the sector type we want.
+                //
+
+                switch (rawReadInfo->TrackMode) {
+                case CDDA:
+
+                    cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR;
+                    cdb->READ_CD.IncludeUserData = 1;
+                    cdb->READ_CD.HeaderCode = 3;
+                    cdb->READ_CD.IncludeSyncData = 1;
+                    break;
+
+                case YellowMode2:
+
+                    cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR;
+                    cdb->READ_CD.IncludeUserData = 1;
+                    cdb->READ_CD.HeaderCode = 1;
+                    cdb->READ_CD.IncludeSyncData = 1;
+                    break;
+
+                case XAForm2:
+
+                    cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR;
+                    cdb->READ_CD.IncludeUserData = 1;
+                    cdb->READ_CD.HeaderCode = 3;
+                    cdb->READ_CD.IncludeSyncData = 1;
+                    break;
+
+                default:
+                    Irp->IoStatus.Information = 0;
+                    Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+                    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                    ExFreePool(senseBuffer);
+                    ExFreePool(srb);
+                    IoFreeIrp(irp2);
+                    IoStartNextPacket(DeviceObject, FALSE);
+                    DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+                    return;
+                }
+
+                cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
+
+                nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+                nextIrpStack->Parameters.Scsi.Srb = srb;
+
+                if (!(nextIrpStack->Parameters.Others.Argument1)) {
+
+                    //
+                    // Only jam this in if it doesn't exist. The completion routines can
+                    // call StartIo directly in the case of retries and resetting it will
+                    // cause infinite loops.
+                    //
+
+                    nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+                }
+
+                //
+                // Set up IoCompletion routine address.
+                //
+
+                IoSetCompletionRoutine(Irp,
+                                       CdRomXACompletion,
+                                       srb,
+                                       TRUE,
+                                       TRUE,
+                                       TRUE);
+
+                IoCallDriver(deviceExtension->PortDeviceObject, Irp);
+                return;
+
+            }
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
+
+            //
+            // Issue ReadCapacity to update device extension
+            // with information for current media.
+            //
+
+            DebugPrint((3,
+                        "CdRomStartIo: Get drive capacity\n"));
+
+            //
+            // setup remaining srb and cdb parameters.
+            //
+
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+            srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
+            srb->CdbLength = 10;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(READ_CAPACITY_DATA));
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+                return;
+
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            sizeof(READ_CAPACITY_DATA),
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+
+            srb->DataBuffer = dataBuffer;
+            cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_CHECK_VERIFY: {
+
+            //
+            // Since a test unit ready is about to be performed, reset the timer
+            // value to decrease the opportunities for it to race with this code.
+            //
+
+            cdData->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
+
+            //
+            // Set up the SRB/CDB
+            //
+
+            srb->CdbLength = 6;
+            cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+            srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            DebugPrint((2, "ScsiCdRomStartIo: [%lx] Sending CHECK_VERIFY irp %lx\n", Irp, irp2));
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_GET_LAST_SESSION:
+
+            //
+            // Set format to return first and last session numbers.
+            //
+
+            cdb->READ_TOC.Format = GET_LAST_SESSION;
+
+            //
+            // Fall through to READ TOC code.
+            //
+
+        case IOCTL_CDROM_READ_TOC: {
+
+
+            if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) {
+
+                //
+                // Use MSF addressing if not request for session information.
+                //
+
+                cdb->READ_TOC.Msf = CDB_USE_MSF;
+            }
+
+            //
+            // Set size of TOC structure.
+            //
+
+            transferByteCount =
+                currentIrpStack->Parameters.Read.Length >
+                    sizeof(CDROM_TOC) ? sizeof(CDROM_TOC):
+                    currentIrpStack->Parameters.Read.Length;
+
+            cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
+            cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
+
+            cdb->READ_TOC.Control = 0;
+
+            //
+            // Start at beginning of disc.
+            //
+
+            cdb->READ_TOC.StartingTrack = 0;
+
+            //
+            // setup remaining srb and cdb parameters.
+            //
+
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+            srb->DataTransferLength = transferByteCount;
+            srb->CdbLength = 10;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount);
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            transferByteCount,
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+
+            srb->DataBuffer = dataBuffer;
+            cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        case IOCTL_CDROM_PLAY_AUDIO_MSF: {
+
+            PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+            //
+            // Set up the SRB/CDB
+            //
+
+            srb->CdbLength = 10;
+            cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;
+
+            cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
+            cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
+            cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
+
+            cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
+            cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
+            cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
+
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        case IOCTL_CDROM_READ_Q_CHANNEL: {
+
+            PSUB_Q_CHANNEL_DATA userChannelData =
+                             Irp->AssociatedIrp.SystemBuffer;
+            PCDROM_SUB_Q_DATA_FORMAT inputBuffer =
+                             Irp->AssociatedIrp.SystemBuffer;
+
+            //
+            // Allocate buffer for subq channel information.
+            //
+
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
+                                     sizeof(SUB_Q_CHANNEL_DATA));
+
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            transferByteCount,
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+
+            srb->DataBuffer = dataBuffer;
+
+            //
+            // Always logical unit 0, but only use MSF addressing
+            // for IOCTL_CDROM_CURRENT_POSITION
+            //
+
+            if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
+                cdb->SUBCHANNEL.Msf = CDB_USE_MSF;
+
+            //
+            // Return subchannel data
+            //
+
+            cdb->SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;
+
+            //
+            // Specify format of informatin to return
+            //
+
+            cdb->SUBCHANNEL.Format = inputBuffer->Format;
+
+            //
+            // Specify which track to access (only used by Track ISRC reads)
+            //
+
+            if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC) {
+                cdb->SUBCHANNEL.TrackNumber = inputBuffer->Track;
+            }
+
+            //
+            // Set size of channel data -- however, this is dependent on
+            // what information we are requesting (which Format)
+            //
+
+            switch( inputBuffer->Format ) {
+
+                case IOCTL_CDROM_CURRENT_POSITION:
+                    transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
+                    break;
+
+                case IOCTL_CDROM_MEDIA_CATALOG:
+                    transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
+                    break;
+
+                case IOCTL_CDROM_TRACK_ISRC:
+                    transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
+                    break;
+            }
+
+            cdb->SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
+            cdb->SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount &  0xFF);
+            cdb->SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+            srb->DataTransferLength = transferByteCount;
+            srb->CdbLength = 10;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        case IOCTL_CDROM_PAUSE_AUDIO: {
+
+            cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
+            cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;
+
+            srb->CdbLength = 10;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_RESUME_AUDIO: {
+
+            cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
+            cdb->PAUSE_RESUME.Action = CDB_AUDIO_RESUME;
+
+            srb->CdbLength = 10;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_SEEK_AUDIO_MSF: {
+
+            PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
+            ULONG                 logicalBlockAddress;
+
+            logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F);
+
+            cdb->SEEK.OperationCode      = SCSIOP_SEEK;
+            cdb->SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
+            cdb->SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
+            cdb->SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
+            cdb->SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
+
+            srb->CdbLength                = 10;
+            srb->TimeOutValue             = deviceExtension->TimeOutValue;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        case IOCTL_CDROM_STOP_AUDIO: {
+
+            cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+            cdb->START_STOP.Immediate = 1;
+            cdb->START_STOP.Start = 0;
+            cdb->START_STOP.LoadEject = 0;
+
+            srb->CdbLength = 6;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+        }
+
+        case IOCTL_CDROM_GET_CONTROL: {
+
+            PAUDIO_OUTPUT audioOutput;
+            PCDROM_AUDIO_CONTROL audioControl = Irp->AssociatedIrp.SystemBuffer;
+
+            //
+            // Allocate buffer for volume control information.
+            //
+
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
+                                         MODE_DATA_SIZE);
+
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            MODE_DATA_SIZE,
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+            srb->DataBuffer = dataBuffer;
+
+            RtlZeroMemory(dataBuffer, MODE_DATA_SIZE);
+
+            //
+            // Setup for either 6 or 10 byte CDBs.
+            //
+
+            if (use6Byte) {
+
+                cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+                cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
+                cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
+
+                //
+                // Disable block descriptors.
+                //
+
+                cdb->MODE_SENSE.Dbd = TRUE;
+
+                srb->CdbLength = 6;
+            } else {
+
+                cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+                cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
+                cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
+                cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
+
+                //
+                // Disable block descriptors.
+                //
+
+                cdb->MODE_SENSE10.Dbd = TRUE;
+
+                srb->CdbLength = 10;
+            }
+
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->DataTransferLength = MODE_DATA_SIZE;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        case IOCTL_CDROM_GET_VOLUME:
+        case IOCTL_CDROM_SET_VOLUME: {
+
+            dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
+                                         MODE_DATA_SIZE);
+
+            if (!dataBuffer) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            irp2->MdlAddress = IoAllocateMdl(dataBuffer,
+                                            MODE_DATA_SIZE,
+                                            FALSE,
+                                            FALSE,
+                                            (PIRP) NULL);
+
+            if (!irp2->MdlAddress) {
+                Irp->IoStatus.Information = 0;
+                Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+                ExFreePool(senseBuffer);
+                ExFreePool(srb);
+                ExFreePool(dataBuffer);
+                IoFreeIrp(irp2);
+                IoStartNextPacket(DeviceObject, FALSE);
+                return;
+            }
+
+            //
+            // Prepare the MDL
+            //
+
+            MmBuildMdlForNonPagedPool(irp2->MdlAddress);
+            srb->DataBuffer = dataBuffer;
+
+            RtlZeroMemory(dataBuffer, MODE_DATA_SIZE);
+
+
+            if (use6Byte) {
+                cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+                cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
+                cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
+
+                srb->CdbLength = 6;
+
+            } else {
+
+                cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
+                cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
+                cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
+                cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
+
+                srb->CdbLength = 10;
+            }
+
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->DataTransferLength = MODE_DATA_SIZE;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+
+            if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_SET_VOLUME) {
+
+                //
+                // Setup a different completion routine as the mode sense data is needed in order
+                // to send the mode select.
+                //
+
+                IoSetCompletionRoutine(irp2,
+                                       CdRomSetVolumeIntermediateCompletion,
+                                       srb,
+                                       TRUE,
+                                       TRUE,
+                                       TRUE);
+
+            }
+
+            IoCallDriver(deviceExtension->PortDeviceObject, irp2);
+            return;
+
+        }
+
+        default:
+
+            //
+            // Just complete the request - CdRomClassIoctlCompletion will take
+            // care of it for us
+            //
+
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return;
+
+        } // end switch()
+    }
+
+    //
+    // If a read or an unhandled IRP_MJ_XX, end up here. The unhandled IRP_MJ's
+    // are expected and composed of AutoRun Irps, at present.
+    //
+
+    IoCallDriver(deviceExtension->PortDeviceObject, Irp);
+    return;
+}
+
+
+\f
+NTSTATUS
+STDCALL
+ScsiCdRomReadVerification(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    )
+
+/*++
+
+Routine Description:
+
+    This is the entry called by the I/O system for read requests.
+    It builds the SRB and sends it to the port driver.
+
+Arguments:
+
+    DeviceObject - the system object for the device.
+    Irp - IRP involved.
+
+Return Value:
+
+    NT Status
+
+--*/
+
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+    ULONG               transferByteCount = currentIrpStack->Parameters.Read.Length;
+    LARGE_INTEGER       startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
+    PCDROM_DATA         cdData = (PCDROM_DATA)(deviceExtension+1);
+    KIRQL               irql;
+    SCSI_REQUEST_BLOCK  srb;
+    PCDB                cdb = (PCDB)srb.Cdb;
+    NTSTATUS            status;
+
+    //
+    // If the cd is playing music then reject this request.
+    //
+
+    if (PLAY_ACTIVE(deviceExtension)) {
+        Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
+        return STATUS_DEVICE_BUSY;
+    }
+
+    //
+    // Verify parameters of this request.
+    // Check that ending sector is on disc and
+    // that number of bytes to transfer is a multiple of
+    // the sector size.
+    //
+
+    startingOffset.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart +
+                              transferByteCount;
+
+    if (!deviceExtension->DiskGeometry->BytesPerSector) {
+        deviceExtension->DiskGeometry->BytesPerSector = 2048;
+    }
+
+    if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) ||
+        (transferByteCount & deviceExtension->DiskGeometry->BytesPerSector - 1)) {
+
+        DebugPrint((1,"ScsiCdRomRead: Invalid I/O parameters\n"));
+        DebugPrint((1, "\toffset %x:%x, Length %x:%x\n",
+                    startingOffset.u.HighPart,
+                    startingOffset.u.LowPart,
+                    deviceExtension->PartitionLength.u.HighPart,
+                    deviceExtension->PartitionLength.u.LowPart));
+        DebugPrint((1, "\tbps %x\n", deviceExtension->DiskGeometry->BytesPerSector));
+
+        //
+        // Fail request with status of invalid parameters.
+        //
+
+        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+        return STATUS_INVALID_PARAMETER;
+    }
+
+
+    return STATUS_SUCCESS;
+
+} // end ScsiCdRomReadVerification()
+
+
+\f
+NTSTATUS
+STDCALL
+CdRomDeviceControlCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    )
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PDEVICE_EXTENSION   physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension;
+    PIO_STACK_LOCATION  irpStack        = IoGetCurrentIrpStackLocation(Irp);
+    PCDROM_DATA         cdData = (PCDROM_DATA)(deviceExtension + 1);
+    BOOLEAN             use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
+    PIO_STACK_LOCATION  realIrpStack;
+    PIO_STACK_LOCATION  realIrpNextStack;
+    PSCSI_REQUEST_BLOCK srb     = Context;
+    PIRP                realIrp = NULL;
+    NTSTATUS            status;
+    BOOLEAN             retry;
+
+    //
+    // Extract the 'real' irp from the irpstack.
+    //
+
+    realIrp = (PIRP) irpStack->Parameters.Others.Argument2;
+    realIrpStack = IoGetCurrentIrpStackLocation(realIrp);
+    realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
+
+    //
+    // Check SRB status for success of completing request.
+    //
+
+    if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+        DebugPrint((2,
+                    "CdRomDeviceControlCompletion: Irp %lx, Srb %lx Real Irp %lx Status %lx\n",
+                    Irp,
+                    srb,
+                    realIrp,
+                    srb->SrbStatus));
+
+        //
+        // Release the queue if it is frozen.
+        //
+
+        if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+            DebugPrint((2, "CdRomDeviceControlCompletion: Releasing Queue\n"));
+            ScsiClassReleaseQueue(DeviceObject);
+        }
+
+
+        retry = ScsiClassInterpretSenseInfo(DeviceObject,
+                                            srb,
+                                            irpStack->MajorFunction,
+                                            irpStack->Parameters.DeviceIoControl.IoControlCode,
+                                            MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1),
+                                            &status);
+
+        DebugPrint((2, "CdRomDeviceControlCompletion: IRP will %sbe retried\n",
+                    (retry ? "" : "not ")));
+
+        //
+        // Some of the Device Controls need special cases on non-Success status's.
+        //
+
+        if (realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+            if ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) ||
+                (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC)         ||
+                (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_CONTROL)      ||
+                (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)) {
+
+                if (status == STATUS_DATA_OVERRUN) {
+                    status = STATUS_SUCCESS;
+                    retry = FALSE;
+                }
+            }
+
+            if (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) {
+                PLAY_ACTIVE(deviceExtension) = FALSE;
+            }
+        }
+
+        //
+        // If the status is verified required and the this request
+        // should bypass verify required then retry the request.
+        //
+
+        if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+            status == STATUS_VERIFY_REQUIRED) {
+
+            if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ||
+                (realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)) &&
+                (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY)) {
+
+                //
+                // Update the geometry information, as the media could have changed.
+                // The completion routine for this will complete the real irp and start
+                // the next packet.
+                //
+
+                status = CdRomUpdateCapacity(deviceExtension,realIrp, NULL);
+                DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] CdRomUpdateCapacity completed with status %lx\n", realIrp, status));
+                ASSERT(status == STATUS_PENDING);
+
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            } else {
+
+                status = STATUS_IO_DEVICE_ERROR;
+                retry = TRUE;
+            }
+
+        }
+
+        if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) {
+
+
+            if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) {
+
+                //
+                // Retry request.
+                //
+
+                DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp));
+
+
+                ExFreePool(srb->SenseInfoBuffer);
+                if (srb->DataBuffer) {
+                    ExFreePool(srb->DataBuffer);
+                }
+                ExFreePool(srb);
+                if (Irp->MdlAddress) {
+                    IoFreeMdl(Irp->MdlAddress);
+                }
+
+                IoFreeIrp(Irp);
+
+                //
+                // Call StartIo directly since IoStartNextPacket hasn't been called,
+                // the serialisation is still intact.
+                //
+
+                ScsiCdRomStartIo(DeviceObject, realIrp);
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            }
+
+            //
+            // Exhausted retries. Fall through and complete the request with the appropriate status.
+            //
+
+        }
+    } else {
+
+        //
+        // Set status for successful request.
+        //
+
+        status = STATUS_SUCCESS;
+    }
+
+    if (NT_SUCCESS(status)) {
+
+        switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) {
+
+        case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
+
+            PREAD_CAPACITY_DATA readCapacityBuffer = srb->DataBuffer;
+            ULONG               lastSector;
+            ULONG               bps;
+            ULONG               lastBit;
+            ULONG               tmp;
+
+            //
+            // Swizzle bytes from Read Capacity and translate into
+            // the necessary geometry information in the device extension.
+            //
+
+            tmp = readCapacityBuffer->BytesPerBlock;
+            ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+            ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+            ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+            ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+
+            //
+            // Insure that bps is a power of 2.
+            // This corrects a problem with the HP 4020i CDR where it
+            // returns an incorrect number for bytes per sector.
+            //
+
+            if (!bps) {
+                bps = 2048;
+            } else {
+                lastBit = (ULONG) -1;
+                while (bps) {
+                    lastBit++;
+                    bps = bps >> 1;
+                }
+
+                bps = 1 << lastBit;
+            }
+            deviceExtension->DiskGeometry->BytesPerSector = bps;
+
+            DebugPrint((2,
+                        "CdRomDeviceControlCompletion: Calculated bps %#x\n",
+                        deviceExtension->DiskGeometry->BytesPerSector));
+
+            //
+            // Copy last sector in reverse byte order.
+            //
+
+            tmp = readCapacityBuffer->LogicalBlockAddress;
+            ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+            ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+            ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+            ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+
+            //
+            // Calculate sector to byte shift.
+            //
+
+            WHICH_BIT(bps, deviceExtension->SectorShift);
+
+            DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n",
+                deviceExtension->DiskGeometry->BytesPerSector));
+
+            DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n",
+                lastSector + 1));
+
+            //
+            // Calculate media capacity in bytes.
+            //
+
+            deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
+
+            //
+            // Calculate number of cylinders.
+            //
+
+            deviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
+
+            deviceExtension->PartitionLength.QuadPart =
+                (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
+
+            if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
+
+                //
+                // This device supports removable media.
+                //
+
+                deviceExtension->DiskGeometry->MediaType = RemovableMedia;
+
+            } else {
+
+                //
+                // Assume media type is fixed disk.
+                //
+
+                deviceExtension->DiskGeometry->MediaType = FixedMedia;
+            }
+
+            //
+            // Assume sectors per track are 32;
+            //
+
+            deviceExtension->DiskGeometry->SectorsPerTrack = 32;
+
+            //
+            // Assume tracks per cylinder (number of heads) is 64.
+            //
+
+            deviceExtension->DiskGeometry->TracksPerCylinder = 64;
+
+            //
+            // Copy the device extension's geometry info into the user buffer.
+            //
+
+            RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
+                          deviceExtension->DiskGeometry,
+                          sizeof(DISK_GEOMETRY));
+
+            //
+            // update information field.
+            //
+
+            realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+            break;
+        }
+
+        case IOCTL_CDROM_CHECK_VERIFY:
+
+            if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) &&
+               (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) {
+
+                *((PULONG)realIrp->AssociatedIrp.SystemBuffer) =
+                    physicalExtension->MediaChangeCount;
+                realIrp->IoStatus.Information = sizeof(ULONG);
+            } else {
+                realIrp->IoStatus.Information = 0;
+            }
+
+            DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] completing CHECK_VERIFY buddy irp %lx\n", realIrp, Irp));
+            break;
+
+        case IOCTL_CDROM_GET_LAST_SESSION:
+        case IOCTL_CDROM_READ_TOC: {
+
+                PCDROM_TOC toc = srb->DataBuffer;
+
+                //
+                // Copy the device extension's geometry info into the user buffer.
+                //
+
+                RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
+                              toc,
+                              srb->DataTransferLength);
+
+                //
+                // update information field.
+                //
+
+                realIrp->IoStatus.Information = srb->DataTransferLength;
+                break;
+            }
+
+        case IOCTL_CDROM_PLAY_AUDIO_MSF:
+
+            PLAY_ACTIVE(deviceExtension) = TRUE;
+
+            break;
+
+        case IOCTL_CDROM_READ_Q_CHANNEL: {
+
+            PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer;
+            PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer;
+            PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer;
+
+#if DBG
+            switch( inputBuffer->Format ) {
+
+            case IOCTL_CDROM_CURRENT_POSITION:
+                DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
+                break;
+
+            case IOCTL_CDROM_MEDIA_CATALOG:
+                DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
+                break;
+
+            case IOCTL_CDROM_TRACK_ISRC:
+                DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
+                DebugPrint((2,"CdRomDeviceControlCompletion: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
+                break;
+
+            }
+#endif
+
+            //
+            // Update the play active status.
+            //
+
+            if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
+
+                PLAY_ACTIVE(deviceExtension) = TRUE;
+
+            } else {
+
+                PLAY_ACTIVE(deviceExtension) = FALSE;
+
+            }
+
+            //
+            // Check if output buffer is large enough to contain
+            // the data.
+            //
+
+            if (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
+                srb->DataTransferLength) {
+
+                srb->DataTransferLength =
+                    realIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+            }
+
+            //
+            // Copy our buffer into users.
+            //
+
+            RtlMoveMemory(userChannelData,
+                          subQPtr,
+                          srb->DataTransferLength);
+
+            realIrp->IoStatus.Information = srb->DataTransferLength;
+            break;
+        }
+
+        case IOCTL_CDROM_PAUSE_AUDIO:
+
+            PLAY_ACTIVE(deviceExtension) = FALSE;
+            realIrp->IoStatus.Information = 0;
+            break;
+
+        case IOCTL_CDROM_RESUME_AUDIO:
+
+            realIrp->IoStatus.Information = 0;
+            break;
+
+        case IOCTL_CDROM_SEEK_AUDIO_MSF:
+
+            realIrp->IoStatus.Information = 0;
+            break;
+
+        case IOCTL_CDROM_STOP_AUDIO:
+
+            PLAY_ACTIVE(deviceExtension) = FALSE;
+
+            realIrp->IoStatus.Information = 0;
+            break;
+
+        case IOCTL_CDROM_GET_CONTROL: {
+
+            PCDROM_AUDIO_CONTROL audioControl = srb->DataBuffer;
+            PAUDIO_OUTPUT        audioOutput;
+            ULONG                bytesTransferred;
+
+            audioOutput = ScsiClassFindModePage((PCHAR)audioControl,
+                                                srb->DataTransferLength,
+                                                CDROM_AUDIO_CONTROL_PAGE,
+                                                use6Byte);
+            //
+            // Verify the page is as big as expected.
+            //
+
+            bytesTransferred = (PCHAR) audioOutput - (PCHAR) audioControl +
+                               sizeof(AUDIO_OUTPUT);
+
+            if (audioOutput != NULL &&
+                srb->DataTransferLength >= bytesTransferred) {
+
+                audioControl->LbaFormat = audioOutput->LbaFormat;
+
+                audioControl->LogicalBlocksPerSecond =
+                    (audioOutput->LogicalBlocksPerSecond[0] << (UCHAR)8) |
+                    audioOutput->LogicalBlocksPerSecond[1];
+
+                realIrp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL);
+
+            } else {
+                realIrp->IoStatus.Information = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+            break;
+        }
+
+        case IOCTL_CDROM_GET_VOLUME: {
+
+            PAUDIO_OUTPUT audioOutput;
+            PVOLUME_CONTROL volumeControl = srb->DataBuffer;
+            ULONG i,bytesTransferred;
+
+            audioOutput = ScsiClassFindModePage((PCHAR)volumeControl,
+                                                 srb->DataTransferLength,
+                                                 CDROM_AUDIO_CONTROL_PAGE,
+                                                 use6Byte);
+
+            //
+            // Verify the page is as big as expected.
+            //
+
+            bytesTransferred = (PCHAR) audioOutput - (PCHAR) volumeControl +
+                               sizeof(AUDIO_OUTPUT);
+
+            if (audioOutput != NULL &&
+                srb->DataTransferLength >= bytesTransferred) {
+
+                for (i=0; i<4; i++) {
+                    volumeControl->PortVolume[i] =
+                        audioOutput->PortOutput[i].Volume;
+                }
+
+                //
+                // Set bytes transferred in IRP.
+                //
+
+                realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL);
+
+            } else {
+                realIrp->IoStatus.Information = 0;
+                status = STATUS_INVALID_DEVICE_REQUEST;
+            }
+
+            break;
+        }
+
+        case IOCTL_CDROM_SET_VOLUME:
+
+            realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL);
+            break;
+
+        default:
+
+            ASSERT(FALSE);
+            realIrp->IoStatus.Information = 0;
+            status = STATUS_INVALID_DEVICE_REQUEST;
+
+        } // end switch()
+    }
+
+    //
+    // Deallocate srb and sense buffer.
+    //
+
+    if (srb) {
+        if (srb->DataBuffer) {
+            ExFreePool(srb->DataBuffer);
+        }
+        if (srb->SenseInfoBuffer) {
+            ExFreePool(srb->SenseInfoBuffer);
+        }
+        ExFreePool(srb);
+    }
+
+    if (realIrp->PendingReturned) {
+        IoMarkIrpPending(realIrp);
+    }
+
+    if (Irp->MdlAddress) {
+        IoFreeMdl(Irp->MdlAddress);
+    }
+
+    IoFreeIrp(Irp);
+
+    //
+    // Set status in completing IRP.
+    //
+
+    realIrp->IoStatus.Status = status;
+
+    //
+    // Set the hard error if necessary.
+    //
+
+    if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
+
+        //
+        // Store DeviceObject for filesystem, and clear
+        // in IoStatus.Information field.
+        //
+
+        DebugPrint((1, "CdRomDeviceCompletion - Setting Hard Error on realIrp %lx\n",
+                    realIrp));
+        IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
+        realIrp->IoStatus.Information = 0;
+    }
+
+    IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+
+    IoStartNextPacket(DeviceObject, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+\f
+NTSTATUS
+STDCALL
+CdRomSetVolumeIntermediateCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    )
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION  irpStack        = IoGetCurrentIrpStackLocation(Irp);
+    PCDROM_DATA         cdData = (PCDROM_DATA)(deviceExtension + 1);
+    BOOLEAN             use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
+    PIO_STACK_LOCATION  realIrpStack;
+    PIO_STACK_LOCATION  realIrpNextStack;
+    PSCSI_REQUEST_BLOCK srb     = Context;
+    PIRP                realIrp = NULL;
+    NTSTATUS            status;
+    BOOLEAN             retry;
+
+    //
+    // Extract the 'real' irp from the irpstack.
+    //
+
+    realIrp = (PIRP) irpStack->Parameters.Others.Argument2;
+    realIrpStack = IoGetCurrentIrpStackLocation(realIrp);
+    realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
+
+    //
+    // Check SRB status for success of completing request.
+    //
+
+    if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+        DebugPrint((2,
+                    "CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n",
+                    Irp,
+                    srb,
+                    realIrp));
+
+        //
+        // Release the queue if it is frozen.
+        //
+
+        if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+            ScsiClassReleaseQueue(DeviceObject);
+        }
+
+
+        retry = ScsiClassInterpretSenseInfo(DeviceObject,
+                                            srb,
+                                            irpStack->MajorFunction,
+                                            irpStack->Parameters.DeviceIoControl.IoControlCode,
+                                            MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1),
+                                            &status);
+
+        if (status == STATUS_DATA_OVERRUN) {
+            status = STATUS_SUCCESS;
+            retry = FALSE;
+        }
+
+        //
+        // If the status is verified required and the this request
+        // should bypass verify required then retry the request.
+        //
+
+        if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+            status == STATUS_VERIFY_REQUIRED) {
+
+            status = STATUS_IO_DEVICE_ERROR;
+            retry = TRUE;
+        }
+
+        if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) {
+
+            if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) {
+
+                //
+                // Retry request.
+                //
+
+                DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp));
+
+
+                ExFreePool(srb->SenseInfoBuffer);
+                ExFreePool(srb->DataBuffer);
+                ExFreePool(srb);
+                if (Irp->MdlAddress) {
+                    IoFreeMdl(Irp->MdlAddress);
+                }
+
+                IoFreeIrp(Irp);
+
+                //
+                // Call StartIo directly since IoStartNextPacket hasn't been called,
+                // the serialisation is still intact.
+                //
+                ScsiCdRomStartIo(DeviceObject, realIrp);
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            }
+
+            //
+            // Exhausted retries. Fall through and complete the request with the appropriate status.
+            //
+
+        }
+    } else {
+
+        //
+        // Set status for successful request.
+        //
+
+        status = STATUS_SUCCESS;
+    }
+
+    if (NT_SUCCESS(status)) {
+
+        PAUDIO_OUTPUT   audioInput = NULL;
+        PAUDIO_OUTPUT   audioOutput;
+        PVOLUME_CONTROL volumeControl = realIrp->AssociatedIrp.SystemBuffer;
+        ULONG           i,bytesTransferred,headerLength;
+        PVOID           dataBuffer;
+        PCDB            cdb;
+
+        audioInput = ScsiClassFindModePage((PCHAR)srb->DataBuffer,
+                                             srb->DataTransferLength,
+                                             CDROM_AUDIO_CONTROL_PAGE,
+                                             use6Byte);
+
+        //
+        // Check to make sure the mode sense data is valid before we go on
+        //
+
+        if(audioInput == NULL) {
+
+            DebugPrint((1, "Mode Sense Page %d not found\n",
+                        CDROM_AUDIO_CONTROL_PAGE));
+
+            realIrp->IoStatus.Information = 0;
+            realIrp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+            IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+            ExFreePool(srb->SenseInfoBuffer);
+            ExFreePool(srb);
+            IoFreeMdl(Irp->MdlAddress);
+            IoFreeIrp(Irp);
+            return STATUS_MORE_PROCESSING_REQUIRED;
+        }
+
+        if (use6Byte) {
+            headerLength = sizeof(MODE_PARAMETER_HEADER);
+        } else {
+            headerLength = sizeof(MODE_PARAMETER_HEADER10);
+        }
+
+        bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;
+
+        //
+        // Allocate a new buffer for the mode select.
+        //
+
+        dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, bytesTransferred);
+
+        if (!dataBuffer) {
+            realIrp->IoStatus.Information = 0;
+            realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+            IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+            ExFreePool(srb->SenseInfoBuffer);
+            ExFreePool(srb);
+            IoFreeMdl(Irp->MdlAddress);
+            IoFreeIrp(Irp);
+            return STATUS_MORE_PROCESSING_REQUIRED;
+        }
+
+        RtlZeroMemory(dataBuffer, bytesTransferred);
+
+        //
+        // Rebuild the data buffer to include the user requested values.
+        //
+
+        audioOutput = (PAUDIO_OUTPUT) ((PCHAR) dataBuffer + headerLength);
+
+        for (i=0; i<4; i++) {
+            audioOutput->PortOutput[i].Volume =
+                volumeControl->PortVolume[i];
+            audioOutput->PortOutput[i].ChannelSelection =
+                audioInput->PortOutput[i].ChannelSelection;
+        }
+
+        audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
+        audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
+        audioOutput->Immediate = MODE_SELECT_IMMEDIATE;
+
+        //
+        // Free the old data buffer, mdl.
+        //
+
+        ExFreePool(srb->DataBuffer);
+        IoFreeMdl(Irp->MdlAddress);
+
+        //
+        // rebuild the srb.
+        //
+
+        cdb = (PCDB)srb->Cdb;
+        RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
+
+        srb->SrbStatus = srb->ScsiStatus = 0;
+        srb->SrbFlags = deviceExtension->SrbFlags;
+        srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT);
+        srb->DataTransferLength = bytesTransferred;
+
+        if (use6Byte) {
+
+            cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+            cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred;
+            cdb->MODE_SELECT.PFBit = 1;
+            srb->CdbLength = 6;
+        } else {
+
+            cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
+            cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8);
+            cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF);
+            cdb->MODE_SELECT10.PFBit = 1;
+            srb->CdbLength = 10;
+        }
+
+        //
+        // Prepare the MDL
+        //
+
+        Irp->MdlAddress = IoAllocateMdl(dataBuffer,
+                                        bytesTransferred,
+                                        FALSE,
+                                        FALSE,
+                                        (PIRP) NULL);
+
+        if (!Irp->MdlAddress) {
+            realIrp->IoStatus.Information = 0;
+            realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+            IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+            ExFreePool(srb->SenseInfoBuffer);
+            ExFreePool(srb);
+            ExFreePool(dataBuffer);
+            IoFreeIrp(Irp);
+            return STATUS_MORE_PROCESSING_REQUIRED;
+
+        }
+
+        MmBuildMdlForNonPagedPool(Irp->MdlAddress);
+        srb->DataBuffer = dataBuffer;
+
+        irpStack = IoGetNextIrpStackLocation(Irp);
+        irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+        irpStack->Parameters.Scsi.Srb = srb;
+
+        //
+        // reset the irp completion.
+        //
+
+        IoSetCompletionRoutine(Irp,
+                               CdRomDeviceControlCompletion,
+                               srb,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+        //
+        // Call the port driver.
+        //
+
+        IoCallDriver(deviceExtension->PortDeviceObject, Irp);
+
+        return STATUS_MORE_PROCESSING_REQUIRED;
+    }
+
+    //
+    // Deallocate srb and sense buffer.
+    //
+
+    if (srb) {
+        if (srb->DataBuffer) {
+            ExFreePool(srb->DataBuffer);
+        }
+        if (srb->SenseInfoBuffer) {
+            ExFreePool(srb->SenseInfoBuffer);
+        }
+        ExFreePool(srb);
+    }
+
+    if (Irp->PendingReturned) {
+      IoMarkIrpPending(Irp);
+    }
+
+    if (realIrp->PendingReturned) {
+        IoMarkIrpPending(realIrp);
+    }
+
+    if (Irp->MdlAddress) {
+        IoFreeMdl(Irp->MdlAddress);
+    }
+
+    IoFreeIrp(Irp);
+
+    //
+    // Set status in completing IRP.
+    //
+
+    realIrp->IoStatus.Status = status;
+
+    //
+    // Set the hard error if necessary.
+    //
+
+    if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
+
+        //
+        // Store DeviceObject for filesystem, and clear
+        // in IoStatus.Information field.
+        //
+
+        IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
+        realIrp->IoStatus.Information = 0;
+    }
+
+    IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+
+    IoStartNextPacket(DeviceObject, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+\f
+NTSTATUS
+STDCALL
+CdRomSwitchModeCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    )
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PIO_STACK_LOCATION  irpStack        = IoGetCurrentIrpStackLocation(Irp);
+    PCDROM_DATA         cdData = (PCDROM_DATA)(deviceExtension + 1);
+    BOOLEAN             use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
+    PIO_STACK_LOCATION  realIrpStack;
+    PIO_STACK_LOCATION  realIrpNextStack;
+    PSCSI_REQUEST_BLOCK srb     = Context;
+    PIRP                realIrp = NULL;
+    NTSTATUS            status;
+    BOOLEAN             retry;
+
+    //
+    // Extract the 'real' irp from the irpstack.
+    //
+
+    realIrp = (PIRP) irpStack->Parameters.Others.Argument2;
+    realIrpStack = IoGetCurrentIrpStackLocation(realIrp);
+    realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
+
+    //
+    // Check SRB status for success of completing request.
+    //
+
+    if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+        DebugPrint((2,
+                    "CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n",
+                    Irp,
+                    srb,
+                    realIrp));
+
+        //
+        // Release the queue if it is frozen.
+        //
+
+        if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+            ScsiClassReleaseQueue(DeviceObject);
+        }
+
+
+        retry = ScsiClassInterpretSenseInfo(DeviceObject,
+                                            srb,
+                                            irpStack->MajorFunction,
+                                            irpStack->Parameters.DeviceIoControl.IoControlCode,
+                                            MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1),
+                                            &status);
+
+        //
+        // If the status is verified required and the this request
+        // should bypass verify required then retry the request.
+        //
+
+        if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+            status == STATUS_VERIFY_REQUIRED) {
+
+            status = STATUS_IO_DEVICE_ERROR;
+            retry = TRUE;
+        }
+
+        if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) {
+
+            if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) {
+
+                //
+                // Retry request.
+                //
+
+                DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp));
+
+
+                ExFreePool(srb->SenseInfoBuffer);
+                ExFreePool(srb->DataBuffer);
+                ExFreePool(srb);
+                if (Irp->MdlAddress) {
+                    IoFreeMdl(Irp->MdlAddress);
+                }
+
+                IoFreeIrp(Irp);
+
+                //
+                // Call StartIo directly since IoStartNextPacket hasn't been called,
+                // the serialisation is still intact.
+                //
+
+                ScsiCdRomStartIo(DeviceObject, realIrp);
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            }
+
+            //
+            // Exhausted retries. Fall through and complete the request with the appropriate status.
+            //
+        }
+    } else {
+
+        //
+        // Set status for successful request.
+        //
+
+        status = STATUS_SUCCESS;
+    }
+
+    if (NT_SUCCESS(status)) {
+
+        ULONG sectorSize, startingSector, transferByteCount;
+        PCDB cdb;
+
+        //
+        // Update device ext. to show which mode we are currently using.
+        //
+
+        sectorSize =  cdData->u1.BlockDescriptor.BlockLength[0] << 16;
+        sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[1] << 8);
+        sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[2]);
+
+        cdData->RawAccess = (sectorSize == RAW_SECTOR_SIZE) ? TRUE : FALSE;
+
+        //
+        // Free the old data buffer, mdl.
+        //
+
+        ExFreePool(srb->DataBuffer);
+        IoFreeMdl(Irp->MdlAddress);
+        IoFreeIrp(Irp);
+
+        //
+        // rebuild the srb.
+        //
+
+        cdb = (PCDB)srb->Cdb;
+        RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
+
+
+        if (cdData->RawAccess) {
+
+            PRAW_READ_INFO rawReadInfo =
+                               (PRAW_READ_INFO)realIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+
+            ULONG maximumTransferLength;
+            ULONG transferPages;
+            UCHAR min, sec, frame;
+
+            //
+            // Calculate starting offset.
+            //
+
+            startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
+            transferByteCount  = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
+            maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
+            transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress),
+                                                           transferByteCount);
+
+            //
+            // Determine if request is within limits imposed by miniport.
+            // If the request is larger than the miniport's capabilities, split it.
+            //
+
+            if (transferByteCount > maximumTransferLength ||
+                transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) {
+
+                realIrp->IoStatus.Information = 0;
+                realIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+                IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+
+                ExFreePool(srb->SenseInfoBuffer);
+                ExFreePool(srb->DataBuffer);
+                ExFreePool(srb);
+                IoFreeMdl(Irp->MdlAddress);
+                IoFreeIrp(Irp);
+
+                IoStartNextPacket(DeviceObject, FALSE);
+
+                return STATUS_MORE_PROCESSING_REQUIRED;
+            }
+
+            srb->OriginalRequest = realIrp;
+            srb->SrbFlags = deviceExtension->SrbFlags;
+            srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
+            srb->DataTransferLength = transferByteCount;
+            srb->TimeOutValue = deviceExtension->TimeOutValue;
+            srb->CdbLength = 10;
+            srb->DataBuffer = MmGetMdlVirtualAddress(realIrp->MdlAddress);
+
+            if (rawReadInfo->TrackMode == CDDA) {
+                if (cdData->XAFlags & PLEXTOR_CDDA) {
+
+                    srb->CdbLength = 12;
+
+                    cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun;
+                    cdb->PLXTR_READ_CDDA.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                    cdb->PLXTR_READ_CDDA.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                    cdb->PLXTR_READ_CDDA.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                    cdb->PLXTR_READ_CDDA.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                    cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+                    cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8);
+                    cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0;
+                    cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0;
+
+                    cdb->PLXTR_READ_CDDA.SubCode = 0;
+                    cdb->PLXTR_READ_CDDA.OperationCode = 0xD8;
+
+                } else if (cdData->XAFlags & NEC_CDDA) {
+
+                    cdb->NEC_READ_CDDA.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                    cdb->NEC_READ_CDDA.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                    cdb->NEC_READ_CDDA.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                    cdb->NEC_READ_CDDA.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                    cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+                    cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8);
+
+                    cdb->NEC_READ_CDDA.OperationCode = 0xD4;
+                }
+            } else {
+                cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun;
+
+                cdb->CDB10.TransferBlocksMsb  = (UCHAR) (rawReadInfo->SectorCount >> 8);
+                cdb->CDB10.TransferBlocksLsb  = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
+
+                cdb->CDB10.LogicalBlockByte3  = (UCHAR) (startingSector & 0xFF);
+                cdb->CDB10.LogicalBlockByte2  = (UCHAR) ((startingSector >>  8) & 0xFF);
+                cdb->CDB10.LogicalBlockByte1  = (UCHAR) ((startingSector >> 16) & 0xFF);
+                cdb->CDB10.LogicalBlockByte0  = (UCHAR) ((startingSector >> 24) & 0xFF);
+
+                cdb->CDB10.OperationCode = SCSIOP_READ;
+            }
+
+            srb->SrbStatus = srb->ScsiStatus = 0;
+
+
+            irpStack = IoGetNextIrpStackLocation(realIrp);
+            irpStack->MajorFunction = IRP_MJ_SCSI;
+            irpStack->Parameters.Scsi.Srb = srb;
+
+            if (!(irpStack->Parameters.Others.Argument1)) {
+
+                //
+                // Only jam this in if it doesn't exist. The completion routines can
+                // call StartIo directly in the case of retries and resetting it will
+                // cause infinite loops.
+                //
+
+                irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+            }
+
+            //
+            // Set up IoCompletion routine address.
+            //
+
+            IoSetCompletionRoutine(realIrp,
+                                   CdRomXACompletion,
+                                   srb,
+                                   TRUE,
+                                   TRUE,
+                                   TRUE);
+        } else {
+
+            ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
+            ULONG transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress),
+                                                                 realIrpStack->Parameters.Read.Length);
+            //
+            // Back to cooked sectors. Build and send a normal read.
+            // The real work for setting offsets and checking for splitrequests was
+            // done in startio
+            //
+
+            if ((realIrpStack->Parameters.Read.Length > maximumTransferLength) ||
+                (transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages)) {
+
+                //
+                // Request needs to be split. Completion of each portion of the request will
+                // fire off the next portion. The final request will signal Io to send a new request.
+                //
+
+                ScsiClassSplitRequest(DeviceObject, realIrp, maximumTransferLength);
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            } else {
+
+                //
+                // Build SRB and CDB for this IRP.
+                //
+
+                ScsiClassBuildRequest(DeviceObject, realIrp);
+
+            }
+        }
+
+        //
+        // Call the port driver.
+        //
+
+        IoCallDriver(deviceExtension->PortDeviceObject, realIrp);
+
+        return STATUS_MORE_PROCESSING_REQUIRED;
+    }
+
+    //
+    // Update device Extension flags to indicate that XA isn't supported.
+    //
+
+    cdData->XAFlags |= XA_NOT_SUPPORTED;
+
+    //
+    // Deallocate srb and sense buffer.
+    //
+
+    if (srb) {
+        if (srb->DataBuffer) {
+            ExFreePool(srb->DataBuffer);
+        }
+        if (srb->SenseInfoBuffer) {
+            ExFreePool(srb->SenseInfoBuffer);
+        }
+        ExFreePool(srb);
+    }
+
+    if (Irp->PendingReturned) {
+      IoMarkIrpPending(Irp);
+    }
+
+    if (realIrp->PendingReturned) {
+        IoMarkIrpPending(realIrp);
+    }
+
+    if (Irp->MdlAddress) {
+        IoFreeMdl(Irp->MdlAddress);
+    }
+
+    IoFreeIrp(Irp);
+
+    //
+    // Set status in completing IRP.
+    //
+
+    realIrp->IoStatus.Status = status;
+
+    //
+    // Set the hard error if necessary.
+    //
+
+    if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
+
+        //
+        // Store DeviceObject for filesystem, and clear
+        // in IoStatus.Information field.
+        //
+
+        IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
+        realIrp->IoStatus.Information = 0;
+    }
+
+    IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
+
+    IoStartNextPacket(DeviceObject, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+\f
+NTSTATUS
+STDCALL
+CdRomXACompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine executes when the port driver has completed a request.
+    It looks at the SRB status in the completing SRB and if not success
+    it checks for valid request sense buffer information. If valid, the
+    info is used to update status with more precise message of type of
+    error. This routine deallocates the SRB.
+
+Arguments:
+
+    DeviceObject - Supplies the device object which represents the logical
+        unit.
+
+    Irp - Supplies the Irp which has completed.
+
+    Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+    NT status
+
+--*/
+
+{
+    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+    PIO_STACK_LOCATION irpNextStack = IoGetNextIrpStackLocation(Irp);
+    PSCSI_REQUEST_BLOCK srb = Context;
+    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
+    NTSTATUS status;
+    BOOLEAN retry;
+
+    //
+    // Check SRB status for success of completing request.
+    //
+
+    if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+        DebugPrint((2,"ScsiClassIoComplete: IRP %lx, SRB %lx\n", Irp, srb));
+
+        //
+        // Release the queue if it is frozen.
+        //
+
+        if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+            ScsiClassReleaseQueue(DeviceObject);
+        }
+
+        retry = ScsiClassInterpretSenseInfo(
+            DeviceObject,
+            srb,
+            irpStack->MajorFunction,
+            irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0,
+            MAXIMUM_RETRIES - ((ULONG)irpNextStack->Parameters.Others.Argument1),
+            &status);
+
+        //
+        // If the status is verified required and the this request
+        // should bypass verify required then retry the request.
+        //
+
+        if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+            status == STATUS_VERIFY_REQUIRED) {
+
+            status = STATUS_IO_DEVICE_ERROR;
+            retry = TRUE;
+        }
+
+        if (retry && (irpNextStack->Parameters.Others.Argument1 = (ULONG)irpNextStack->Parameters.Others.Argument1-1)) {
+
+            if (((ULONG)irpNextStack->Parameters.Others.Argument1)) {
+
+                //
+                // Retry request.
+                //
+
+                DebugPrint((1, "CdRomXACompletion: Retry request %lx - Calling StartIo\n", Irp));
+
+
+                ExFreePool(srb->SenseInfoBuffer);
+                ExFreePool(srb->DataBuffer);
+                ExFreePool(srb);
+
+                //
+                // Call StartIo directly since IoStartNextPacket hasn't been called,
+                // the serialisation is still intact.
+                //
+
+                ScsiCdRomStartIo(DeviceObject, Irp);
+                return STATUS_MORE_PROCESSING_REQUIRED;
+
+            }
+
+            //
+            // Exhausted retries. Fall through and complete the request with the appropriate status.
+            //
+        }
+    } else {
+
+        //
+        // Set status for successful request.
+        //
+
+        status = STATUS_SUCCESS;
+
+    } // end if (SRB_STATUS(srb->SrbStatus) ...
+
+    //
+    // Return SRB to nonpaged pool.
+    //
+
+    ExFreePool(srb);
+
+    //
+    // Set status in completing IRP.
+    //
+
+    Irp->IoStatus.Status = status;
+
+    //
+    // Set the hard error if necessary.
+    //
+
+    if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
+
+        //
+        // Store DeviceObject for filesystem, and clear
+        // in IoStatus.Information field.
+        //
+
+        IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+        Irp->IoStatus.Information = 0;
+    }
+
+    //
+    // If pending has be returned for this irp then mark the current stack as
+    // pending.
+    //
+
+    if (Irp->PendingReturned) {
+      IoMarkIrpPending(Irp);
+    }
+
+    //IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+    IoStartNextPacket(DeviceObject, FALSE);
+
+    return status;
+}
+
+\f
+NTSTATUS
+STDCALL
+CdRomDeviceControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+    )
+
+/*++
+
+Routine Description:
+
+    This is the NT device control handler for CDROMs.
+
+Arguments:
+
+    DeviceObject - for this CDROM
+
+    Irp - IO Request packet
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+
+{
+    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+    PIO_STACK_LOCATION nextStack;
+    PKEVENT            deviceControlEvent;
+    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
+    PCDROM_DATA        cdData = (PCDROM_DATA)(deviceExtension+1);
+    BOOLEAN            use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
+    SCSI_REQUEST_BLOCK srb;
+    PCDB cdb = (PCDB)srb.Cdb;
+    PVOID outputBuffer;
+    ULONG bytesTransferred = 0;
+    NTSTATUS status;
+    NTSTATUS status2;
+    KIRQL    irql;
+
+    ULONG ioctlCode;
+    ULONG baseCode;
+    ULONG functionCode;
+
+RetryControl:
+
+    //
+    // Zero the SRB on stack.
+    //
+
+    RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+    Irp->IoStatus.Information = 0;
+
+    //
+    // if this is a class driver ioctl then we need to change the base code
+    // to IOCTL_CDROM_BASE so that the switch statement can handle it.
+    //
+    // WARNING - currently the scsi class ioctl function codes are between
+    // 0x200 & 0x300.  this routine depends on that fact
+    //
+
+    ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+    baseCode = ioctlCode >> 16;
+    functionCode = (ioctlCode & (~0xffffc003)) >> 2;
+
+    DebugPrint((1, "CdRomDeviceControl: Ioctl Code = %#08lx, Base Code = %#lx,"
+                   " Function Code = %#lx\n",
+                ioctlCode,
+                baseCode,
+                functionCode
+              ));
+
+    if((functionCode >= 0x200) && (functionCode <= 0x300)) {
+
+        ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0);
+
+        DebugPrint((1, "CdRomDeviceControl: Class Code - new ioctl code is %#08lx\n",
+                    ioctlCode));
+
+        irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
+
+    }
+
+    switch (ioctlCode) {
+
+    case IOCTL_CDROM_RAW_READ: {
+
+        LARGE_INTEGER  startingOffset;
+        ULONG          transferBytes;
+        ULONG          startingSector;
+        PRAW_READ_INFO rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+        PUCHAR         userData = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
+
+        //
+        // Ensure that XA reads are supported.
+        //
+
+        if (cdData->XAFlags & XA_NOT_SUPPORTED) {
+
+            DebugPrint((1,
+                        "CdRomDeviceControl: XA Reads not supported. Flags (%x)\n",
+                        cdData->XAFlags));
+
+            status = STATUS_INVALID_DEVICE_REQUEST;
+            break;
+        }
+
+        //
+        // Check that ending sector is on disc and buffers are there and of
+        // correct size.
+        //
+
+        if (rawReadInfo == NULL) {
+
+            //
+            //  Called from user space. Validate the buffers.
+            //
+
+            rawReadInfo = (PRAW_READ_INFO)userData;
+            irpStack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)userData;
+
+            if (rawReadInfo == NULL) {
+
+                DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (No extent info\n"));
+
+                Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return STATUS_INVALID_PARAMETER;
+            }
+
+            if (irpStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(RAW_READ_INFO)) {
+
+                DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Invalid info buffer\n"));
+
+                Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+                IoCompleteRequest(Irp, IO_NO_INCREMENT);
+                return STATUS_INVALID_PARAMETER;
+            }
+        }
+
+        startingOffset.QuadPart = rawReadInfo->DiskOffset.QuadPart;
+        startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
+        transferBytes = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
+
+        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < transferBytes) {
+
+            DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Bad buffer size)\n"));
+
+            //
+            // Fail request with status of invalid parameters.
+            //
+
+            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        if ((startingOffset.QuadPart + transferBytes) > deviceExtension->PartitionLength.QuadPart) {
+
+            DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Request Out of Bounds)\n"));
+
+            //
+            // Fail request with status of invalid parameters.
+            //
+
+            status = STATUS_INVALID_PARAMETER;
+            break;
+        }
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
+
+        DebugPrint((2,"CdRomDeviceControl: Get drive geometry\n"));
+
+        if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof( DISK_GEOMETRY ) ) {
+
+            status = STATUS_INFO_LENGTH_MISMATCH;
+            break;
+        }
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject,Irp, NULL,NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_GET_LAST_SESSION:
+    case IOCTL_CDROM_READ_TOC:  {
+
+        //
+        // If the cd is playing music then reject this request.
+        //
+
+        if (CdRomIsPlayActive(DeviceObject)) {
+            Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            return STATUS_DEVICE_BUSY;
+        }
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_PLAY_AUDIO_MSF: {
+
+        //
+        // Play Audio MSF
+        //
+
+        DebugPrint((2,"CdRomDeviceControl: Play audio MSF\n"));
+
+        if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(CDROM_PLAY_AUDIO_MSF)) {
+
+            //
+            // Indicate unsuccessful status.
+            //
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            break;
+        }
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_SEEK_AUDIO_MSF: {
+
+
+        //
+        // Seek Audio MSF
+        //
+
+        DebugPrint((2,"CdRomDeviceControl: Seek audio MSF\n"));
+
+        if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(CDROM_SEEK_AUDIO_MSF)) {
+
+            //
+            // Indicate unsuccessful status.
+            //
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            break;
+        } else {
+            IoMarkIrpPending(Irp);
+            IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+            return STATUS_PENDING;
+
+        }
+    }
+
+    case IOCTL_CDROM_PAUSE_AUDIO: {
+
+        //
+        // Pause audio
+        //
+
+        DebugPrint((2, "CdRomDeviceControl: Pause audio\n"));
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+
+        break;
+    }
+
+    case IOCTL_CDROM_RESUME_AUDIO: {
+
+        //
+        // Resume audio
+        //
+
+        DebugPrint((2, "CdRomDeviceControl: Resume audio\n"));
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_READ_Q_CHANNEL: {
+
+        if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(CDROM_SUB_Q_DATA_FORMAT)) {
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            Irp->IoStatus.Information = 0;
+            break;
+        }
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_GET_CONTROL: {
+
+        DebugPrint((2, "CdRomDeviceControl: Get audio control\n"));
+
+        //
+        // Verify user buffer is large enough for the data.
+        //
+
+        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(CDROM_AUDIO_CONTROL)) {
+
+            //
+            // Indicate unsuccessful status and no data transferred.
+            //
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            Irp->IoStatus.Information = 0;
+            break;
+
+        } else {
+
+            IoMarkIrpPending(Irp);
+            IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+            return STATUS_PENDING;
+        }
+    }
+
+    case IOCTL_CDROM_GET_VOLUME: {
+
+        DebugPrint((2, "CdRomDeviceControl: Get volume control\n"));
+
+        //
+        // Verify user buffer is large enough for data.
+        //
+
+        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+            sizeof(VOLUME_CONTROL)) {
+
+            //
+            // Indicate unsuccessful status and no data transferred.
+            //
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            Irp->IoStatus.Information = 0;
+            break;
+
+        } else {
+            IoMarkIrpPending(Irp);
+            IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+            return STATUS_PENDING;
+        }
+    }
+
+    case IOCTL_CDROM_SET_VOLUME: {
+
+        DebugPrint((2, "CdRomDeviceControl: Set volume control\n"));
+
+        if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+            sizeof(VOLUME_CONTROL)) {
+
+            //
+            // Indicate unsuccessful status.
+            //
+
+            status = STATUS_BUFFER_TOO_SMALL;
+            break;
+        } else {
+
+            IoMarkIrpPending(Irp);
+            IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+            return STATUS_PENDING;
+        }
+    }
+
+    case IOCTL_CDROM_STOP_AUDIO: {
+
+        //
+        // Stop play.
+        //
+
+        DebugPrint((2, "CdRomDeviceControl: Stop audio\n"));
+
+        IoMarkIrpPending(Irp);
+        IoStartPacket(DeviceObject,Irp, NULL,NULL);
+
+        return STATUS_PENDING;
+    }
+
+    case IOCTL_CDROM_CHECK_VERIFY: {
+        DebugPrint((1, "CdRomDeviceControl: [%lx] Check Verify\n", Irp));
+        IoMarkIrpPending(Irp);
+
+        if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) &&
+           (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) {
+
+           DebugPrint((1, "CdRomDeviceControl: Check Verify: media count "
+                          "buffer too small\n"));
+
+           status = STATUS_BUFFER_TOO_SMALL;
+           break;
+        }
+
+        IoStartPacket(DeviceObject,Irp, NULL,NULL);
+
+        return STATUS_PENDING;
+    }
+
+    default: {
+
+        //
+        // allocate an event and stuff it into our stack location.
+        //
+
+        deviceControlEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
+
+        if(!deviceControlEvent) {
+
+            status = STATUS_INSUFFICIENT_RESOURCES;
+
+        } else {
+
+            PIO_STACK_LOCATION currentStack;
+
+            KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE);
+
+            currentStack = IoGetCurrentIrpStackLocation(Irp);
+            nextStack = IoGetNextIrpStackLocation(Irp);
+
+            //
+            // Copy the stack down a notch
+            //
+
+            *nextStack = *currentStack;
+
+            IoSetCompletionRoutine(
+                Irp,
+                CdRomClassIoctlCompletion,
+                deviceControlEvent,
+                TRUE,
+                TRUE,
+                TRUE
+                );
+
+            IoSetNextIrpStackLocation(Irp);
+
+            Irp->IoStatus.Status = STATUS_SUCCESS;
+            Irp->IoStatus.Information = 0;
+
+            //
+            // Override volume verifies on this stack location so that we
+            // will be forced through the synchronization.  Once this location
+            // goes away we get the old value back
+            //
+
+            nextStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
+            IoMarkIrpPending(Irp);
+
+            IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+            //
+            // Wait for CdRomClassIoctlCompletion to set the event. This
+            // ensures serialization remains intact for these unhandled device
+            // controls.
+            //
+
+            KeWaitForSingleObject(
+                deviceControlEvent,
+                Suspended,
+                KernelMode,
+                FALSE,
+                NULL);
+
+            ExFreePool(deviceControlEvent);
+
+            DebugPrint((2, "CdRomDeviceControl: irp %#08lx synchronized\n", Irp));
+
+            //
+            // If an error occured then propagate that back up - we are no longer
+            // guaranteed synchronization and the upper layers will have to
+            // retry.
+            //
+            // If no error occured, call down to the class driver directly
+            // then start up the next request.
+            //
+
+            if(Irp->IoStatus.Status == STATUS_SUCCESS) {
+
+                status = ScsiClassDeviceControl(DeviceObject, Irp);
+
+                KeRaiseIrql(DISPATCH_LEVEL, &irql);
+
+                IoStartNextPacket(DeviceObject, FALSE);
+
+                KeLowerIrql(irql);
+            }
+        }
+
+        return status;
+    }
+
+    } // end switch()
+
+    if (status == STATUS_VERIFY_REQUIRED) {
+
+        //
+        // If the status is verified required and this request
+        // should bypass verify required then retry the request.
+        //
+
+        if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
+
+            status = STATUS_IO_DEVICE_ERROR;
+            goto RetryControl;
+
+        }
+    }
+
+    if (IoIsErrorUserInduced(status)) {
+
+        IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+
+    }
+
+    //
+    // Update IRP with completion status.
+    //
+
+    Irp->IoStatus.Status = status;
+
+    //
+    // Complete the request.
+    //
+
+    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
+    DebugPrint((2, "CdRomDeviceControl: Status is %lx\n", status));
+    return status;
+
+} // end ScsiCdRomDeviceControl()
+
+\f
+VOID
+STDCALL
+ScanForSpecial(
+    PDEVICE_OBJECT DeviceObject,
+    PINQUIRYDATA InquiryData,
+    PIO_SCSI_CAPABILITIES PortCapabilities
+    )
+
+/*++
+
+Routine Description:
+
+    This function checks to see if an SCSI logical unit requires an special
+    initialization or error processing.
+
+Arguments:
+
+    DeviceObject - Supplies the device object to be tested.
+
+    InquiryData - Supplies the inquiry data returned by the device of interest.
+
+    PortCapabilities - Supplies the capabilities of the device object.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
+    PCDROM_DATA       cdData = (PCDROM_DATA)(deviceExtension+1);
+
+    //
+    // Look for a Hitachi CDR-1750. Read-ahead must be disabled in order
+    // to get this cdrom drive to work on scsi adapters that use PIO.
+    //
+
+    if ((strncmp(InquiryData->VendorId, "HITACHI CDR-1750S", strlen("HITACHI CDR-1750S")) == 0 ||
+        strncmp(InquiryData->VendorId, "HITACHI CDR-3650/1650S", strlen("HITACHI CDR-3650/1650S")) == 0)
+        && PortCapabilities->AdapterUsesPio) {
+
+        DebugPrint((1, "CdRom ScanForSpecial:  Found Hitachi CDR-1750S.\n"));
+
+        //
+        // Setup an error handler to reinitialize the cd rom after it is reset.
+        //
+
+        deviceExtension->ClassError = HitachProcessError;
+
+    } else if (( RtlCompareMemory( InquiryData->VendorId,"FUJITSU", 7 ) == 7 ) &&
+              (( RtlCompareMemory( InquiryData->ProductId,"FMCD-101", 8 ) == 8 ) ||
+               ( RtlCompareMemory( InquiryData->ProductId,"FMCD-102", 8 ) == 8 ))) {
+
+        //
+        // When Read command is issued to FMCD-101 or FMCD-102 and there is a music
+        // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning
+        // error status.
+        //
+
+        deviceExtension->TimeOutValue = 20;
+
+    } else if (( RtlCompareMemory( InquiryData->VendorId,"TOSHIBA", 7) == 7) &&
+              (( RtlCompareMemory( InquiryData->ProductId,"CD-ROM XM-34", 12) == 12))) {
+
+        SCSI_REQUEST_BLOCK srb;
+        PCDB               cdb;
+        ULONG              length;
+        PUCHAR             buffer;
+        NTSTATUS           status;
+
+        //
+        // Set the density code and the error handler.
+        //
+
+        length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH);
+
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        //
+        // Build the MODE SENSE CDB.
+        //
+
+        srb.CdbLength = 6;
+        cdb = (PCDB)srb.Cdb;
+
+        //
+        // Set timeout value from device extension.
+        //
+
+        srb.TimeOutValue = deviceExtension->TimeOutValue;
+
+        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+        cdb->MODE_SENSE.PageCode = 0x1;
+        cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
+
+        buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH));
+        if (!buffer) {
+            return;
+        }
+
+        status = ScsiClassSendSrbSynchronous(DeviceObject,
+                                             &srb,
+                                             buffer,
+                                             length,
+                                             FALSE);
+
+        ((PERROR_RECOVERY_DATA)buffer)->BlockDescriptor.DensityCode = 0x83;
+        ((PERROR_RECOVERY_DATA)buffer)->Header.ModeDataLength = 0x0;
+
+        RtlCopyMemory(&cdData->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA));
+
+        RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        //
+        // Build the MODE SENSE CDB.
+        //
+
+        srb.CdbLength = 6;
+        cdb = (PCDB)srb.Cdb;
+
+        //
+        // Set timeout value from device extension.
+        //
+
+        srb.TimeOutValue = deviceExtension->TimeOutValue;
+
+        cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+        cdb->MODE_SELECT.PFBit = 1;
+        cdb->MODE_SELECT.ParameterListLength = (UCHAR)length;
+
+        status = ScsiClassSendSrbSynchronous(DeviceObject,
+                                             &srb,
+                                             buffer,
+                                             length,
+                                             TRUE);
+
+        if (!NT_SUCCESS(status)) {
+            DebugPrint((1,
+                        "Cdrom.ScanForSpecial: Setting density code on Toshiba failed [%x]\n",
+                        status));
+        }
+
+        deviceExtension->ClassError = ToshibaProcessError;
+
+        ExFreePool(buffer);
+
+    }
+
+    //
+    // Determine special CD-DA requirements.
+    //
+
+    if (RtlCompareMemory( InquiryData->VendorId,"PLEXTOR",7) == 7) {
+        cdData->XAFlags |= PLEXTOR_CDDA;
+    } else if (RtlCompareMemory ( InquiryData->VendorId,"NEC",3) == 3) {
+        cdData->XAFlags |= NEC_CDDA;
+    }
+
+    return;
+}
+
+\f
+VOID
+STDCALL
+HitachProcessError(
+    PDEVICE_OBJECT DeviceObject,
+    PSCSI_REQUEST_BLOCK Srb,
+    NTSTATUS *Status,
+    BOOLEAN *Retry
+    )
+/*++
+
+Routine Description:
+
+   This routine checks the type of error.  If the error indicates CD-ROM the
+   CD-ROM needs to be reinitialized then a Mode sense command is sent to the
+   device.  This command disables read-ahead for the device.
+
+Arguments:
+
+    DeviceObject - Supplies a pointer to the device object.
+
+    Srb - Supplies a pointer to the failing Srb.
+
+    Status - Not used.
+
+    Retry - Not used.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PSENSE_DATA         senseBuffer = Srb->SenseInfoBuffer;
+    LARGE_INTEGER       largeInt;
+    PUCHAR              modePage;
+    PIO_STACK_LOCATION  irpStack;
+    PIRP                irp;
+    PSCSI_REQUEST_BLOCK srb;
+    PCOMPLETION_CONTEXT context;
+    PCDB                cdb;
+    ULONG               alignment;
+
+    UNREFERENCED_PARAMETER(Status);
+    UNREFERENCED_PARAMETER(Retry);
+
+    largeInt.QuadPart = (LONGLONG) 1;
+
+    //
+    // Check the status.  The initialization command only needs to be sent
+    // if UNIT ATTENTION is returned.
+    //
+
+    if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {
+
+        //
+        // The drive does not require reinitialization.
+        //
+
+        return;
+    }
+
+    //
+    // Found a bad HITACHI cd-rom.  These devices do not work with PIO
+    // adapters when read-ahead is enabled.  Read-ahead is disabled by
+    // a mode select command.  The mode select page code is zero and the
+    // length is 6 bytes.  All of the other bytes should be zero.
+    //
+
+
+    if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
+
+        DebugPrint((1, "HitachiProcessError: Reinitializing the CD-ROM.\n"));
+
+        //
+        // Send the special mode select command to disable read-ahead
+        // on the CD-ROM reader.
+        //
+
+        alignment = DeviceObject->AlignmentRequirement ?
+            DeviceObject->AlignmentRequirement : 1;
+
+        context = ExAllocatePool(
+            NonPagedPool,
+            sizeof(COMPLETION_CONTEXT) +  HITACHI_MODE_DATA_SIZE + alignment
+            );
+
+        if (context == NULL) {
+
+            //
+            // If there is not enough memory to fulfill this request,
+            // simply return. A subsequent retry will fail and another
+            // chance to start the unit.
+            //
+
+            return;
+        }
+
+        context->DeviceObject = DeviceObject;
+        srb = &context->Srb;
+
+        RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
+
+        //
+        // Write length to SRB.
+        //
+
+        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+
+        //
+        // Set up SCSI bus address.
+        //
+
+        srb->PathId = deviceExtension->PathId;
+        srb->TargetId = deviceExtension->TargetId;
+        srb->Lun = deviceExtension->Lun;
+
+        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->TimeOutValue = deviceExtension->TimeOutValue;
+
+        //
+        // Set the transfer length.
+        //
+
+        srb->DataTransferLength = HITACHI_MODE_DATA_SIZE;
+        srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+        //
+        // The data buffer must be aligned.
+        //
+
+        srb->DataBuffer = (PVOID) (((ULONG) (context + 1) + (alignment - 1)) &
+            ~(alignment - 1));
+
+
+        //
+        // Build the HITACHI read-ahead mode select CDB.
+        //
+
+        srb->CdbLength = 6;
+        cdb = (PCDB)srb->Cdb;
+        cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun;
+        cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SELECT;
+        cdb->MODE_SENSE.AllocationLength = HITACHI_MODE_DATA_SIZE;
+
+        //
+        // Initialize the mode sense data.
+        //
+
+        modePage = srb->DataBuffer;
+
+        RtlZeroMemory(modePage, HITACHI_MODE_DATA_SIZE);
+
+        //
+        // Set the page length field to 6.
+        //
+
+        modePage[5] = 6;
+
+        //
+        // Build the asynchronous request to be sent to the port driver.
+        //
+
+        irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE,
+                                           DeviceObject,
+                                           srb->DataBuffer,
+                                           srb->DataTransferLength,
+                                           &largeInt,
+                                           NULL);
+
+        IoSetCompletionRoutine(irp,
+                   (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion,
+                   context,
+                   TRUE,
+                   TRUE,
+                   TRUE);
+
+        irpStack = IoGetNextIrpStackLocation(irp);
+
+        irpStack->MajorFunction = IRP_MJ_SCSI;
+
+        srb->OriginalRequest = irp;
+
+        //
+        // Save SRB address in next stack for port driver.
+        //
+
+        irpStack->Parameters.Scsi.Srb = (PVOID)srb;
+
+        //
+        // Set up IRP Address.
+        //
+
+        (VOID)IoCallDriver(deviceExtension->PortDeviceObject, irp);
+
+    }
+}
+
+\f
+NTSTATUS
+STDCALL
+ToshibaProcessErrorCompletion(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp,
+    PVOID Context
+    )
+
+/*++
+
+Routine Description:
+
+    Completion routine for the ClassError routine to handle older Toshiba units
+    that require setting the density code.
+
+Arguments:
+
+    DeviceObject - Supplies a pointer to the device object.
+
+    Irp - Pointer to irp created to set the density code.
+
+    Context - Supplies a pointer to the Mode Select Srb.
+
+
+Return Value:
+
+    STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+
+{
+
+    PSCSI_REQUEST_BLOCK srb = Context;
+
+    //
+    // Check for a frozen queue.
+    //
+
+    if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+
+        //
+        // Unfreeze the queue getting the device object from the context.
+        //
+
+        ScsiClassReleaseQueue(DeviceObject);
+    }
+
+    //
+    // Free all of the allocations.
+    //
+
+    ExFreePool(srb->DataBuffer);
+    ExFreePool(srb);
+    IoFreeMdl(Irp->MdlAddress);
+    IoFreeIrp(Irp);
+
+    //
+    // Indicate the I/O system should stop processing the Irp completion.
+    //
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+\f
+VOID
+STDCALL
+ToshibaProcessError(
+    PDEVICE_OBJECT DeviceObject,
+    PSCSI_REQUEST_BLOCK Srb,
+    NTSTATUS *Status,
+    BOOLEAN *Retry
+    )
+
+/*++
+
+Routine Description:
+
+   This routine checks the type of error.  If the error indicates a unit attention,
+   the density code needs to be set via a Mode select command.
+
+Arguments:
+
+    DeviceObject - Supplies a pointer to the device object.
+
+    Srb - Supplies a pointer to the failing Srb.
+
+    Status - Not used.
+
+    Retry - Not used.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PCDROM_DATA         cdData = (PCDROM_DATA)(deviceExtension+1);
+    PSENSE_DATA         senseBuffer = Srb->SenseInfoBuffer;
+    PIO_STACK_LOCATION  irpStack;
+    PIRP                irp;
+    PSCSI_REQUEST_BLOCK srb;
+    ULONG               length;
+    PCDB                cdb;
+    PUCHAR              dataBuffer;
+
+
+    if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {
+        return;
+    }
+
+    //
+    // The Toshiba's require the density code to be set on power up and media changes.
+    //
+
+    if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
+
+
+        irp = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1),
+                              FALSE);
+
+        if (!irp) {
+            return;
+        }
+
+        srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
+        if (!srb) {
+            IoFreeIrp(irp);
+            return;
+        }
+
+
+        length = sizeof(ERROR_RECOVERY_DATA);
+        dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, length);
+        if (!dataBuffer) {
+            ExFreePool(srb);
+            IoFreeIrp(irp);
+            return;
+        }
+
+        irp->MdlAddress = IoAllocateMdl(dataBuffer,
+                                        length,
+                                        FALSE,
+                                        FALSE,
+                                        (PIRP) NULL);
+
+        if (!irp->MdlAddress) {
+            ExFreePool(srb);
+            ExFreePool(dataBuffer);
+            IoFreeIrp(irp);
+            return;
+        }
+
+        //
+        // Prepare the MDL
+        //
+
+        MmBuildMdlForNonPagedPool(irp->MdlAddress);
+
+        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+        srb->DataBuffer = dataBuffer;
+        cdb = (PCDB)srb->Cdb;
+
+        //
+        // Set up the irp.
+        //
+
+        IoSetNextIrpStackLocation(irp);
+        irp->IoStatus.Status = STATUS_SUCCESS;
+        irp->IoStatus.Information = 0;
+        irp->Flags = 0;
+        irp->UserBuffer = NULL;
+
+        //
+        // Save the device object and irp in a private stack location.
+        //
+
+        irpStack = IoGetCurrentIrpStackLocation(irp);
+        irpStack->DeviceObject = deviceExtension->DeviceObject;
+
+        //
+        // Construct the IRP stack for the lower level driver.
+        //
+
+        irpStack = IoGetNextIrpStackLocation(irp);
+        irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
+        irpStack->Parameters.Scsi.Srb = srb;
+
+        IoSetCompletionRoutine(irp,
+                               ToshibaProcessErrorCompletion,
+                               srb,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+        srb->PathId = deviceExtension->PathId;
+        srb->TargetId = deviceExtension->TargetId;
+        srb->Lun = deviceExtension->Lun;
+        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+        srb->Cdb[1] |= deviceExtension->Lun << 5;
+        srb->SrbStatus = srb->ScsiStatus = 0;
+        srb->NextSrb = 0;
+        srb->OriginalRequest = irp;
+        srb->SenseInfoBufferLength = 0;
+
+        //
+        // Set the transfer length.
+        //
+
+        srb->DataTransferLength = length;
+        srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+
+        srb->CdbLength = 6;
+        cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+        cdb->MODE_SELECT.PFBit = 1;
+        cdb->MODE_SELECT.ParameterListLength = (UCHAR)length;
+
+        //
+        // Copy the Mode page into the databuffer.
+        //
+
+        RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, length);
+
+        //
+        // Set the density code.
+        //
+
+        ((PERROR_RECOVERY_DATA)srb->DataBuffer)->BlockDescriptor.DensityCode = 0x83;
+
+        IoCallDriver(deviceExtension->PortDeviceObject, irp);
+    }
+}
+
+\f
+BOOLEAN
+STDCALL
+CdRomIsPlayActive(
+    IN PDEVICE_OBJECT DeviceObject
+    )
+
+/*++
+
+Routine Description:
+
+    This routine determines if the cd is currently playing music.
+
+Arguments:
+
+    DeviceObject - Device object to test.
+
+Return Value:
+
+    TRUE if the device is playing music.
+
+--*/
+{
+    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
+    PIRP irp;
+    IO_STATUS_BLOCK ioStatus;
+    KEVENT event;
+    NTSTATUS status;
+    PSUB_Q_CURRENT_POSITION currentBuffer;
+
+    if (!PLAY_ACTIVE(deviceExtension)) {
+        return(FALSE);
+    }
+
+    currentBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SUB_Q_CURRENT_POSITION));
+
+    if (currentBuffer == NULL) {
+        return(FALSE);
+    }
+
+    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
+    ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
+
+    //
+    // Create notification event object to be used to signal the
+    // request completion.
+    //
+
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    //
+    // Build the synchronous request  to be sent to the port driver
+    // to perform the request.
+    //
+
+    irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_Q_CHANNEL,
+                                        deviceExtension->DeviceObject,
+                                        currentBuffer,
+                                        sizeof(CDROM_SUB_Q_DATA_FORMAT),
+                                        currentBuffer,
+                                        sizeof(SUB_Q_CURRENT_POSITION),
+                                        FALSE,
+                                        &event,
+                                        &ioStatus);
+
+    if (irp == NULL) {
+        ExFreePool(currentBuffer);
+        return FALSE;
+    }
+
+    //
+    // Pass request to port driver and wait for request to complete.
+    //
+
+    status = IoCallDriver(deviceExtension->DeviceObject, irp);
+
+    if (status == STATUS_PENDING) {
+        KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
+        status = ioStatus.Status;
+    }
+
+    if (!NT_SUCCESS(status)) {
+        ExFreePool(currentBuffer);
+        return FALSE;
+    }
+
+    ExFreePool(currentBuffer);
+
+    return(PLAY_ACTIVE(deviceExtension));
+
+}
+
+
+\f
+NTSTATUS
+STDCALL
+CdRomMediaChangeCompletion(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp,
+    PVOID Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine handles the completion of the test unit ready irps
+    used to determine if the media has changed.  If the media has
+    changed, this code signals the named event to wake up other
+    system services that react to media change (aka AutoPlay).
+
+Arguments:
+
+    DeviceObject - the object for the completion
+    Irp - the IRP being completed
+    Context - the SRB from the IRP
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+
+{
+    PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context;
+    PIO_STACK_LOCATION  cdStack = IoGetCurrentIrpStackLocation(Irp);
+    PIO_STACK_LOCATION  irpNextStack = IoGetNextIrpStackLocation(Irp);
+    PDEVICE_EXTENSION   deviceExtension;
+    PDEVICE_EXTENSION   physicalExtension;
+    PSENSE_DATA         senseBuffer;
+    PCDROM_DATA         cddata;
+
+    ASSERT(Irp);
+    ASSERT(cdStack);
+    DeviceObject = cdStack->DeviceObject;
+    ASSERT(DeviceObject);
+
+    deviceExtension = DeviceObject->DeviceExtension;
+    physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension;
+    cddata = (PCDROM_DATA)(deviceExtension + 1);
+
+    ASSERT(cddata->MediaChangeIrp == NULL);
+
+    //
+    // If the sense data field is valid, look for a media change.
+    // otherwise this iteration of the polling will just assume nothing
+    // changed.
+    //
+
+    DebugPrint((3, "CdRomMediaChangeHandler: Completing Autorun Irp 0x%lx "
+                   "for device %d\n",
+                   Irp, deviceExtension->DeviceNumber));
+
+    if (srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
+        if (srb->SenseInfoBufferLength >= FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation)) {
+
+            //
+            // See if this is a media change.
+            //
+
+            senseBuffer = srb->SenseInfoBuffer;
+            if ((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_UNIT_ATTENTION)  {
+                if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_MEDIUM_CHANGED) {
+
+                    DebugPrint((1, "CdRomMediaChangeCompletion: New media inserted "
+                                   "into CdRom%d [irp = 0x%lx]\n",
+                                deviceExtension->DeviceNumber, Irp));
+
+                    //
+                    // Media change event occurred - signal the named event.
+                    //
+
+                    KeSetEvent(deviceExtension->MediaChangeEvent,
+                               (KPRIORITY) 0,
+                               FALSE);
+
+                    deviceExtension->MediaChangeNoMedia = FALSE;
+
+                }
+
+                if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
+
+                    //
+                    // Must remember the media changed and force the
+                    // file system to verify on next access
+                    //
+
+                    DeviceObject->Flags |= DO_VERIFY_VOLUME;
+                }
+
+                physicalExtension->MediaChangeCount++;
+
+            } else if(((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_NOT_READY)&&
+                      (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)&&
+                      (!deviceExtension->MediaChangeNoMedia)){
+
+                //
+                // If there was no media in the device then signal the waiters if
+                // we haven't already done so before.
+                //
+
+                DebugPrint((1, "CdRomMediaChangeCompletion: No media in device"
+                               "CdRom%d [irp = 0x%lx]\n",
+                            deviceExtension->DeviceNumber, Irp));
+
+                KeSetEvent(deviceExtension->MediaChangeEvent,
+                           (KPRIORITY) 0,
+                           FALSE);
+
+                deviceExtension->MediaChangeNoMedia = TRUE;
+
+            }
+        }
+    } else if((srb->SrbStatus == SRB_STATUS_SUCCESS)&&
+              (deviceExtension->MediaChangeNoMedia)) {
+        //
+        // We didn't have any media before and now the requests are succeeding
+        // we probably missed the Media change somehow.  Signal the change
+        // anyway
+        //
+
+        DebugPrint((1, "CdRomMediaChangeCompletion: Request completed normally"
+                       "for CdRom%d which was marked w/NoMedia [irp = 0x%lx]\n",
+                    deviceExtension->DeviceNumber, Irp));
+
+        KeSetEvent(deviceExtension->MediaChangeEvent,
+                   (KPRIORITY) 0,
+                   FALSE);
+
+        deviceExtension->MediaChangeNoMedia = FALSE;
+
+    }
+
+    if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+        ScsiClassReleaseQueue(deviceExtension->DeviceObject);
+    }
 
-/* INCLUDES *****************************************************************/
+    //
+    // Remember the IRP and SRB for use the next time.
+    //
 
-#include <ntddk.h>
-#include <scsi.h>
-#include <ntddscsi.h>
-#include <ntdddisk.h>
-#include <ntddcdrm.h>
-#include <include/class2.h>
-#include <stdio.h>
+    irpNextStack->Parameters.Scsi.Srb = srb;
+    cddata->MediaChangeIrp = Irp;
 
-#define NDEBUG
-#include <debug.h>
+    if (deviceExtension->ClassError) {
 
-#define VERSION "0.0.1"
+        NTSTATUS status;
+        BOOLEAN  retry;
 
+        //
+        // Throw away the status and retry values. Just give the error routine a chance
+        // to do what it needs to.
+        //
 
-#define SCSI_CDROM_TIMEOUT 10          /* Default timeout: 10 seconds */
+        deviceExtension->ClassError(DeviceObject,
+                                    srb,
+                                    &status,
+                                    &retry);
+    }
 
+    IoStartNextPacket(DeviceObject, FALSE);
 
-typedef struct _ERROR_RECOVERY_DATA6
-{
-  MODE_PARAMETER_HEADER Header;
-  MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
-} ERROR_RECOVERY_DATA6, *PERROR_RECOVERY_DATA6;
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+\f
+VOID
+STDCALL
+CdRomTickHandler(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PVOID Context
+    )
 
+/*++
 
-typedef struct _ERROR_RECOVERY_DATA10
-{
-  MODE_PARAMETER_HEADER10 Header;
-  MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
-} ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10;
+Routine Description:
 
-typedef struct _MODE_CAPABILITIES_PAGE2
-{
-  UCHAR PageCode:6;
-  UCHAR Reserved1:1;
-  UCHAR PSBit:1;
-  UCHAR PageLength;
-  UCHAR Reserved2[2];
-  UCHAR Capabilities[4];
-  UCHAR MaximumSpeedSupported[2];
-  UCHAR Reserved3;
-  UCHAR NumberVolumeLevels;
-  UCHAR BufferSize[2];
-  UCHAR CurrentSpeed[2];
-  UCHAR Reserved4;
-  UCHAR Reserved5:1;
-  UCHAR DigitalOutput:4;
-  UCHAR Reserved6:3;
-  UCHAR Reserved7[2];
-} MODE_CAPABILITIES_PAGE2, *PMODE_CAPABILITIES_PAGE2;
-
-typedef struct _MODE_CAPABILITIES_DATA6
-{
-  MODE_PARAMETER_HEADER Header;
-  MODE_CAPABILITIES_PAGE2 CababilitiesPage;
-} MODE_CAPABILITIES_DATA6, *PMODE_CAPABILITIES_DATA6;
+    This routine handles the once per second timer provided by the
+    Io subsystem.  It is only used when the cdrom device itself is
+    a candidate for autoplay support.  It should never be called if
+    the cdrom device is a changer device.
 
-typedef struct _MODE_CAPABILITIES_DATA10
-{
-  MODE_PARAMETER_HEADER10 Header;
-  MODE_CAPABILITIES_PAGE2 CababilitiesPage;
-} MODE_CAPABILITIES_DATA10, *PMODE_CAPABILITIES_DATA10;
+Arguments:
 
-typedef struct _CDROM_DATA
-{
-  BOOLEAN PlayActive;
-  BOOLEAN RawAccess;
-  USHORT XaFlags;
+    DeviceObject - what to check.
+    Context - not used.
 
-} CDROM_DATA, *PCDROM_DATA;
+Return Value:
 
-/* CDROM_DATA.XaFlags */
-#define XA_USE_6_BYTE          0x0001
-#define XA_USE_10_BYTE         0x0002
-#define XA_USE_READ_CD         0x0004
-#define XA_NOT_SUPPORTED       0x0008
-
-
-BOOLEAN STDCALL
-CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
-                     IN PUNICODE_STRING RegistryPath,
-                     IN PCLASS_INIT_DATA InitializationData,
-                     IN PDEVICE_OBJECT PortDeviceObject,
-                     IN ULONG PortNumber);
-
-BOOLEAN STDCALL
-CdromClassCheckDevice(IN PINQUIRYDATA InquiryData);
-
-NTSTATUS STDCALL
-CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
-                        IN PIRP Irp);
-
-static VOID
-CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
-                                IN ULONG DeviceNumber);
-
-static NTSTATUS
-CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
-                            IN PUNICODE_STRING RegistryPath,
-                            IN PDEVICE_OBJECT PortDeviceObject,
-                            IN ULONG PortNumber,
-                            IN ULONG DeviceNumber,
-                            IN PIO_SCSI_CAPABILITIES Capabilities,
-                            IN PSCSI_INQUIRY_DATA InquiryData,
-                            IN PCLASS_INIT_DATA InitializationData);
-
-
-NTSTATUS STDCALL
-CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
-                       IN PIRP Irp);
-
-VOID STDCALL
-CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject,
-                  IN PIRP Irp);
-
-NTSTATUS STDCALL
-CdromDeviceControlCompletion (IN PDEVICE_OBJECT DeviceObject,
-                             IN PIRP Irp,
-                             IN PVOID Context);
-
-VOID STDCALL
-CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject,
-                 IN PVOID Context);
-
-VOID STDCALL
-CdromWorkItem(IN PDEVICE_OBJECT DeviceObject,
-             IN PVOID Context);
-
-
-/* FUNCTIONS ****************************************************************/
-
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     DriverEntry
- *
- * DESCRIPTION:
- *     This function initializes the driver, locates and claims
- *     hardware resources, and creates various NT objects needed
- *     to process I/O requests.
- *
- * RUN LEVEL:
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS:
- *     DriverObject
- *             System allocated Driver Object for this driver
- *     RegistryPath
- *             Name of registry driver service key
- *
- * RETURNS:
- *     Status.
- */
+    None.
+
+--*/
 
-NTSTATUS STDCALL
-DriverEntry(IN PDRIVER_OBJECT DriverObject,
-           IN PUNICODE_STRING RegistryPath)
 {
-  CLASS_INIT_DATA InitData;
-
-  DPRINT("CD-ROM Class Driver %s\n",
-        VERSION);
-  DPRINT("RegistryPath '%wZ'\n",
-        RegistryPath);
-
-  InitData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
-  InitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA);
-  InitData.DeviceType = FILE_DEVICE_CD_ROM;
-  InitData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
-
-  InitData.ClassError = NULL;
-  InitData.ClassReadWriteVerification = CdromClassCheckReadWrite;
-  InitData.ClassFindDeviceCallBack = CdromClassCheckDevice;
-  InitData.ClassFindDevices = CdromClassFindDevices;
-  InitData.ClassDeviceControl = CdromClassDeviceControl;
-  InitData.ClassShutdownFlush = NULL;
-  InitData.ClassCreateClose = NULL;
-  InitData.ClassStartIo = CdromClassStartIo;
-
-  return(ScsiClassInitialize(DriverObject,
-                            RegistryPath,
-                            &InitData));
-}
+    PIRP              irp;
+    PIRP              heldIrpList;
+    PIRP              nextIrp;
+    PLIST_ENTRY       listEntry;
+    PCDROM_DATA       cddata;
+    PIO_STACK_LOCATION irpStack;
+    PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
+
+    cddata = (PCDROM_DATA)(deviceExtension + 1);
+
+    if (cddata->MediaChange) {
+        if (cddata->MediaChangeIrp != NULL) {
+
+            //
+            // Media change support is active and the IRP is waiting.
+            // Decrement the timer.
+            // There is no MP protection on the timer counter.  This
+            // code is the only code that will manipulate the timer
+            // and only one instance of it should be running at any
+            // given time.
+            //
+
+            cddata->MediaChangeCountDown--;
+
+#ifdef DBG
+            cddata->MediaChangeIrpTimeInUse = 0;
+            cddata->MediaChangeIrpLost = FALSE;
+#endif
 
+            if (!cddata->MediaChangeCountDown) {
+                PSCSI_REQUEST_BLOCK srb;
+                PIO_STACK_LOCATION  irpNextStack;
+                PCDB cdb;
+
+                //
+                // Reset the timer.
+                //
+
+                cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
+
+                //
+                // Prepare the IRP for the test unit ready
+                //
+
+                irp = cddata->MediaChangeIrp;
+                cddata->MediaChangeIrp = NULL;
+
+                irp->IoStatus.Status = STATUS_SUCCESS;
+                irp->IoStatus.Information = 0;
+                irp->Flags = 0;
+                irp->UserBuffer = NULL;
+
+                //
+                // If the irp is sent down when the volume needs to be
+                // verified, CdRomUpdateGeometryCompletion won't complete
+                // it since it's not associated with a thread.  Marking
+                // it to override the verify causes it always be sent
+                // to the port driver
+                //
+
+                irpStack = IoGetCurrentIrpStackLocation(irp);
+                irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
+                irpNextStack = IoGetNextIrpStackLocation(irp);
+                irpNextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+                irpNextStack->Parameters.DeviceIoControl.IoControlCode =
+                    IOCTL_SCSI_EXECUTE_NONE;
+
+                //
+                // Prepare the SRB for execution.
+                //
+
+                srb = irpNextStack->Parameters.Scsi.Srb;
+                srb->SrbStatus = srb->ScsiStatus = 0;
+                srb->NextSrb = 0;
+                srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+                srb->PathId = deviceExtension->PathId;
+                srb->TargetId = deviceExtension->TargetId;
+                srb->Lun = deviceExtension->Lun;
+                srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+                srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+                                SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+                srb->DataTransferLength = 0;
+                srb->OriginalRequest = irp;
+
+                RtlZeroMemory(srb->SenseInfoBuffer, SENSE_BUFFER_SIZE);
+                srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+                cdb = (PCDB) &srb->Cdb[0];
+                cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+                cdb->CDB6GENERIC.LogicalUnitNumber = srb->Lun;
+
+                //
+                // Setup the IRP to perform a test unit ready.
+                //
+
+                IoSetCompletionRoutine(irp,
+                                       CdRomMediaChangeCompletion,
+                                       srb,
+                                       TRUE,
+                                       TRUE,
+                                       TRUE);
+
+                //
+                // Issue the request.
+                //
+
+                IoStartPacket(DeviceObject, irp, NULL, NULL);
+            }
+        } else {
+
+#ifdef DBG
+            if(cddata->MediaChangeIrpLost == FALSE) {
+                if(cddata->MediaChangeIrpTimeInUse++ >
+                   MEDIA_CHANGE_TIMEOUT_TIME) {
+
+                    DebugPrint((0, "CdRom%d: AutoPlay has lost it's irp and "
+                                   "doesn't know where to find it.  Leave it "
+                                   "alone and it'll come home dragging it's "
+                                   "stack behind it.\n",
+                                   deviceExtension->DeviceNumber));
+                    cddata->MediaChangeIrpLost = TRUE;
+                }
+            }
 
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     CdromClassFindDevices
- *
- * DESCRIPTION:
- *     This function searches for device that are attached to the
- *     given scsi port.
- *
- * RUN LEVEL:
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS:
- *     DriverObject
- *             System allocated Driver Object for this driver
- *     RegistryPath
- *             Name of registry driver service key.
- *     InitializationData
- *             Pointer to the main initialization data
- *     PortDeviceObject
- *             Scsi port device object
- *     PortNumber
- *             Port number
- *
- * RETURNS:
- *     TRUE: At least one disk drive was found
- *     FALSE: No disk drive found
- */
+#endif
+        }
+    }
 
-BOOLEAN STDCALL
-CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
-                     IN PUNICODE_STRING RegistryPath,
-                     IN PCLASS_INIT_DATA InitializationData,
-                     IN PDEVICE_OBJECT PortDeviceObject,
-                     IN ULONG PortNumber)
-{
-  PCONFIGURATION_INFORMATION ConfigInfo;
-  PIO_SCSI_CAPABILITIES PortCapabilities;
-  PSCSI_ADAPTER_BUS_INFO AdapterBusInfo;
-  PSCSI_INQUIRY_DATA UnitInfo;
-  PINQUIRYDATA InquiryData;
-  PCHAR Buffer;
-  ULONG Bus;
-  ULONG DeviceCount;
-  BOOLEAN FoundDevice = FALSE;
-  NTSTATUS Status;
-
-  DPRINT("CdromClassFindDevices() called.\n");
-
-  /* Get port capabilities */
-  Status = ScsiClassGetCapabilities(PortDeviceObject,
-                                   &PortCapabilities);
-  if (!NT_SUCCESS(Status))
-    {
-      DPRINT1("ScsiClassGetCapabilities() failed! (Status 0x%lX)\n", Status);
-      return(FALSE);
-    }
-
-  DPRINT("PortCapabilities: %p\n", PortCapabilities);
-  DPRINT("MaximumTransferLength: %lu\n", PortCapabilities->MaximumTransferLength);
-  DPRINT("MaximumPhysicalPages: %lu\n", PortCapabilities->MaximumPhysicalPages);
-
-  /* Get inquiry data */
-  Status = ScsiClassGetInquiryData(PortDeviceObject,
-                                  (PSCSI_ADAPTER_BUS_INFO *)&Buffer);
-  if (!NT_SUCCESS(Status))
-    {
-      DPRINT1("ScsiClassGetInquiryData() failed! (Status 0x%lX)\n", Status);
-      return(FALSE);
-    }
-
-  /* Check whether there are unclaimed devices */
-  AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer;
-  DeviceCount = ScsiClassFindUnclaimedDevices(InitializationData,
-                                             AdapterBusInfo);
-  if (DeviceCount == 0)
-    {
-      DPRINT("No unclaimed devices!\n");
-      return(FALSE);
-    }
-
-  DPRINT("Found %lu unclaimed devices!\n", DeviceCount);
-
-  ConfigInfo = IoGetConfigurationInformation();
-  DPRINT("Number of SCSI ports: %lu\n", ConfigInfo->ScsiPortCount);
-
-  /* Search each bus of this adapter */
-  for (Bus = 0; Bus < (ULONG)AdapterBusInfo->NumberOfBuses; Bus++)
-    {
-      DPRINT("Searching bus %lu\n", Bus);
-
-      UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterBusInfo->BusData[Bus].InquiryDataOffset);
-
-      while (AdapterBusInfo->BusData[Bus].InquiryDataOffset)
-       {
-         InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData;
-
-         if ((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
-             (InquiryData->DeviceTypeQualifier == 0) &&
-             (UnitInfo->DeviceClaimed == FALSE))
-           {
-             DPRINT("Vendor: '%.24s'\n",
-                    InquiryData->VendorId);
-
-             /* Create device objects for disk */
-             Status = CdromClassCreateDeviceObject(DriverObject,
-                                                   RegistryPath,
-                                                   PortDeviceObject,
-                                                   PortNumber,
-                                                   ConfigInfo->CdRomCount,
-                                                   PortCapabilities,
-                                                   UnitInfo,
-                                                   InitializationData);
-             if (NT_SUCCESS(Status))
-               {
-                 ConfigInfo->CdRomCount++;
-                 FoundDevice = TRUE;
-               }
-           }
-
-         if (UnitInfo->NextInquiryDataOffset == 0)
-           break;
-
-         UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + UnitInfo->NextInquiryDataOffset);
-       }
-    }
-
-  ExFreePool(Buffer);
-
-  DPRINT("CdromClassFindDevices() done\n");
-
-  return(FoundDevice);
-}
+    //
+    // Process all generic timer IRPS in the timer list.  As IRPs are pulled
+    // off of the TimerIrpList they must be remembered in the first loop
+    // if they are not sent to the lower driver.  After all items have
+    // been pulled off the list, it is possible to put the held IRPs back
+    // into the TimerIrpList.
+    //
+
+    heldIrpList = NULL;
+    if (IsListEmpty(&cddata->TimerIrpList)) {
+        listEntry = NULL;
+    } else {
+        listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList,
+                                                &cddata->TimerIrpSpinLock);
+    }
+    while (listEntry) {
 
+        //
+        // There is something in the timer list.  Pick up the IRP and
+        // see if it is ready to be submitted.
+        //
 
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     CdromClassCheckDevice
- *
- * DESCRIPTION
- *     This function checks the InquiryData for the correct device
- *     type and qualifier.
- *
- * RUN LEVEL
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS
- *     InquiryData
- *             Pointer to the inquiry data for the device in question.
- *
- * RETURN VALUE
- *     TRUE: A disk device was found.
- *     FALSE: Otherwise.
- */
+        irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+        irpStack = IoGetCurrentIrpStackLocation(irp);
 
-BOOLEAN STDCALL
-CdromClassCheckDevice(IN PINQUIRYDATA InquiryData)
-{
-  return((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
-        (InquiryData->DeviceTypeQualifier == 0));
-}
+        if (irpStack->Parameters.Others.Argument3) {
+            ULONG count;
 
+            //
+            // Decrement the countdown timer and put the IRP back in the list.
+            //
 
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     CdromClassCheckReadWrite
- *
- * DESCRIPTION
- *     This function checks the given IRP for correct data.
- *
- * RUN LEVEL
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS
- *     DeviceObject
- *             Pointer to the device.
- *     Irp
- *             Irp to check.
- *
- * RETURN VALUE
- *     STATUS_SUCCESS: The IRP matches the requirements of the given device.
- *     Others: Failure.
- */
+            count = (ULONG) irpStack->Parameters.Others.Argument3;
+            count--;
+            irpStack->Parameters.Others.Argument3 = (PVOID) count;
 
-NTSTATUS STDCALL
-CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
-                        IN PIRP Irp)
-{
-  DPRINT("CdromClassCheckReadWrite() called\n");
+            ASSERT(irp->AssociatedIrp.MasterIrp == NULL);
+            if (heldIrpList) {
+                irp->AssociatedIrp.MasterIrp = (PVOID) heldIrpList;
+            }
+            heldIrpList = irp;
 
-  return(STATUS_SUCCESS);
-}
+        } else {
 
+            //
+            // Submit this IRP to the lower driver.  This IRP does not
+            // need to be remembered here.  It will be handled again when
+            // it completes.
+            //
 
-static VOID
-CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
-                                IN ULONG DeviceNumber)
-{
+            DebugPrint((1, "CdRomTickHandler: Reissuing request %lx (thread = %lx)\n", irp, irp->Tail.Overlay.Thread));
+
+            //
+            // feed this to the appropriate port driver
+            //
+
+            IoCallDriver (deviceExtension->PortDeviceObject, irp);
+
+        }
+
+        //
+        // Pick up the next IRP from the timer list.
+        //
+
+        listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList,
+                                                &cddata->TimerIrpSpinLock);
+    }
+
+    //
+    // Move all held IRPs back onto the timer list.
+    //
+
+    while (heldIrpList) {
 
+        //
+        // Save the single list pointer before queueing this IRP.
+        //
 
-  DeviceExtension->MediaChangeEvent =
-    IoCreateSynchronizationEvent (NULL,
-                                 &DeviceExtension->MediaChangeEventHandle);
+        nextIrp = (PIRP) heldIrpList->AssociatedIrp.MasterIrp;
+        heldIrpList->AssociatedIrp.MasterIrp = NULL;
 
-  KeClearEvent (DeviceExtension->MediaChangeEvent);
+        //
+        // Return the held IRP to the timer list.
+        //
+
+        ExInterlockedInsertTailList(&cddata->TimerIrpList,
+                                    &heldIrpList->Tail.Overlay.ListEntry,
+                                    &cddata->TimerIrpSpinLock);
+
+        //
+        // Continue processing the held IRPs
+        //
+
+        heldIrpList = nextIrp;
+    }
 }
+\f
+BOOLEAN
+STDCALL
+CdRomCheckRegistryForMediaChangeValue(
+    IN PUNICODE_STRING RegistryPath,
+    IN ULONG DeviceNumber
+    )
 
+/*++
 
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     CdromClassCreateDeviceObject
- *
- * DESCRIPTION:
- *     Create the raw device and any partition devices on this drive
- *
- * RUN LEVEL:
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS:
- *     DriverObject
- *             System allocated Driver Object for this driver.
- *     RegistryPath
- *             Name of registry driver service key.
- *     PortDeviceObject
- *     PortNumber
- *     DeviceNumber
- *     Capabilities
- *     InquiryData
- *     InitializationData
- *
- * RETURNS:
- *     Status.
- */
+Routine Description:
+
+    The user must specify that AutoPlay is to run on the platform
+    by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
+    Services\Cdrom\Autorun:REG_DWORD:1.
+
+    The user can override the global setting to enable or disable Autorun on a
+    specific cdrom device by setting the key HKEY_LOCAL_MACHINE\System\
+    CurrentControlSet\Services\Cdrom\Device<N>\Autorun:REG_DWORD to one or zero.
+    (CURRENTLY UNIMPLEMENTED)
+
+    If this registry value does not exist or contains the value zero then
+    the timer to check for media change does not run.
+
+Arguments:
+
+    RegistryPath - pointer to the unicode string inside
+                   ...\CurrentControlSet\Services\Cdrom
+    DeviceNumber - The number of the device object
+
+Return Value:
+
+    TRUE - Autorun is enabled.
+    FALSE - no autorun.
+
+--*/
 
-static NTSTATUS
-CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
-                            IN PUNICODE_STRING RegistryPath,
-                            IN PDEVICE_OBJECT PortDeviceObject,
-                            IN ULONG PortNumber,
-                            IN ULONG DeviceNumber,
-                            IN PIO_SCSI_CAPABILITIES Capabilities,
-                            IN PSCSI_INQUIRY_DATA InquiryData,
-                            IN PCLASS_INIT_DATA InitializationData)
 {
-  PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */
-  PDEVICE_OBJECT DiskDeviceObject;
-  PCDROM_DATA CdromData;
-  CHAR NameBuffer[80];
-  SCSI_REQUEST_BLOCK Srb;
-  PUCHAR Buffer;
-  ULONG Length;
-  PCDB Cdb;
-  NTSTATUS Status;
-
-  DPRINT("CdromClassCreateDeviceObject() called\n");
-
-  /* Claim the cdrom device */
-  Status = ScsiClassClaimDevice(PortDeviceObject,
-                               InquiryData,
-                               FALSE,
-                               &PortDeviceObject);
-  if (!NT_SUCCESS(Status))
-    {
-      DbgPrint("Could not claim cdrom device\n");
-      return(Status);
-    }
-
-  /* Create cdrom device */
-  sprintf(NameBuffer,
-         "\\Device\\CdRom%lu",
-         DeviceNumber);
-
-  Status = ScsiClassCreateDeviceObject(DriverObject,
-                                      NameBuffer,
-                                      NULL,
-                                      &DiskDeviceObject,
-                                      InitializationData);
-  if (!NT_SUCCESS(Status))
-    {
-      DPRINT1("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status);
-
-      /* Release (unclaim) the disk */
-      ScsiClassClaimDevice(PortDeviceObject,
-                          InquiryData,
-                          TRUE,
-                          NULL);
-
-      return(Status);
-    }
-
-  DiskDeviceObject->Flags |= DO_DIRECT_IO;
-  DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
-  DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
-
-  if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement)
-    {
-      DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
-    }
-
-  DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
-  DiskDeviceExtension->LockCount = 0;
-  DiskDeviceExtension->DeviceNumber = DeviceNumber;
-  DiskDeviceExtension->DeviceObject = DiskDeviceObject;
-  DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
-  DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
-  DiskDeviceExtension->PortCapabilities = Capabilities;
-  DiskDeviceExtension->StartingOffset.QuadPart = 0;
-  DiskDeviceExtension->PortNumber = (UCHAR)PortNumber;
-  DiskDeviceExtension->PathId = InquiryData->PathId;
-  DiskDeviceExtension->TargetId = InquiryData->TargetId;
-  DiskDeviceExtension->Lun = InquiryData->Lun;
-
-  /* zero-out disk data */
-  CdromData = (PCDROM_DATA)(DiskDeviceExtension + 1);
-  RtlZeroMemory(CdromData,
-               sizeof(CDROM_DATA));
-
-  DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool,
-                                                 sizeof(SENSE_DATA));
-  if (DiskDeviceExtension->SenseData == NULL)
-    {
-      DPRINT1("Failed to allocate sense data buffer!\n");
-
-      IoDeleteDevice(DiskDeviceObject);
-
-      /* Release (unclaim) the disk */
-      ScsiClassClaimDevice(PortDeviceObject,
-                          InquiryData,
-                          TRUE,
-                          NULL);
-
-      return(STATUS_INSUFFICIENT_RESOURCES);
-    }
-
-  /* Get timeout value */
-  DiskDeviceExtension->TimeOutValue =
-    ScsiClassQueryTimeOutRegistryValue(RegistryPath);
-  if (DiskDeviceExtension->TimeOutValue == 0)
-    DiskDeviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
-
-  /* Initialize lookaside list for SRBs */
-  ScsiClassInitializeSrbLookasideList(DiskDeviceExtension,
-                                     4);
-
-  /* Get disk geometry */
-  DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
-                                                    sizeof(DISK_GEOMETRY));
-  if (DiskDeviceExtension->DiskGeometry == NULL)
-    {
-      DPRINT1("Failed to allocate geometry buffer!\n");
-
-      ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
-
-      IoDeleteDevice(DiskDeviceObject);
-
-      /* Release (unclaim) the disk */
-      ScsiClassClaimDevice(PortDeviceObject,
-                          InquiryData,
-                          TRUE,
-                          NULL);
-
-      return(STATUS_INSUFFICIENT_RESOURCES);
-    }
-
-  /* Read the drive's capacity */
-  Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
-  if (!NT_SUCCESS(Status) ||
-      DiskDeviceExtension->DiskGeometry->BytesPerSector == 0)
-    {
-      /* Set ISO9660 defaults */
-      DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
-      DiskDeviceExtension->DiskGeometry->MediaType = RemovableMedia;
-      DiskDeviceExtension->SectorShift = 11;
-      DiskDeviceExtension->PartitionLength.QuadPart = (ULONGLONG)0x7fffffff;
-    }
-  else
-    {
-      /* Make sure the BytesPerSector value is a power of 2 */
-//      DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
-    }
-
-  DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
-
-  /* Initialize media change support */
-  CdromClassCreateMediaChangeEvent (DiskDeviceExtension,
-                                   DeviceNumber);
-  if (DiskDeviceExtension->MediaChangeEvent != NULL)
-    {
-      DPRINT("Allocated media change event!\n");
-
-      /* FIXME: Allocate media change IRP and SRB */
-    }
-
-  /* Use 6 byte xa commands by default */
-  CdromData->XaFlags |= XA_USE_6_BYTE;
-
-  /* Read 'error recovery page' to get additional drive capabilities */
-  Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH;
-
-  RtlZeroMemory (&Srb,
-                sizeof(SCSI_REQUEST_BLOCK));
-  Srb.CdbLength = 6;
-  Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue;
-
-  Cdb = (PCDB)Srb.Cdb;
-  Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
-  Cdb->MODE_SENSE.PageCode = 0x01;
-  Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length;
-
-  Buffer = ExAllocatePool (NonPagedPool,
-                           max(sizeof(ERROR_RECOVERY_DATA6),
-                              max(sizeof(ERROR_RECOVERY_DATA10),
-                                  max(sizeof(MODE_CAPABILITIES_DATA6),
-                                      sizeof(MODE_CAPABILITIES_DATA10)))));
-  if (Buffer == NULL)
-    {
-      DPRINT1("Allocating recovery page buffer failed!\n");
-      return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-  Status = ScsiClassSendSrbSynchronous (DiskDeviceObject,
-                                       &Srb,
-                                       Buffer,
-                                       Length,
-                                       FALSE);
-
-  if (!NT_SUCCESS (Status))
-    {
-      DPRINT("MODE_SENSE(6) failed\n");
-
-      /* Try the 10 byte version */
-      Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH10;
-
-      RtlZeroMemory (&Srb,
-                    sizeof(SCSI_REQUEST_BLOCK));
-      Srb.CdbLength = 10;
-      Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue;
-
-      Cdb = (PCDB)Srb.Cdb;
-      Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
-      Cdb->MODE_SENSE10.PageCode = 0x01;
-      Cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(Length >> 8);
-      Cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(Length & 0xFF);
-
-      Status = ScsiClassSendSrbSynchronous (DiskDeviceObject,
-                                           &Srb,
-                                           Buffer,
-                                           Length,
-                                           FALSE);
-
-      if (Status == STATUS_DATA_OVERRUN)
-       {
-         DPRINT1("Data overrun\n");
-
-         /* FIXME */
-       }
-      else if (NT_SUCCESS (Status))
-       {
-         DPRINT("Use 10 byte commands\n");
-         CdromData->XaFlags &= XA_USE_6_BYTE;
-         CdromData->XaFlags |= XA_USE_10_BYTE;
-       }
-      else
-       {
-         DPRINT("XA not supported\n");
-         CdromData->XaFlags |= XA_NOT_SUPPORTED;
-       }
-    }
-  else
-    {
-      DPRINT("Use 6 byte commands\n");
-    }
-
-  /* Read 'capabilities & mechanical status page' to get additional drive capabilities */
-  Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH;
-
-  if (!(CdromData->XaFlags & XA_NOT_SUPPORTED))
-    {
-      RtlZeroMemory (&Srb, sizeof(SCSI_REQUEST_BLOCK));
-      Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue;
-      Cdb = (PCDB)Srb.Cdb;
-
-      if (CdromData->XaFlags & XA_USE_10_BYTE)
-        {
-          /* Try the 10 byte version */
-          Length = sizeof(MODE_CAPABILITIES_PAGE2) + MODE_HEADER_LENGTH10;
-
-          Srb.CdbLength = 10;
-          Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
-          Cdb->MODE_SENSE10.PageCode = 0x2a;
-          Cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(Length >> 8);
-          Cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(Length & 0xFF);
-       }
-      else
-        {
-          Length = sizeof(MODE_CAPABILITIES_PAGE2) + MODE_HEADER_LENGTH;
-
-          Srb.CdbLength = 6;
-          Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
-          Cdb->MODE_SENSE.PageCode = 0x2a;
-          Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length;
-        }
-      Status = ScsiClassSendSrbSynchronous (DiskDeviceObject,
-                                           &Srb,
-                                           Buffer,
-                                           Length,
-                                           FALSE);
-      if (NT_SUCCESS (Status))
-       {
-#if 0
-          PMODE_CAPABILITIES_PAGE2 CapabilitiesData;
-         if (CdromData->XaFlags & XA_USE_10_BYTE)
-           {
-             CapabilitiesData = (PMODE_CAPABILITIES_PAGE2)(Buffer + sizeof(MODE_PARAMETER_HEADER10));
-           }
-         else
-           {
-             CapabilitiesData = (PMODE_CAPABILITIES_PAGE2)(Buffer + sizeof(MODE_PARAMETER_HEADER));
-           }
-
-         DbgPrint("Capabilities for '%s':\n", NameBuffer);
-         if (CapabilitiesData->Reserved2[0] & 0x20)
-           {
-             DbgPrint("  Drive supports reading of DVD-RAM discs\n");
-           }
-         if (CapabilitiesData->Reserved2[0] & 0x10)
-           {
-             DbgPrint("  Drive supports reading of DVD-R discs\n");
-           }
-         if (CapabilitiesData->Reserved2[0] & 0x08)
-           {
-             DbgPrint("  Drive supports reading of DVD-ROM discs\n");
-           }
-         if (CapabilitiesData->Reserved2[0] & 0x04)
-           {
-             DbgPrint("  Drive supports reading CD-R discs with addressing method 2\n");
-           }
-         if (CapabilitiesData->Reserved2[0] & 0x02)
-           {
-             DbgPrint("  Drive can read from CD-R/W (CD-E) discs (orange book, part III)\n");
-           }
-         if (CapabilitiesData->Reserved2[0] & 0x01)
-           {
-             DbgPrint("  Drive supports read from CD-R discs (orange book, part II)\n");
-           }
-         DPRINT("CapabilitiesData.Reserved2[1] %x\n", CapabilitiesData->Reserved2[1]);
-         if (CapabilitiesData->Reserved2[1] & 0x01)
-           {
-             DbgPrint("  Drive can write to CD-R discs (orange book, part II)\n");
-           }
-         if (CapabilitiesData->Reserved2[1] & 0x02)
-           {
-             DbgPrint("  Drive can write to CD-R/W (CD-E) discs (orange book, part III)\n");
-           }
-         if (CapabilitiesData->Reserved2[1] & 0x04)
-           {
-             DbgPrint("  Drive can fake writes\n");
-           }
-         if (CapabilitiesData->Reserved2[1] & 0x10)
-           {
-             DbgPrint("  Drive can write DVD-R discs\n");
-           }
-         if (CapabilitiesData->Reserved2[1] & 0x20)
-           {
-             DbgPrint("  Drive can write DVD-RAM discs\n");
-           }
-         DPRINT("CapabilitiesData.Capabilities[0] %x\n", CapabilitiesData->Capabilities[0]);
-         if (CapabilitiesData->Capabilities[0] & 0x01)
-           {
-             DbgPrint("  Drive supports audio play operations\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x02)
-           {
-             DbgPrint("  Drive can deliver a composite audio/video data stream\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x04)
-           {
-             DbgPrint("  Drive supports digital output on port 1\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x08)
-           {
-             DbgPrint("  Drive supports digital output on port 2\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x10)
-           {
-             DbgPrint("  Drive can read mode 2, form 1 (XA) data\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x20)
-           {
-             DbgPrint("  Drive can read mode 2, form 2 data\n");
-           }
-         if (CapabilitiesData->Capabilities[0] & 0x40)
-           {
-             DbgPrint("  Drive can read multisession discs\n");
-           }
-         DPRINT("CapabilitiesData.Capabilities[1] %x\n", CapabilitiesData->Capabilities[1]);
-         if (CapabilitiesData->Capabilities[1] & 0x01)
-           {
-             DbgPrint("  Drive can read Red Book audio data\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x02)
-           {
-             DbgPrint("  Drive can continue a read cdda operation from a loss of streaming\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x04)
-           {
-             DbgPrint("  Subchannel reads can return combined R-W information\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x08)
-           {
-             DbgPrint("  R-W data will be returned deinterleaved and error corrected\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x10)
-           {
-             DbgPrint("  Drive supports C2 error pointers\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x20)
-           {
-             DbgPrint("  Drive can return International Standard Recording Code info\n");
-           }
-         if (CapabilitiesData->Capabilities[1] & 0x40)
-           {
-             DbgPrint("  Drive can return Media Catalog Number (UPC) info\n");
-           }
-         DPRINT("CapabilitiesData.Capabilities[2] %x\n", CapabilitiesData->Capabilities[2]);
-         if (CapabilitiesData->Capabilities[2] & 0x01)
-           {
-             DbgPrint("  Drive can lock the door\n");
-           }
-         if (CapabilitiesData->Capabilities[2] & 0x02)
-           {
-             DbgPrint("  The door is locked\n");
-           }
-         if (CapabilitiesData->Capabilities[2] & 0x04)
-           {
-           }
-         if (CapabilitiesData->Capabilities[2] & 0x08)
-           {
-             DbgPrint("  Drive can eject a disc or changer cartridge\n");
-           }
-         if (CapabilitiesData->Capabilities[2] & 0x10)
-           {
-             DbgPrint("  Drive supports C2 error pointers\n");
-           }
-         switch (CapabilitiesData->Capabilities[2] >> 5)
-           {
-             case 0:
-               DbgPrint("  Drive use a caddy type loading mechanism\n");
-               break;
-             case 1:
-               DbgPrint("  Drive use a tray type loading mechanism\n");
-               break;
-             case 2:
-               DbgPrint("  Drive use a pop-up type loading mechanism\n");
-               break;
-             case 4:
-               DbgPrint("  Drive is a changer with individually changeable discs\n");
-               break;
-             case 5:
-               DbgPrint("  Drive is a changer with cartridge mechanism\n");
-               break;
-           }
-         DPRINT("CapabilitiesData.Capabilities[3] %x\n", CapabilitiesData->Capabilities[3]);
-         if (CapabilitiesData->Capabilities[3] & 0x01)
-           {
-             DbgPrint("  Audio level for each channel can be controlled independently\n");
-           }
-         if (CapabilitiesData->Capabilities[3] & 0x02)
-           {
-             DbgPrint("  Audio for each channel can be muted independently\n");
-           }
-         if (CapabilitiesData->Capabilities[3] & 0x04)
-           {
-             DbgPrint("  Changer can report exact contents of slots\n");
-           }
-         if (CapabilitiesData->Capabilities[3] & 0x08)
-           {
-             DbgPrint("  Drive supports software slot selection\n");
-           }
-         DbgPrint("  Maximum speed is %d kB/s\n",
-                 (CapabilitiesData->MaximumSpeedSupported[0] << 8)
-                 | CapabilitiesData->MaximumSpeedSupported[1]);
-         DbgPrint("  Current speed is %d kB/s\n",
-                 (CapabilitiesData->CurrentSpeed[0] << 8)
-                 | CapabilitiesData->CurrentSpeed[1]);
-         DbgPrint("  Number of discrete volume levels is %d\n",
-                 (CapabilitiesData->Reserved3 << 8)
-                 | CapabilitiesData->NumberVolumeLevels);
-         DbgPrint("  Buffer size is %d kB\n",
-                 (CapabilitiesData->BufferSize[0] << 8)
-                 | CapabilitiesData->BufferSize[1]);
-#endif
-       }
-      else
-       {
-         DPRINT("XA not supported\n");
-         CdromData->XaFlags |= XA_NOT_SUPPORTED;
-       }
+#define ITEMS_TO_QUERY 2 /* always 1 greater than what is searched */
+    PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
+    NTSTATUS          status;
+    LONG              zero = 0;
+
+    LONG              tmp = 0;
+    LONG              doRun = 0;
+
+    UCHAR             buf[32];
+    ANSI_STRING       paramNum;
 
+    UNICODE_STRING    paramStr;
+
+    UNICODE_STRING    paramSuffix;
+    UNICODE_STRING    paramPath;
+    UNICODE_STRING    paramDevPath;
+
+    //
+    // First append \Parameters to the passed in registry path
+    //
+
+    RtlInitUnicodeString(&paramStr, L"\\Parameters");
+
+    RtlInitUnicodeString(&paramPath, NULL);
+
+    paramPath.MaximumLength = RegistryPath->Length +
+                              paramStr.Length +
+                              sizeof(WCHAR);
+
+    paramPath.Buffer = ExAllocatePool(PagedPool, paramPath.MaximumLength);
+
+    if(!paramPath.Buffer) {
+
+        DebugPrint((1,"CdRomCheckRegAP: couldn't allocate paramPath\n"));
+
+        return FALSE;
     }
 
+    RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength);
+    RtlAppendUnicodeToString(&paramPath, RegistryPath->Buffer);
+    RtlAppendUnicodeToString(&paramPath, paramStr.Buffer);
 
-  ExFreePool (Buffer);
+    DebugPrint((2, "CdRomCheckRegAP: paramPath [%d] = %ws\n",
+                paramPath.Length,
+                paramPath.Buffer));
 
-  /* Initialize device timer */
-  IoInitializeTimer(DiskDeviceObject,
-                   CdromTimerRoutine,
-                   NULL);
-  IoStartTimer(DiskDeviceObject);
+    //
+    // build a counted ANSI string that contains
+    // the suffix for the path
+    //
 
-  DiskDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+    sprintf(buf, "\\Device%d", DeviceNumber);
+    RtlInitAnsiString(&paramNum, buf);
 
-  DPRINT("CdromClassCreateDeviceObjects() done\n");
+    //
+    // Next convert this into a unicode string
+    //
 
-  return(STATUS_SUCCESS);
-}
+    status = RtlAnsiStringToUnicodeString(&paramSuffix, &paramNum, TRUE);
 
+    if(!NT_SUCCESS(status)) {
+        DebugPrint((1,"CdRomCheckRegAP: couldn't convert paramNum to paramSuffix\n"));
+        ExFreePool(paramPath.Buffer);
+        return FALSE;
+    }
 
-/**********************************************************************
- * NAME
- *     CdromClassReadTocEntry
- *
- * ARGUMENTS:
- *      DeviceObject
- *      TrackNo
- *      Buffer
- *      Length
- *
- * RETURNS:
- *     Status.
- */
-static NTSTATUS
-CdromClassReadTocEntry (PDEVICE_OBJECT DeviceObject,
-                       SIZE_T TrackNo,
-                       PVOID Buffer,
-                       SIZE_T Length)
-{
-  PDEVICE_EXTENSION DeviceExtension;
-  SCSI_REQUEST_BLOCK Srb;
-  PCDB Cdb;
-
-  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-
-  RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
-  Srb.CdbLength = 10;
-  Srb.TimeOutValue = DeviceExtension->TimeOutValue;
-
-  Cdb = (PCDB)Srb.Cdb;
-  Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
-  Cdb->READ_TOC.StartingTrack = TrackNo;
-  Cdb->READ_TOC.Format = 0;
-  Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
-  Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
-  Cdb->READ_TOC.Msf = 1;
-
-  return(ScsiClassSendSrbSynchronous(DeviceObject,
-                                    &Srb,
-                                    Buffer,
-                                    Length,
-                                    FALSE));
-}
+    RtlInitUnicodeString(&paramDevPath, NULL);
 
+    //
+    // now build the device specific path
+    //
 
-static NTSTATUS
-CdromClassReadLastSession (PDEVICE_OBJECT DeviceObject,
-                          SIZE_T TrackNo,
-                          PVOID Buffer,
-                          SIZE_T Length)
-{
-  PDEVICE_EXTENSION DeviceExtension;
-  SCSI_REQUEST_BLOCK Srb;
-  PCDB Cdb;
-
-  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-
-  RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
-  Srb.CdbLength = 10;
-  Srb.TimeOutValue = DeviceExtension->TimeOutValue;
-
-  Cdb = (PCDB)Srb.Cdb;
-  Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
-  Cdb->READ_TOC.StartingTrack = TrackNo;
-  Cdb->READ_TOC.Format = 1;
-  Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
-  Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
-  Cdb->READ_TOC.Msf = 0;
-
-  return(ScsiClassSendSrbSynchronous(DeviceObject,
-                                    &Srb,
-                                    Buffer,
-                                    Length,
-                                    FALSE));
+    paramDevPath.MaximumLength = paramPath.Length +
+                                 paramSuffix.Length +
+                                 sizeof(WCHAR);
+    paramDevPath.Buffer = ExAllocatePool(PagedPool, paramDevPath.MaximumLength);
+
+    if(!paramDevPath.Buffer) {
+        RtlFreeUnicodeString(&paramSuffix);
+        ExFreePool(paramPath.Buffer);
+        return FALSE;
+    }
+
+    RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength);
+    RtlAppendUnicodeToString(&paramDevPath, paramPath.Buffer);
+    RtlAppendUnicodeToString(&paramDevPath, paramSuffix.Buffer);
+
+    DebugPrint((2, "CdRomCheckRegAP: paramDevPath [%d] = %ws\n",
+                paramPath.Length,
+                paramPath.Buffer));
+
+    parameters = ExAllocatePool(NonPagedPool,
+                                sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY);
+
+    if (parameters) {
+
+        //
+        // Check for the Autorun value.
+        //
+
+        RtlZeroMemory(parameters,
+                      (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));
+
+        parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
+        parameters[0].Name          = L"Autorun";
+        parameters[0].EntryContext  = &doRun;
+        parameters[0].DefaultType   = REG_DWORD;
+        parameters[0].DefaultData   = &zero;
+        parameters[0].DefaultLength = sizeof(ULONG);
+
+        status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+                                        RegistryPath->Buffer,
+                                        parameters,
+                                        NULL,
+                                        NULL);
+
+        DebugPrint((2, "CdRomCheckRegAP: cdrom/Autorun flag = %d\n", doRun));
+
+        RtlZeroMemory(parameters,
+                      (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));
+
+        parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
+        parameters[0].Name          = L"Autorun";
+        parameters[0].EntryContext  = &tmp;
+        parameters[0].DefaultType   = REG_DWORD;
+        parameters[0].DefaultData   = &doRun;
+        parameters[0].DefaultLength = sizeof(ULONG);
+
+        status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+                                        paramPath.Buffer,
+                                        parameters,
+                                        NULL,
+                                        NULL);
+
+        DebugPrint((2, "CdRomCheckRegAP: cdrom/parameters/autorun flag = %d\n", tmp));
+
+        RtlZeroMemory(parameters,
+                      (sizeof(RTL_QUERY_REGISTRY_TABLE) * ITEMS_TO_QUERY));
+
+        parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
+        parameters[0].Name          = L"Autorun";
+        parameters[0].EntryContext  = &doRun;
+        parameters[0].DefaultType   = REG_DWORD;
+        parameters[0].DefaultData   = &tmp;
+        parameters[0].DefaultLength = sizeof(ULONG);
+
+        status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+                                        paramDevPath.Buffer,
+                                        parameters,
+                                        NULL,
+                                        NULL);
+
+        DebugPrint((1, "CdRomCheckRegAP: cdrom/parameters/device%d/autorun flag = %d\n", DeviceNumber, doRun));
+
+        ExFreePool(parameters);
+
+    }
+
+    ExFreePool(paramPath.Buffer);
+    ExFreePool(paramDevPath.Buffer);
+    RtlFreeUnicodeString(&paramSuffix);
+
+    DebugPrint((1, "CdRomCheckRegAP: Autoplay for device %d is %s\n",
+                DeviceNumber,
+                (doRun ? "on" : "off")));
+
+    if(doRun) {
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
+\f
+BOOLEAN
+STDCALL
+IsThisASanyo(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  UCHAR          PathId,
+    IN  UCHAR          TargetId
+    )
 
-/**********************************************************************
- * NAME                                                        EXPORTED
- *     CdromClassDeviceControl
- *
- * DESCRIPTION:
- *     Answer requests for device control calls
- *
- * RUN LEVEL:
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS:
- *     DeviceObject
- *     Irp
- *             Standard dispatch arguments
- *
- * RETURNS:
- *     Status.
- */
+/*++
+
+Routine Description:
+
+    This routine is called by DriverEntry to determine whether a Sanyo 3-CD
+    changer device is present.
+
+Arguments:
+
+    DeviceObject - Supplies the device object for the 'real' device.
+
+    PathId       -
+
+Return Value:
+
+    TRUE - if an Atapi changer device is found.
+
+--*/
 
-NTSTATUS STDCALL
-CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
-                       IN PIRP Irp)
 {
-  PDEVICE_EXTENSION DeviceExtension;
-  PIO_STACK_LOCATION IrpStack;
-  ULONG ControlCode, InputLength, OutputLength;
-  PCDROM_DATA CdromData;
-  ULONG Information;
-  NTSTATUS Status;
-
-  DPRINT("CdromClassDeviceControl() called!\n");
-
-  Status = STATUS_INVALID_DEVICE_REQUEST;
-  Information = 0;
-  IrpStack = IoGetCurrentIrpStackLocation(Irp);
-  ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
-  InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
-  OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
-  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-  CdromData = (PCDROM_DATA)(DeviceExtension + 1);
-
-  switch (ControlCode)
-    {
-      case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
-       DPRINT ("CdromClassDeviceControl: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
-       if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
-         {
-           Status = STATUS_INFO_LENGTH_MISMATCH;
-           break;
-         }
-       IoMarkIrpPending (Irp);
-       IoStartPacket (DeviceObject,
-                      Irp,
-                      NULL,
-                      NULL);
-       return STATUS_PENDING;
-
-      case IOCTL_CDROM_CHECK_VERIFY:
-       DPRINT ("CdromClassDeviceControl: IOCTL_CDROM_CHECK_VERIFY\n");
-       if (OutputLength != 0 && OutputLength < sizeof (ULONG))
-         {
-           DPRINT1("Buffer too small\n");
-           Status = STATUS_BUFFER_TOO_SMALL;
-           break;
-         }
-       IoMarkIrpPending (Irp);
-       IoStartPacket (DeviceObject,
-                      Irp,
-                      NULL,
-                      NULL);
-       return STATUS_PENDING;
-
-      case IOCTL_CDROM_READ_TOC:
-       DPRINT("IOCTL_CDROM_READ_TOC\n");
-       if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC))
-         {
-           Status = STATUS_INFO_LENGTH_MISMATCH;
-         }
-       else
-         {
-           PCDROM_TOC TocBuffer;
-           USHORT Length;
-
-           TocBuffer = Irp->AssociatedIrp.SystemBuffer;
-
-           /* First read the lead out */
-           Length = 4 + sizeof(TRACK_DATA);
-           Status = CdromClassReadTocEntry(DeviceObject,
-                                           0xAA,
-                                           TocBuffer,
-                                           Length);
-           if (NT_SUCCESS(Status))
-             {
-               if (TocBuffer->FirstTrack == 0xaa)
-                 {
-                   /* there is an empty cd */
-                   Information = Length;
-                 }
-               else
-                 {
-                   /* read the toc */
-                   Length = 4 + sizeof(TRACK_DATA) * (TocBuffer->LastTrack - TocBuffer->FirstTrack + 2);
-                   Status = CdromClassReadTocEntry(DeviceObject,
-                                                   TocBuffer->FirstTrack,
-                                                   TocBuffer, Length);
-                   if (NT_SUCCESS(Status))
-                     {
-                       Information = Length;
-                     }
-                 }
-             }
-         }
-       break;
-
-      case IOCTL_CDROM_GET_LAST_SESSION:
-       DPRINT("IOCTL_CDROM_GET_LAST_SESSION\n");
-       if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < 4 + sizeof(TRACK_DATA))
-         {
-           Status = STATUS_INFO_LENGTH_MISMATCH;
-         }
-       else
-         {
-           PCDROM_TOC TocBuffer;
-           USHORT Length;
-
-           TocBuffer = Irp->AssociatedIrp.SystemBuffer;
-           Length = 4 + sizeof(TRACK_DATA);
-           Status = CdromClassReadLastSession(DeviceObject,
-                                              0,
-                                              TocBuffer,
-                                              Length);
-           if (NT_SUCCESS(Status))
-             {
-               Information = Length;
-             }
-         }
-       break;
-
-      default:
-       /* Call the common device control function */
-       return(ScsiClassDeviceControl(DeviceObject, Irp));
-    }
-
-  /* Verify the device if the user caused the error */
-  if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status))
-    {
-      IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
-    }
-
-  Irp->IoStatus.Status = Status;
-  Irp->IoStatus.Information = Information;
-  IoCompleteRequest(Irp,
-                   IO_DISK_INCREMENT);
-
-  return Status;
+    KEVENT                 event;
+    PIRP                   irp;
+    PCHAR                  inquiryBuffer;
+    IO_STATUS_BLOCK        ioStatus;
+    NTSTATUS               status;
+    PSCSI_ADAPTER_BUS_INFO adapterInfo;
+    ULONG                  scsiBus;
+    PINQUIRYDATA           inquiryData;
+    PSCSI_INQUIRY_DATA     lunInfo;
+
+    inquiryBuffer = ExAllocatePool(NonPagedPool, 2048);
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+    irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_INQUIRY_DATA,
+                                        DeviceObject,
+                                        NULL,
+                                        0,
+                                        inquiryBuffer,
+                                        2048,
+                                        FALSE,
+                                        &event,
+                                        &ioStatus);
+    if (!irp) {
+        return FALSE;
+    }
+
+    status = IoCallDriver(DeviceObject, irp);
+
+    if (status == STATUS_PENDING) {
+        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+        status = ioStatus.Status;
+    }
+
+    if (!NT_SUCCESS(status)) {
+        return FALSE;
+    }
+
+    adapterInfo = (PVOID) inquiryBuffer;
+
+    for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) {
+
+        //
+        // Get the SCSI bus scan data for this bus.
+        //
+
+        lunInfo = (PVOID) (inquiryBuffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
+
+        for (;;) {
+
+            if (lunInfo->PathId == PathId && lunInfo->TargetId == TargetId) {
+
+                inquiryData = (PVOID) lunInfo->InquiryData;
+
+                if (RtlCompareMemory(inquiryData->VendorId, "TORiSAN CD-ROM CDR-C", 20) == 20) {
+                    ExFreePool(inquiryBuffer);
+                    return TRUE;
+                }
+
+                ExFreePool(inquiryBuffer);
+                return FALSE;
+            }
+
+            if (!lunInfo->NextInquiryDataOffset) {
+                break;
+            }
+
+            lunInfo = (PVOID) (inquiryBuffer + lunInfo->NextInquiryDataOffset);
+        }
+    }
+
+    ExFreePool(inquiryBuffer);
+    return FALSE;
 }
 
+\f
+BOOLEAN
+STDCALL
+IsThisAnAtapiChanger(
+    IN  PDEVICE_OBJECT DeviceObject,
+    OUT PULONG         DiscsPresent
+    )
+
+/*++
+
+Routine Description:
+
+    This routine is called by DriverEntry to determine whether an Atapi
+    changer device is present.
+
+Arguments:
+
+    DeviceObject    - Supplies the device object for the 'real' device.
+
+    DiscsPresent    - Supplies a pointer to the number of Discs supported by the changer.
+
+Return Value:
+
+    TRUE - if an Atapi changer device is found.
+
+--*/
 
-/**********************************************************************
- * NAME
- *     CdromClassStartIo
- *
- * DESCRIPTION:
- *     Starts IRP processing.
- *
- * RUN LEVEL:
- *     PASSIVE_LEVEL
- *
- * ARGUMENTS:
- *     DeviceObject
- *     Irp
- *             Standard dispatch arguments
- *
- * RETURNS:
- *     None.
- */
-VOID STDCALL
-CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject,
-                  IN PIRP Irp)
 {
-  PDEVICE_EXTENSION DeviceExtension;
-  PIO_STACK_LOCATION IrpStack;
-  PIO_STACK_LOCATION SubIrpStack;
-  ULONG MaximumTransferLength;
-  ULONG TransferPages;
-  PSCSI_REQUEST_BLOCK Srb;
-  PIRP SubIrp;
-  PUCHAR SenseBuffer;
-  PVOID DataBuffer;
-  PCDB Cdb;
-
-  DPRINT("CdromClassStartIo() called!\n");
-
-  IoMarkIrpPending (Irp);
-
-  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-  IrpStack = IoGetCurrentIrpStackLocation (Irp);
-
-  MaximumTransferLength = DeviceExtension->PortCapabilities->MaximumTransferLength;
-
-  if (DeviceObject->Flags & DO_VERIFY_VOLUME)
-    {
-      if (!(IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
-       {
-         DPRINT1 ("Verify required\n");
-
-         if (Irp->Tail.Overlay.Thread)
-           {
-             IoSetHardErrorOrVerifyDevice (Irp,
-                                           DeviceObject);
-           }
-         Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
-
-         /* FIXME: Update drive capacity */
-         IoCompleteRequest (Irp,
-                            IO_DISK_INCREMENT);
-         IoStartNextPacket (DeviceObject,
-                            FALSE);
-         return;
-       }
-    }
-
-  if (IrpStack->MajorFunction == IRP_MJ_READ)
-    {
-      DPRINT ("CdromClassStartIo: IRP_MJ_READ\n");
-
-      TransferPages =
-       ADDRESS_AND_SIZE_TO_SPAN_PAGES (MmGetMdlVirtualAddress(Irp->MdlAddress),
-                                       IrpStack->Parameters.Read.Length);
-
-      /* Check transfer size */
-      if ((IrpStack->Parameters.Read.Length > MaximumTransferLength) ||
-         (TransferPages > DeviceExtension->PortCapabilities->MaximumPhysicalPages))
-       {
-         /* Transfer size is too large - split it */
-         TransferPages =
-           DeviceExtension->PortCapabilities->MaximumPhysicalPages - 1;
-
-         /* Adjust transfer size */
-         if (MaximumTransferLength > TransferPages * PAGE_SIZE)
-           MaximumTransferLength = TransferPages * PAGE_SIZE;
-
-         if (MaximumTransferLength == 0)
-           MaximumTransferLength = PAGE_SIZE;
-
-         /* Split the transfer */
-         ScsiClassSplitRequest (DeviceObject,
-                                Irp,
-                                MaximumTransferLength);
-         return;
-       }
-      else
-       {
-         /* Build SRB */
-         ScsiClassBuildRequest (DeviceObject,
-                                Irp);
-       }
-    }
-  else if (IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
-    {
-      DPRINT ("CdromClassStartIo: IRP_MJ_IRP_MJ_DEVICE_CONTROL\n");
-
-      /* Allocate an IRP for sending requests to the port driver */
-      SubIrp = IoAllocateIrp ((CCHAR)(DeviceObject->StackSize + 1),
-                             FALSE);
-      if (SubIrp == NULL)
-       {
-         Irp->IoStatus.Information = 0;
-         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-         IoCompleteRequest (Irp,
-                            IO_DISK_INCREMENT);
-         IoStartNextPacket (DeviceObject,
-                            FALSE);
-         return;
-       }
-
-      /* Allocate an SRB */
-      Srb = ExAllocatePool (NonPagedPool,
-                           sizeof (SCSI_REQUEST_BLOCK));
-      if (Srb == NULL)
-       {
-         IoFreeIrp (SubIrp);
-         Irp->IoStatus.Information = 0;
-         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-         IoCompleteRequest (Irp,
-                            IO_DISK_INCREMENT);
-         IoStartNextPacket (DeviceObject,
-                            FALSE);
-         return;
-       }
-
-      /* Allocte a sense buffer */
-      SenseBuffer = ExAllocatePool (NonPagedPoolCacheAligned,
-                                   SENSE_BUFFER_SIZE);
-      if (SenseBuffer == NULL)
-       {
-         ExFreePool (Srb);
-         IoFreeIrp (SubIrp);
-         Irp->IoStatus.Information = 0;
-         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-         IoCompleteRequest (Irp,
-                            IO_DISK_INCREMENT);
-         IoStartNextPacket (DeviceObject,
-                            FALSE);
-         return;
-       }
-
-      /* Initialize the IRP */
-      IoSetNextIrpStackLocation (SubIrp);
-      SubIrp->IoStatus.Information = 0;
-      SubIrp->IoStatus.Status = STATUS_SUCCESS;
-      SubIrp->Flags = 0;
-      SubIrp->UserBuffer = NULL;
-
-      SubIrpStack = IoGetCurrentIrpStackLocation (SubIrp);
-      SubIrpStack->DeviceObject = DeviceExtension->DeviceObject;
-      SubIrpStack->Parameters.Others.Argument2 = (PVOID)Irp;
-
-      /* Initialize next stack location */
-      SubIrpStack = IoGetNextIrpStackLocation (SubIrp);
-      SubIrpStack->MajorFunction = IRP_MJ_SCSI;
-      SubIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
-      SubIrpStack->Parameters.Scsi.Srb = Srb;
-
-      /* Initialize the SRB */
-      RtlZeroMemory(Srb,
-                   sizeof (SCSI_REQUEST_BLOCK));
-      Srb->Length = sizeof (SCSI_REQUEST_BLOCK);
-      Srb->PathId = DeviceExtension->PathId;
-      Srb->TargetId = DeviceExtension->TargetId;
-      Srb->Lun = DeviceExtension->Lun;
-      Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
-      Srb->SrbStatus = SRB_STATUS_SUCCESS;
-      Srb->ScsiStatus = 0;
-      Srb->NextSrb = 0;
-      Srb->OriginalRequest = SubIrp;
-      Srb->SenseInfoBuffer = SenseBuffer;
-      Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
-
-      /* Initialize the CDB */
-      Cdb = (PCDB)Srb->Cdb;
-
-      /* Set the completion routine */
-      IoSetCompletionRoutine (SubIrp,
-                             CdromDeviceControlCompletion,
-                             Srb,
-                             TRUE,
-                             TRUE,
-                             TRUE);
-
-      switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
-       {
-         case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
-           DPRINT ("CdromClassStartIo: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
-           Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
-           Srb->CdbLength = 10;
-           Srb->TimeOutValue = DeviceExtension->TimeOutValue;
-           Srb->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN;
-           Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
-
-           /* Allocate data buffer */
-           DataBuffer = ExAllocatePool (NonPagedPoolCacheAligned,
-                                        sizeof(READ_CAPACITY_DATA));
-           if (DataBuffer == NULL)
-             {
-               Irp->IoStatus.Information = 0;
-               Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-               IoCompleteRequest (Irp,
-                                  IO_DISK_INCREMENT);
-               ExFreePool (SenseBuffer);
-               ExFreePool (Srb);
-               IoFreeIrp (SubIrp);
-               IoStartNextPacket (DeviceObject,
-                                  FALSE);
-               return;
-             }
-
-           /* Allocate an MDL for the data buffer */
-           SubIrp->MdlAddress = IoAllocateMdl (DataBuffer,
-                                               sizeof(READ_CAPACITY_DATA),
-                                               FALSE,
-                                               FALSE,
-                                               NULL);
-           if (SubIrp->MdlAddress == NULL)
-             {
-               Irp->IoStatus.Information = 0;
-               Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
-               IoCompleteRequest (Irp,
-                                  IO_DISK_INCREMENT);
-               ExFreePool (DataBuffer);
-               ExFreePool (SenseBuffer);
-               ExFreePool (Srb);
-               IoFreeIrp (SubIrp);
-               IoStartNextPacket (DeviceObject,
-                                  FALSE);
-               return;
-             }
-
-           MmBuildMdlForNonPagedPool (SubIrp->MdlAddress);
-           Srb->DataBuffer = DataBuffer;
-
-        RtlZeroMemory(DataBuffer, sizeof(READ_CAPACITY_DATA));
-           IoCallDriver (DeviceExtension->PortDeviceObject,
-                         SubIrp);
-           return;
-
-         case IOCTL_CDROM_CHECK_VERIFY:
-           DPRINT ("CdromClassStartIo: IOCTL_CDROM_CHECK_VERIFY\n");
-           Srb->CdbLength = 6;
-           Srb->TimeOutValue = DeviceExtension->TimeOutValue * 2;
-           Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
-           Cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
-
-           IoCallDriver (DeviceExtension->PortDeviceObject,
-                         SubIrp);
-           return;
-
-         default:
-           IoCompleteRequest (Irp,
-                              IO_NO_INCREMENT);
-           return;
-       }
-    }
-
-  /* Call the SCSI port driver */
-  IoCallDriver (DeviceExtension->PortDeviceObject,
-               Irp);
+    PDEVICE_EXTENSION   deviceExtension = DeviceObject->DeviceExtension;
+    PMECHANICAL_STATUS_INFORMATION_HEADER mechanicalStatusBuffer;
+    NTSTATUS            status;
+    SCSI_REQUEST_BLOCK  srb;
+    PCDB                cdb = (PCDB) &srb.Cdb[0];
+    BOOLEAN             retVal = FALSE;
+
+    *DiscsPresent = 0;
+
+    //
+    // Some devices can't handle 12 byte CDB's gracefully
+    //
+
+    if(deviceExtension->DeviceFlags & DEV_NO_12BYTE_CDB) {
+
+        return FALSE;
+
+    }
+
+    //
+    // Build and issue the mechanical status command.
+    //
+
+    mechanicalStatusBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
+                                            sizeof(MECHANICAL_STATUS_INFORMATION_HEADER));
+
+    if (!mechanicalStatusBuffer) {
+        retVal = FALSE;
+    } else {
+
+        //
+        // Build and send the Mechanism status CDB.
+        //
+
+        RtlZeroMemory(&srb, sizeof(srb));
+
+        srb.CdbLength = 12;
+        srb.TimeOutValue = 20;
+
+        cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS;
+        cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
+
+        status = ScsiClassSendSrbSynchronous(DeviceObject,
+                                             &srb,
+                                             mechanicalStatusBuffer,
+                                             sizeof(MECHANICAL_STATUS_INFORMATION_HEADER),
+                                             FALSE);
+
+
+        if (status == STATUS_SUCCESS) {
+
+            //
+            // Indicate number of slots available
+            //
+
+            *DiscsPresent = mechanicalStatusBuffer->NumberAvailableSlots;
+            if (*DiscsPresent > 1) {
+                retVal = TRUE;
+            } else {
+
+                //
+                // If only one disc, no need for this driver.
+                //
+
+                retVal = FALSE;
+            }
+        } else {
+
+            //
+            // Device doesn't support this command.
+            //
+
+            retVal = FALSE;
+        }
+
+        ExFreePool(mechanicalStatusBuffer);
+    }
+
+    return retVal;
 }
 
+\f
+BOOLEAN
+STDCALL
+IsThisAMultiLunDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PDEVICE_OBJECT PortDeviceObject
+    )
+/*++
+
+Routine Description:
+
+    This routine is called to determine whether a multi-lun
+    device is present.
+
+Arguments:
+
+    DeviceObject    - Supplies the device object for the 'real' device.
+
+Return Value:
 
-NTSTATUS STDCALL
-CdromDeviceControlCompletion (IN PDEVICE_OBJECT DeviceObject,
-                             IN PIRP Irp,
-                             IN PVOID Context)
+    TRUE - if a Multi-lun device is found.
+
+--*/
 {
-  PDEVICE_EXTENSION DeviceExtension;
-  PDEVICE_EXTENSION PhysicalExtension;
-  PIO_STACK_LOCATION IrpStack;
-  PIO_STACK_LOCATION OrigCurrentIrpStack;
-  PIO_STACK_LOCATION OrigNextIrpStack;
-  PSCSI_REQUEST_BLOCK Srb;
-  PIRP OrigIrp;
-  BOOLEAN Retry;
-  NTSTATUS Status;
-
-  DPRINT ("CdromDeviceControlCompletion() called\n");
-
-  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
-  PhysicalExtension = (PDEVICE_EXTENSION)DeviceExtension->PhysicalDevice->DeviceExtension;
-  Srb = (PSCSI_REQUEST_BLOCK) Context;
-
-  IrpStack = IoGetCurrentIrpStackLocation (Irp);
-
-  /* Get the original IRP */
-  OrigIrp = (PIRP)IrpStack->Parameters.Others.Argument2;
-  OrigCurrentIrpStack = IoGetCurrentIrpStackLocation (OrigIrp);
-  OrigNextIrpStack = IoGetNextIrpStackLocation (OrigIrp);
-
-  if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
-    {
-      Status = STATUS_SUCCESS;
-    }
-  else
-    {
-      DPRINT ("SrbStatus %lx\n", Srb->SrbStatus);
-
-      /* Interpret sense info */
-      Retry = ScsiClassInterpretSenseInfo (DeviceObject,
-                                          Srb,
-                                          IrpStack->MajorFunction,
-                                          IrpStack->Parameters.DeviceIoControl.IoControlCode,
-                                          MAXIMUM_RETRIES - (ULONG)OrigNextIrpStack->Parameters.Others.Argument1,
-                                          &Status);
-      DPRINT ("Retry %u\n", Retry);
-
-      if (Retry == TRUE &&
-         (ULONG)OrigNextIrpStack->Parameters.Others.Argument1 > 0)
-       {
-         DPRINT1 ("Try again (Retry count 0x%p)\n",
-                  (ULONG)OrigNextIrpStack->Parameters.Others.Argument1);
-
-         OrigNextIrpStack->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)OrigNextIrpStack->Parameters.Others.Argument1 - 1);
-
-         /* Release 'old' buffers */
-         ExFreePool (Srb->SenseInfoBuffer);
-         if (Srb->DataBuffer)
-           ExFreePool(Srb->DataBuffer);
-         ExFreePool(Srb);
-
-         if (Irp->MdlAddress != NULL)
-           IoFreeMdl(Irp->MdlAddress);
-
-         IoFreeIrp(Irp);
-
-         /* Call the StartIo routine again */
-         CdromClassStartIo (DeviceObject,
-                            OrigIrp);
-
-         return STATUS_MORE_PROCESSING_REQUIRED;
-       }
-
-      DPRINT ("Status %lx\n", Status);
-    }
-
-  if (NT_SUCCESS (Status))
-    {
-      switch (OrigCurrentIrpStack->Parameters.DeviceIoControl.IoControlCode)
-       {
-         case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
-           {
-             PREAD_CAPACITY_DATA CapacityBuffer;
-             ULONG LastSector;
-             ULONG SectorSize;
-
-             DPRINT ("CdromClassControlCompletion: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
-
-             CapacityBuffer = (PREAD_CAPACITY_DATA)Srb->DataBuffer;
-             SectorSize = (((PUCHAR)&CapacityBuffer->BytesPerBlock)[0] << 24) |
-                          (((PUCHAR)&CapacityBuffer->BytesPerBlock)[1] << 16) |
-                          (((PUCHAR)&CapacityBuffer->BytesPerBlock)[2] << 8) |
-                           ((PUCHAR)&CapacityBuffer->BytesPerBlock)[3];
-
-             LastSector = (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[0] << 24) |
-                          (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[1] << 16) |
-                          (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[2] << 8) |
-                           ((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[3];
-
-             if (SectorSize == 0)
-               SectorSize = 2048;
-             DeviceExtension->DiskGeometry->BytesPerSector = SectorSize;
-
-             DeviceExtension->PartitionLength.QuadPart = (LONGLONG)(LastSector + 1);
-             WHICH_BIT(DeviceExtension->DiskGeometry->BytesPerSector,
-                       DeviceExtension->SectorShift);
-             DeviceExtension->PartitionLength.QuadPart =
-               (DeviceExtension->PartitionLength.QuadPart << DeviceExtension->SectorShift);
-
-             if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
-               {
-                 DeviceExtension->DiskGeometry->MediaType = RemovableMedia;
-               }
-             else
-               {
-                 DeviceExtension->DiskGeometry->MediaType = FixedMedia;
-               }
-             DeviceExtension->DiskGeometry->Cylinders.QuadPart =
-               (LONGLONG)((LastSector + 1)/(32 * 64));
-             DeviceExtension->DiskGeometry->SectorsPerTrack = 32;
-             DeviceExtension->DiskGeometry->TracksPerCylinder = 64;
-
-             RtlCopyMemory (OrigIrp->AssociatedIrp.SystemBuffer,
-                            DeviceExtension->DiskGeometry,
-                            sizeof(DISK_GEOMETRY));
-             OrigIrp->IoStatus.Information = sizeof(DISK_GEOMETRY);
-           }
-           break;
-
-         case IOCTL_CDROM_CHECK_VERIFY:
-           DPRINT ("CdromDeviceControlCompletion: IOCTL_CDROM_CHECK_VERIFY\n");
-           if (OrigCurrentIrpStack->Parameters.DeviceIoControl.OutputBufferLength != 0)
-             {
-               /* Return the media change counter */
-               *((PULONG)(OrigIrp->AssociatedIrp.SystemBuffer)) =
-                 PhysicalExtension->MediaChangeCount;
-               OrigIrp->IoStatus.Information = sizeof(ULONG);
-             }
-           else
-             {
-               OrigIrp->IoStatus.Information = 0;
-             }
-           break;
-
-         default:
-           OrigIrp->IoStatus.Information = 0;
-           Status = STATUS_INVALID_DEVICE_REQUEST;
-           break;
-       }
-    }
-
-  /* Release the SRB and associated buffers */
-  if (Srb != NULL)
-    {
-      DPRINT("Srb %p\n", Srb);
-
-      if (Srb->DataBuffer != NULL)
-       ExFreePool (Srb->DataBuffer);
-
-      if (Srb->SenseInfoBuffer != NULL)
-       ExFreePool (Srb->SenseInfoBuffer);
-
-      ExFreePool (Srb);
-    }
-
-  if (OrigIrp->PendingReturned)
-    {
-      IoMarkIrpPending (OrigIrp);
-    }
-
-  /* Release the MDL */
-  if (Irp->MdlAddress != NULL)
-    {
-      IoFreeMdl (Irp->MdlAddress);
-    }
-
-  /* Release the sub irp */
-  IoFreeIrp (Irp);
-
-  /* Set io status information */
-  OrigIrp->IoStatus.Status = Status;
-  if (!NT_SUCCESS(Status) && IoIsErrorUserInduced (Status))
-    {
-      IoSetHardErrorOrVerifyDevice (OrigIrp,
-                                   DeviceObject);
-      OrigIrp->IoStatus.Information = 0;
-    }
-
-  /* Complete the original IRP */
-  IoCompleteRequest (OrigIrp,
-                    IO_DISK_INCREMENT);
-  IoStartNextPacket (DeviceObject,
-                    FALSE);
-
-  DPRINT ("CdromDeviceControlCompletion() done\n");
-
-  return STATUS_MORE_PROCESSING_REQUIRED;
+    PCHAR buffer;
+    PSCSI_INQUIRY_DATA lunInfo;
+    PSCSI_ADAPTER_BUS_INFO  adapterInfo;
+    PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
+    PINQUIRYDATA inquiryData;
+    ULONG scsiBus;
+    NTSTATUS status;
+    UCHAR lunCount = 0;
+
+    status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
+
+    if (!NT_SUCCESS(status)) {
+        DebugPrint((1,"IsThisAMultiLunDevice: ScsiClassGetInquiryData failed\n"));
+        return FALSE;
+    }
+
+    adapterInfo = (PVOID) buffer;
+
+    //
+    // For each SCSI bus this adapter supports ...
+    //
+
+    for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {
+
+        //
+        // Get the SCSI bus scan data for this bus.
+        //
+
+        lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
+
+        while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
+
+            inquiryData = (PVOID)lunInfo->InquiryData;
+
+            if ((lunInfo->PathId == deviceExtension->PathId) &&
+                (lunInfo->TargetId == deviceExtension->TargetId) &&
+                (inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)) {
+
+                DebugPrint((1,"IsThisAMultiLunDevice: Vendor string is %.24s\n",
+                            inquiryData->VendorId));
+
+                //
+                // If this device has more than one cdrom-type lun then we
+                // won't support autoplay on it
+                //
+
+                if (lunCount++) {
+                    ExFreePool(buffer);
+                    return TRUE;
+                }
+            }
+
+            //
+            // Get next LunInfo.
+            //
+
+            if (lunInfo->NextInquiryDataOffset == 0) {
+                break;
+            }
+
+            lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
+        }
+    }
+
+    ExFreePool(buffer);
+    return FALSE;
+
 }
 
+\f
+NTSTATUS
+STDCALL
+CdRomUpdateGeometryCompletion(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp,
+    PVOID Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine andles the completion of the test unit ready irps
+    used to determine if the media has changed.  If the media has
+    changed, this code signals the named event to wake up other
+    system services that react to media change (aka AutoPlay).
+
+Arguments:
+
+    DeviceObject - the object for the completion
+    Irp - the IRP being completed
+    Context - the SRB from the IRP
+
+Return Value:
+
+    NTSTATUS
+
+--*/
 
-VOID STDCALL
-CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject,
-                 IN PVOID Context)
 {
-  PIO_WORKITEM WorkItem;
+    PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context;
+    PREAD_CAPACITY_DATA readCapacityBuffer;
+    PDEVICE_EXTENSION   deviceExtension;
+    PIO_STACK_LOCATION  irpStack;
+    NTSTATUS            status;
+    BOOLEAN             retry;
+    ULONG               retryCount;
+    ULONG               lastSector;
+    PIRP                originalIrp;
+    PCDROM_DATA         cddata;
+
+    //
+    // Get items saved in the private IRP stack location.
+    //
+
+    irpStack = IoGetCurrentIrpStackLocation(Irp);
+    retryCount = (ULONG) irpStack->Parameters.Others.Argument1;
+    originalIrp = (PIRP) irpStack->Parameters.Others.Argument2;
+
+    if (!DeviceObject) {
+        DeviceObject = irpStack->DeviceObject;
+    }
+    ASSERT(DeviceObject);
+
+    deviceExtension = DeviceObject->DeviceExtension;
+    cddata = (PCDROM_DATA) (deviceExtension + 1);
+    readCapacityBuffer = srb->DataBuffer;
+
+    if ((NT_SUCCESS(Irp->IoStatus.Status)) && (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)) {
+        PFOUR_BYTE from;
+        PFOUR_BYTE to;
+
+        DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] successful completion of buddy-irp %lx\n", originalIrp, Irp));
+        //
+        // Copy sector size from read capacity buffer to device extension
+        // in reverse byte order.
+        //
+
+        from = (PFOUR_BYTE) &readCapacityBuffer->BytesPerBlock;
+        to = (PFOUR_BYTE) &deviceExtension->DiskGeometry->BytesPerSector;
+        to->Byte0 = from->Byte3;
+        to->Byte1 = from->Byte2;
+        to->Byte2 = from->Byte1;
+        to->Byte3 = from->Byte0;
+
+        //
+        // Using the new BytesPerBlock, calculate and store the SectorShift.
+        //
+
+        WHICH_BIT(deviceExtension->DiskGeometry->BytesPerSector, deviceExtension->SectorShift);
+
+        //
+        // Copy last sector in reverse byte order.
+        //
+
+        from = (PFOUR_BYTE) &readCapacityBuffer->LogicalBlockAddress;
+        to = (PFOUR_BYTE) &lastSector;
+        to->Byte0 = from->Byte3;
+        to->Byte1 = from->Byte2;
+        to->Byte2 = from->Byte1;
+        to->Byte3 = from->Byte0;
+        deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
+
+        //
+        // Calculate number of cylinders.
+        //
+
+        deviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
+        deviceExtension->PartitionLength.QuadPart =
+            (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
+        deviceExtension->DiskGeometry->MediaType = RemovableMedia;
+
+        //
+        // Assume sectors per track are 32;
+        //
+
+        deviceExtension->DiskGeometry->SectorsPerTrack = 32;
+
+        //
+        // Assume tracks per cylinder (number of heads) is 64.
+        //
+
+        deviceExtension->DiskGeometry->TracksPerCylinder = 64;
+
+    } else {
+
+        DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] unsuccessful completion of buddy-irp %lx (status - %lx)\n", originalIrp, Irp, Irp->IoStatus.Status));
+
+        if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+            ScsiClassReleaseQueue(DeviceObject);
+        }
+
+        retry = ScsiClassInterpretSenseInfo(DeviceObject,
+                                            srb,
+                                            IRP_MJ_SCSI,
+                                            0,
+                                            retryCount,
+                                            &status);
+        if (retry) {
+            retryCount--;
+            if (retryCount) {
+                PCDB cdb;
+
+                DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] Retrying request %lx .. thread is %lx\n", originalIrp, Irp, Irp->Tail.Overlay.Thread));
+                //
+                // set up a one shot timer to get this process started over
+                //
+
+                irpStack->Parameters.Others.Argument1 = (PVOID) retryCount;
+                irpStack->Parameters.Others.Argument2 = (PVOID) originalIrp;
+                irpStack->Parameters.Others.Argument3 = (PVOID) 2;
+
+                //
+                // Setup the IRP to be submitted again in the timer routine.
+                //
+
+                irpStack = IoGetNextIrpStackLocation(Irp);
+                irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+                irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+                irpStack->Parameters.Scsi.Srb = srb;
+                IoSetCompletionRoutine(Irp,
+                                       CdRomUpdateGeometryCompletion,
+                                       srb,
+                                       TRUE,
+                                       TRUE,
+                                       TRUE);
+
+                //
+                // Set up the SRB for read capacity.
+                //
+
+                srb->CdbLength = 10;
+                srb->TimeOutValue = deviceExtension->TimeOutValue;
+                srb->SrbStatus = srb->ScsiStatus = 0;
+                srb->NextSrb = 0;
+                srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+                srb->PathId = deviceExtension->PathId;
+                srb->TargetId = deviceExtension->TargetId;
+                srb->Lun = deviceExtension->Lun;
+                srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+                srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+                srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
+
+                //
+                // Set up the CDB
+                //
+
+                cdb = (PCDB) &srb->Cdb[0];
+                cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+                cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun;
+
+                //
+                // Requests queued onto this list will be sent to the
+                // lower level driver during CdRomTickHandler
+                //
+
+                ExInterlockedInsertHeadList(&cddata->TimerIrpList,
+                                            &Irp->Tail.Overlay.ListEntry,
+                                            &cddata->TimerIrpSpinLock);
+
+                return STATUS_MORE_PROCESSING_REQUIRED;
+            } else {
+
+                //
+                // This has been bounced for a number of times.  Error the
+                // original request.
+                //
+
+                originalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+                RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY));
+                deviceExtension->DiskGeometry->BytesPerSector = 2048;
+                deviceExtension->SectorShift = 11;
+                deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
+                deviceExtension->DiskGeometry->MediaType = RemovableMedia;
+            }
+        } else {
+
+            //
+            // Set up reasonable defaults
+            //
+
+            RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY));
+            deviceExtension->DiskGeometry->BytesPerSector = 2048;
+            deviceExtension->SectorShift = 11;
+            deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
+            deviceExtension->DiskGeometry->MediaType = RemovableMedia;
+        }
+    }
+
+    //
+    // Free resources held.
+    //
+
+    ExFreePool(srb->SenseInfoBuffer);
+    ExFreePool(srb->DataBuffer);
+    ExFreePool(srb);
+    if (Irp->MdlAddress) {
+        IoFreeMdl(Irp->MdlAddress);
+    }
+    IoFreeIrp(Irp);
+    if (originalIrp->Tail.Overlay.Thread) {
+
+        DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] completing original IRP\n", originalIrp));
+        IoCompleteRequest(originalIrp, IO_DISK_INCREMENT);
 
-  DPRINT ("CdromTimerRoutine() called\n");
-  WorkItem = IoAllocateWorkItem(DeviceObject);
-  if (!WorkItem)
-    {
-      return;
+    } else {
+        DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] original irp has "
+                       "no thread\n",
+                    originalIrp
+                  ));
     }
 
-  IoQueueWorkItem(WorkItem,
-                 CdromWorkItem,
-                 DelayedWorkQueue,
-                 WorkItem);
+    //
+    // It's now safe to either start the next request or let the waiting ioctl
+    // request continue along it's merry way
+    //
+
+    IoStartNextPacket(DeviceObject, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
+\f
+NTSTATUS
+STDCALL
+CdRomUpdateCapacity(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PIRP IrpToComplete,
+    IN OPTIONAL PKEVENT IoctlEvent
+    )
+
+/*++
+
+Routine Description:
+
+    This routine updates the capacity of the disk as recorded in the device extension.
+    It also completes the IRP given with STATUS_VERIFY_REQUIRED.  This routine is called
+    when a media change has occurred and it is necessary to determine the capacity of the
+    new media prior to the next access.
+
+Arguments:
+
+    DeviceExtension - the device to update
+    IrpToComplete - the request that needs to be completed when done.
+
+Return Value:
+
+    NTSTATUS
+
+--*/
 
-VOID STDCALL
-CdromWorkItem(IN PDEVICE_OBJECT DeviceObject,
-             IN PVOID Context)
 {
-  PIRP Irp;
-  KEVENT Event;
-  IO_STATUS_BLOCK IoStatus;
-  NTSTATUS Status;
-
-  DPRINT("CdromWorkItem() called\n");
-
-  IoFreeWorkItem((PIO_WORKITEM) Context);
-
-  KeInitializeEvent(&Event,
-                   NotificationEvent,
-                   FALSE);
-
-  Irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_CHECK_VERIFY,
-                                     DeviceObject,
-                                     NULL,
-                                     0,
-                                     NULL,
-                                     0,
-                                     FALSE,
-                                     &Event,
-                                     &IoStatus);
-  if (Irp == NULL)
-    {
-      DPRINT("IoBuildDeviceIoControlRequest failed\n");
-      return;
-    }
-
-  Status = IoCallDriver(DeviceObject, Irp);
-  DPRINT("Status: %x\n", Status);
-
-  if (Status == STATUS_PENDING)
-    {
-      KeWaitForSingleObject(&Event,
-                           Suspended,
-                           KernelMode,
-                           FALSE,
-                           NULL);
+    PCDB                cdb;
+    PIRP                irp;
+    PSCSI_REQUEST_BLOCK srb;
+    PREAD_CAPACITY_DATA capacityBuffer;
+    PIO_STACK_LOCATION  irpStack;
+    PUCHAR              senseBuffer;
+    NTSTATUS            status;
+
+    irp = IoAllocateIrp((CCHAR)(DeviceExtension->DeviceObject->StackSize+1),
+                        FALSE);
+
+    if (irp) {
+
+        srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
+        if (srb) {
+            capacityBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
+                                            sizeof(READ_CAPACITY_DATA));
+
+            if (capacityBuffer) {
+
+
+                senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
+
+                if (senseBuffer) {
+
+                    irp->MdlAddress = IoAllocateMdl(capacityBuffer,
+                                                    sizeof(READ_CAPACITY_DATA),
+                                                    FALSE,
+                                                    FALSE,
+                                                    (PIRP) NULL);
+
+                    if (irp->MdlAddress) {
+
+                        //
+                        // Have all resources.  Set up the IRP to send for the capacity.
+                        //
+
+                        IoSetNextIrpStackLocation(irp);
+                        irp->IoStatus.Status = STATUS_SUCCESS;
+                        irp->IoStatus.Information = 0;
+                        irp->Flags = 0;
+                        irp->UserBuffer = NULL;
+
+                        //
+                        // Save the device object and retry count in a private stack location.
+                        //
+
+                        irpStack = IoGetCurrentIrpStackLocation(irp);
+                        irpStack->DeviceObject = DeviceExtension->DeviceObject;
+                        irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
+                        irpStack->Parameters.Others.Argument2 = (PVOID) IrpToComplete;
+
+                        //
+                        // Construct the IRP stack for the lower level driver.
+                        //
+
+                        irpStack = IoGetNextIrpStackLocation(irp);
+                        irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+                        irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+                        irpStack->Parameters.Scsi.Srb = srb;
+                        IoSetCompletionRoutine(irp,
+                                               CdRomUpdateGeometryCompletion,
+                                               srb,
+                                               TRUE,
+                                               TRUE,
+                                               TRUE);
+                        //
+                        // Prepare the MDL
+                        //
+
+                        MmBuildMdlForNonPagedPool(irp->MdlAddress);
+
+
+                        //
+                        // Set up the SRB for read capacity.
+                        //
+
+                        RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+                        RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE);
+                        srb->CdbLength = 10;
+                        srb->TimeOutValue = DeviceExtension->TimeOutValue;
+                        srb->SrbStatus = srb->ScsiStatus = 0;
+                        srb->NextSrb = 0;
+                        srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+                        srb->PathId = DeviceExtension->PathId;
+                        srb->TargetId = DeviceExtension->TargetId;
+                        srb->Lun = DeviceExtension->Lun;
+                        srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+                        srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+                        srb->DataBuffer = capacityBuffer;
+                        srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
+                        srb->OriginalRequest = irp;
+                        srb->SenseInfoBuffer = senseBuffer;
+                        srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+                        //
+                        // Set up the CDB
+                        //
+
+                        cdb = (PCDB) &srb->Cdb[0];
+                        cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+                        cdb->CDB10.LogicalUnitNumber = DeviceExtension->Lun;
+
+                        //
+                        // Set the return value in the IRP that will be completed
+                        // upon completion of the read capacity.
+                        //
+
+                        IrpToComplete->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+                        IoMarkIrpPending(IrpToComplete);
+
+                        status = IoCallDriver(DeviceExtension->PortDeviceObject, irp);
+
+                        //
+                        // status is not checked because the completion routine for this
+                        // IRP will always get called and it will free the resources.
+                        //
+
+                        return STATUS_PENDING;
+
+                    } else {
+                        ExFreePool(senseBuffer);
+                        ExFreePool(capacityBuffer);
+                        ExFreePool(srb);
+                        IoFreeIrp(irp);
+                    }
+                } else {
+                    ExFreePool(capacityBuffer);
+                    ExFreePool(srb);
+                    IoFreeIrp(irp);
+                }
+            } else {
+                ExFreePool(srb);
+                IoFreeIrp(irp);
+            }
+        } else {
+            IoFreeIrp(irp);
+        }
     }
+
+    return STATUS_INSUFFICIENT_RESOURCES;
 }
 
-/* EOF */
+\f
+NTSTATUS
+STDCALL
+CdRomClassIoctlCompletion(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp,
+    IN PVOID Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine signals the event used by CdRomDeviceControl to synchronize
+    class driver (and lower level driver) ioctls with cdrom's startio routine.
+    The irp completion is short-circuited so that CdRomDeviceControl can
+    reissue it once it wakes up.
+
+Arguments:
+
+    DeviceObject - the device object
+    Irp - the request we are synchronizing
+    Context - a PKEVENT that we need to signal
+
+Return Value:
+
+    NTSTATUS
+
+--*/
+
+{
+    PKEVENT syncEvent = (PKEVENT) Context;
+
+    DebugPrint((2, "CdRomClassIoctlCompletion: setting event for irp %#08lx\n",
+                Irp
+                ));
+
+    KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}