atapi, buslogic, cdrom, class2.
[reactos.git] / reactos / drivers / storage / cdrom / cdrom.c
index daad42b..8b68040 100644 (file)
@@ -16,7 +16,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: cdrom.c,v 1.9 2002/05/25 13:29:58 ekohl Exp $
+/* $Id$
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
  */
 
-/* INCLUDES *****************************************************************/
+/*
+ * TODO:
+ *  - Add io timer routine for autorun support.
+ *  - Add cdaudio support (cd player).
+ */
 
-#include <ddk/ntddk.h>
+/* INCLUDES *****************************************************************/
 
-#include "../include/scsi.h"
-#include "../include/class2.h"
-#include "../include/ntddscsi.h"
+#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 VERSION "0.0.1"
 
 
+#define SCSI_CDROM_TIMEOUT 10          /* Default timeout: 10 seconds */
+
+
+typedef struct _ERROR_RECOVERY_DATA6
+{
+  MODE_PARAMETER_HEADER Header;
+  MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
+} ERROR_RECOVERY_DATA6, *PERROR_RECOVERY_DATA6;
+
+
+typedef struct _ERROR_RECOVERY_DATA10
+{
+  MODE_PARAMETER_HEADER10 Header;
+  MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
+} ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10;
+
+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;
+
+typedef struct _MODE_CAPABILITIES_DATA10
+{
+  MODE_PARAMETER_HEADER10 Header;
+  MODE_CAPABILITIES_PAGE2 CababilitiesPage;
+} MODE_CAPABILITIES_DATA10, *PMODE_CAPABILITIES_DATA10;
+
 typedef struct _CDROM_DATA
 {
-  ULONG Dummy;
+  BOOLEAN PlayActive;
+  BOOLEAN RawAccess;
+  USHORT XaFlags;
+
 } CDROM_DATA, *PCDROM_DATA;
 
+/* 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
@@ -60,9 +124,13 @@ 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, /* what's this used for? */
+                            IN PUNICODE_STRING RegistryPath,
                             IN PDEVICE_OBJECT PortDeviceObject,
                             IN ULONG PortNumber,
                             IN ULONG DeviceNumber,
@@ -75,31 +143,47 @@ NTSTATUS STDCALL
 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
                        IN PIRP Irp);
 
+VOID STDCALL
+CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject,
+                  IN PIRP Irp);
+
 NTSTATUS STDCALL
-CdromClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
-                       IN PIRP Irp);
+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 ****************************************************************/
 
-//    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:
-//    IN  PDRIVER_OBJECT   DriverObject  System allocated Driver Object
-//                                       for this driver
-//    IN  PUNICODE_STRING  RegistryPath  Name of registry driver service 
-//                                       key
-//
-//  RETURNS:
-//    NTSTATUS
+/**********************************************************************
+ * 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.
+ */
 
 NTSTATUS STDCALL
 DriverEntry(IN PDRIVER_OBJECT DriverObject,
@@ -107,8 +191,8 @@ DriverEntry(IN PDRIVER_OBJECT DriverObject,
 {
   CLASS_INIT_DATA InitData;
 
-  DbgPrint("CD-ROM Class Driver %s\n",
-          VERSION);
+  DPRINT("CD-ROM Class Driver %s\n",
+        VERSION);
   DPRINT("RegistryPath '%wZ'\n",
         RegistryPath);
 
@@ -117,14 +201,14 @@ DriverEntry(IN PDRIVER_OBJECT DriverObject,
   InitData.DeviceType = FILE_DEVICE_CD_ROM;
   InitData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
 
-  InitData.ClassError = NULL;                          // CdromClassProcessError;
+  InitData.ClassError = NULL;
   InitData.ClassReadWriteVerification = CdromClassCheckReadWrite;
   InitData.ClassFindDeviceCallBack = CdromClassCheckDevice;
   InitData.ClassFindDevices = CdromClassFindDevices;
   InitData.ClassDeviceControl = CdromClassDeviceControl;
-  InitData.ClassShutdownFlush = CdromClassShutdownFlush;
+  InitData.ClassShutdownFlush = NULL;
   InitData.ClassCreateClose = NULL;
-  InitData.ClassStartIo = NULL;
+  InitData.ClassStartIo = CdromClassStartIo;
 
   return(ScsiClassInitialize(DriverObject,
                             RegistryPath,
@@ -132,25 +216,33 @@ DriverEntry(IN PDRIVER_OBJECT DriverObject,
 }
 
 
-//    CdromClassFindDevices
-//
-//  DESCRIPTION:
-//    This function searches for device that are attached to the given scsi port.
-//
-//  RUN LEVEL:
-//    PASSIVE_LEVEL
-//
-//  ARGUMENTS:
-//    IN  PDRIVER_OBJECT   DriverObject        System allocated Driver Object for this driver
-//    IN  PUNICODE_STRING  RegistryPath        Name of registry driver service key
-//    IN  PCLASS_INIT_DATA InitializationData  Pointer to the main initialization data
-//    IN PDEVICE_OBJECT    PortDeviceObject    Scsi port device object
-//    IN ULONG             PortNumber          Port number
-//
-//  RETURNS:
-//    TRUE: At least one disk drive was found
-//    FALSE: No disk drive found
-//
+/**********************************************************************
+ * 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
+ */
 
 BOOLEAN STDCALL
 CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
@@ -167,7 +259,7 @@ CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
   PCHAR Buffer;
   ULONG Bus;
   ULONG DeviceCount;
-  BOOLEAN FoundDevice;
+  BOOLEAN FoundDevice = FALSE;
   NTSTATUS Status;
 
   DPRINT("CdromClassFindDevices() called.\n");
@@ -181,7 +273,9 @@ CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
       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,
@@ -198,7 +292,7 @@ CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
                                              AdapterBusInfo);
   if (DeviceCount == 0)
     {
-      DPRINT1("No unclaimed devices!\n");
+      DPRINT("No unclaimed devices!\n");
       return(FALSE);
     }
 
@@ -230,13 +324,13 @@ CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
                                                    RegistryPath,
                                                    PortDeviceObject,
                                                    PortNumber,
-                                                   ConfigInfo->CDRomCount,
+                                                   ConfigInfo->CdRomCount,
                                                    PortCapabilities,
                                                    UnitInfo,
                                                    InitializationData);
              if (NT_SUCCESS(Status))
                {
-                 ConfigInfo->CDRomCount++;
+                 ConfigInfo->CdRomCount++;
                  FoundDevice = TRUE;
                }
            }
@@ -249,11 +343,10 @@ CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
     }
 
   ExFreePool(Buffer);
-  ExFreePool(PortCapabilities);
 
   DPRINT("CdromClassFindDevices() done\n");
 
-  return(TRUE);
+  return(FoundDevice);
 }
 
 
@@ -298,7 +391,6 @@ CdromClassCheckDevice(IN PINQUIRYDATA InquiryData)
  * ARGUMENTS
  *     DeviceObject
  *             Pointer to the device.
- *
  *     Irp
  *             Irp to check.
  *
@@ -317,33 +409,49 @@ CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
 }
 
 
-//    CdromClassCreateDeviceObject
-//
-//  DESCRIPTION:
-//    Create the raw device and any partition devices on this drive
-//
-//  RUN LEVEL:
-//    PASSIVE_LEVEL
-//
-//  ARGUMENTS:
-//    IN  PDRIVER_OBJECT  DriverObject  The system created driver object
-//    IN  PCONTROLLER_OBJECT         ControllerObject
-//    IN  PIDE_CONTROLLER_EXTENSION  ControllerExtension
-//                                      The IDE controller extension for
-//                                      this device
-//    IN  int             DriveIdx      The index of the drive on this
-//                                      controller
-//    IN  int             HarddiskIdx   The NT device number for this
-//                                      drive
-//
-//  RETURNS:
-//    TRUE   Drive exists and devices were created
-//    FALSE  no devices were created for this device
-//
+static VOID
+CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN ULONG DeviceNumber)
+{
+
+
+  DeviceExtension->MediaChangeEvent =
+    IoCreateSynchronizationEvent (NULL,
+                                 &DeviceExtension->MediaChangeEventHandle);
+
+  KeClearEvent (DeviceExtension->MediaChangeEvent);
+}
+
+
+/**********************************************************************
+ * 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.
+ */
 
 static NTSTATUS
 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
-                            IN PUNICODE_STRING RegistryPath, /* what's this used for? */
+                            IN PUNICODE_STRING RegistryPath,
                             IN PDEVICE_OBJECT PortDeviceObject,
                             IN ULONG PortNumber,
                             IN ULONG DeviceNumber,
@@ -351,13 +459,14 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
                             IN PSCSI_INQUIRY_DATA InquiryData,
                             IN PCLASS_INIT_DATA InitializationData)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING UnicodeDeviceDirName;
-  CHAR NameBuffer[80];
-  PDEVICE_OBJECT DiskDeviceObject;
   PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */
-  HANDLE Handle;
+  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");
@@ -408,12 +517,10 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
   DiskDeviceExtension->LockCount = 0;
   DiskDeviceExtension->DeviceNumber = DeviceNumber;
+  DiskDeviceExtension->DeviceObject = DiskDeviceObject;
   DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
   DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
-
-  /* FIXME: Not yet! Will cause pointer corruption! */
-//  DiskDeviceExtension->PortCapabilities = PortCapabilities;
-
+  DiskDeviceExtension->PortCapabilities = Capabilities;
   DiskDeviceExtension->StartingOffset.QuadPart = 0;
   DiskDeviceExtension->PortNumber = (UCHAR)PortNumber;
   DiskDeviceExtension->PathId = InquiryData->PathId;
@@ -442,6 +549,16 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
       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));
@@ -449,6 +566,8 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
     {
       DPRINT1("Failed to allocate geometry buffer!\n");
 
+      ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
+
       IoDeleteDevice(DiskDeviceObject);
 
       /* Release (unclaim) the disk */
@@ -479,7 +598,332 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 
   DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
 
-  /* FIXME: initialize media change support */
+  /* 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;
+       }
+
+    }
+
+
+  ExFreePool (Buffer);
+
+  /* Initialize device timer */
+  IoInitializeTimer(DiskDeviceObject,
+                   CdromTimerRoutine,
+                   NULL);
+  IoStartTimer(DiskDeviceObject);
 
   DPRINT("CdromClassCreateDeviceObjects() done\n");
 
@@ -487,21 +931,101 @@ CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 }
 
 
+/**********************************************************************
+ * 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;
 
-//    CdromClassDeviceControl
-//
-//  DESCRIPTION:
-//    Answer requests for device control calls
-//
-//  RUN LEVEL:
-//    PASSIVE_LEVEL
-//
-//  ARGUMENTS:
-//    Standard dispatch arguments
-//
-//  RETURNS:
-//    NTSTATUS
-//
+  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));
+}
+
+
+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));
+}
+
+
+/**********************************************************************
+ * NAME                                                        EXPORTED
+ *     CdromClassDeviceControl
+ *
+ * DESCRIPTION:
+ *     Answer requests for device control calls
+ *
+ * RUN LEVEL:
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS:
+ *     DeviceObject
+ *     Irp
+ *             Standard dispatch arguments
+ *
+ * RETURNS:
+ *     Status.
+ */
 
 NTSTATUS STDCALL
 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
@@ -528,27 +1052,97 @@ CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
   switch (ControlCode)
     {
       case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
-       DPRINT1("IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
+       DPRINT ("CdromClassDeviceControl: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
        if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
          {
-           Status = STATUS_INVALID_PARAMETER;
+           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;
          }
-       else if (DeviceExtension->DiskGeometry == NULL)
+       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))
          {
-           DPRINT1("No cdrom geometry available!\n");
-           Status = STATUS_NO_SUCH_DEVICE;
+           Status = STATUS_INFO_LENGTH_MISMATCH;
          }
        else
          {
-           PDISK_GEOMETRY Geometry;
-
-           Geometry = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
-           RtlMoveMemory(Geometry,
-                         DeviceExtension->DiskGeometry,
-                         sizeof(DISK_GEOMETRY));
+           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;
 
-           Status = STATUS_SUCCESS;
-           Information = sizeof(DISK_GEOMETRY);
+      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;
 
@@ -557,43 +1151,553 @@ CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
        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_NO_INCREMENT);
+                   IO_DISK_INCREMENT);
 
-  return(STATUS_SUCCESS);
+  return Status;
 }
 
 
-//    CdromClassShutdownFlush
-//
-//  DESCRIPTION:
-//    Answer requests for shutdown and flush calls
-//
-//  RUN LEVEL:
-//    PASSIVE_LEVEL
-//
-//  ARGUMENTS:
-//    Standard dispatch arguments
-//
-//  RETURNS:
-//    NTSTATUS
-//
+/**********************************************************************
+ * 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;
+
+           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);
+}
+
 
 NTSTATUS STDCALL
-CdromClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
-                       IN PIRP Irp)
+CdromDeviceControlCompletion (IN PDEVICE_OBJECT DeviceObject,
+                             IN PIRP Irp,
+                             IN PVOID Context)
 {
-  DPRINT("CdromClassShutdownFlush() called!\n");
+  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;
 
-  Irp->IoStatus.Status = STATUS_SUCCESS;
-  Irp->IoStatus.Information = 0;
-  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+  DPRINT ("CdromDeviceControlCompletion() called\n");
 
-  return(STATUS_SUCCESS);
+  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;
 }
 
 
+VOID STDCALL
+CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject,
+                 IN PVOID Context)
+{
+  PIO_WORKITEM WorkItem;
+
+  DPRINT ("CdromTimerRoutine() called\n");
+  WorkItem = IoAllocateWorkItem(DeviceObject);
+  if (!WorkItem)
+    {
+      return;
+    }
+
+  IoQueueWorkItem(WorkItem,
+                 CdromWorkItem,
+                 DelayedWorkQueue,
+                 WorkItem);
+}
+
+
+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);
+    }
+}
+
 /* EOF */