merged in MDL fix and a makefile mod from the 0.1.4 release branch. This represents...
[reactos.git] / reactos / drivers / storage / class2 / class2.c
index 9538693..55c3238 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS kernel
- *  Copyright (C) 2001, 2002 ReactOS Team
+ *  Copyright (C) 2001, 2002, 2003 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
@@ -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: class2.c,v 1.15 2002/04/01 23:51:09 ekohl Exp $
+/* $Id: class2.c,v 1.41 2003/10/15 22:16:44 vizzini Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
 
 /*
  * TODO:
- *     - a lot ;-)
+ *   - finish ScsiClassDeviceControl().
  */
 
 /* INCLUDES *****************************************************************/
 
 #include <ddk/ntddk.h>
-#include "../include/scsi.h"
-#include "../include/class2.h"
+#include <ddk/scsi.h>
+#include <ddk/class2.h>
 
 #define NDEBUG
 #include <debug.h>
 
-// #define ENABLE_RETRIES
 
 #define VERSION "0.0.1"
 
@@ -56,10 +55,6 @@ static NTSTATUS STDCALL
 ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
                   IN PIRP Irp);
 
-static NTSTATUS STDCALL
-ScsiClassScsiDispatch(IN PDEVICE_OBJECT DeviceObject,
-                    IN PIRP Irp);
-
 static NTSTATUS STDCALL
 ScsiClassDeviceDispatch(IN PDEVICE_OBJECT DeviceObject,
                       IN PIRP Irp);
@@ -70,33 +65,35 @@ ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
 
 static VOID
 ScsiClassRetryRequest(PDEVICE_OBJECT DeviceObject,
-                     PIRP Irp,
-                     PSCSI_REQUEST_BLOCK Srb);
+                     PIRP Irp, PSCSI_REQUEST_BLOCK Srb, BOOLEAN Associated);
 
 /* FUNCTIONS ****************************************************************/
 
-//    DriverEntry
-//
-//  DESCRIPTION:
-//    This function initializes the driver.
-//
-//  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.
+ *
+ * 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,
            IN PUNICODE_STRING RegistryPath)
 {
-  DbgPrint("Class Driver %s\n", VERSION);
+  DPRINT("Class Driver %s\n", VERSION);
   return(STATUS_SUCCESS);
 }
 
@@ -122,6 +119,9 @@ ScsiClassDebugPrint(IN ULONG DebugPrintLevel,
 }
 
 
+/*
+ * @unimplemented
+ */
 NTSTATUS STDCALL
 ScsiClassAsynchronousCompletion(IN PDEVICE_OBJECT DeviceObject,
                                IN PIRP Irp,
@@ -131,9 +131,12 @@ ScsiClassAsynchronousCompletion(IN PDEVICE_OBJECT DeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 VOID STDCALL
-ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
-                     PIRP Irp)
+ScsiClassBuildRequest(IN PDEVICE_OBJECT DeviceObject,
+                     IN PIRP Irp)
 {
   PDEVICE_EXTENSION DeviceExtension;
   PIO_STACK_LOCATION CurrentIrpStack;
@@ -157,9 +160,7 @@ ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
   DPRINT("Logical block address: %lu\n", LogicalBlockAddress);
 
   /* Allocate and initialize an SRB */
-  /* FIXME: use lookaside list instead */
-  Srb = ExAllocatePool(NonPagedPool,
-                      sizeof(SCSI_REQUEST_BLOCK));
+  Srb = ExAllocateFromNPagedLookasideList(&DeviceExtension->SrbLookasideListHead);
 
   Srb->SrbFlags = 0;
   Srb->Length = sizeof(SCSI_REQUEST_BLOCK); //SCSI_REQUEST_BLOCK_SIZE;
@@ -168,6 +169,7 @@ ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
   Srb->TargetId = DeviceExtension->TargetId;
   Srb->Lun = DeviceExtension->Lun;
   Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+  //FIXME: NT4 DDK sample uses MmGetMdlVirtualAddress! Why shouldn't we?
   Srb->DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
   Srb->DataTransferLength = CurrentIrpStack->Parameters.Read.Length;
   Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
@@ -237,10 +239,9 @@ ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
   /* Initialize next stack location */
   NextIrpStack->MajorFunction = IRP_MJ_SCSI;
   NextIrpStack->Parameters.Scsi.Srb = Srb;
-  NextIrpStack->DeviceObject = DeviceObject;
 
   /* Set retry count */
-  NextIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+  CurrentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
 
   DPRINT("IoSetCompletionRoutine (Irp %p  Srb %p)\n", Irp, Srb);
   IoSetCompletionRoutine(Irp,
@@ -252,6 +253,9 @@ ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 ScsiClassClaimDevice(PDEVICE_OBJECT PortDeviceObject,
                     PSCSI_INQUIRY_DATA LunInfo,
@@ -295,7 +299,7 @@ ScsiClassClaimDevice(PDEVICE_OBJECT PortDeviceObject,
                                      &IoStatusBlock);
   if (Irp == NULL)
     {
-      DPRINT1("Failed to allocate Irp!\n");
+      DPRINT("Failed to allocate Irp!\n");
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -339,6 +343,9 @@ ScsiClassClaimDevice(PDEVICE_OBJECT PortDeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 ScsiClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
                            IN PCCHAR ObjectNameBuffer,
@@ -408,27 +415,235 @@ ScsiClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
-ScsiClassDeviceControl(PDEVICE_OBJECT DeviceObject,
-                      PIRP Irp)
+ScsiClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
+                      IN PIRP Irp)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION NextStack;
+  PIO_STACK_LOCATION Stack;
+  ULONG IoControlCode;
+  ULONG InputBufferLength;
+  ULONG OutputBufferLength;
+  ULONG ModifiedControlCode;
+  PSCSI_REQUEST_BLOCK Srb;
+  PCDB Cdb;
+
+  DPRINT("ScsiClassDeviceControl() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  Stack = IoGetCurrentIrpStackLocation(Irp);
+
+  IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode;
+  InputBufferLength = Stack->Parameters.DeviceIoControl.InputBufferLength;
+  OutputBufferLength = Stack->Parameters.DeviceIoControl.OutputBufferLength;
+
+  if (IoControlCode == IOCTL_SCSI_GET_DUMP_POINTERS)
+    {
+      PDUMP_POINTERS DumpPointers;
+
+      if (OutputBufferLength < sizeof(DUMP_POINTERS))
+       {
+         Irp->IoStatus.Information = 0;
+         Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+         return(STATUS_BUFFER_TOO_SMALL);
+       }
+      DumpPointers = (PDUMP_POINTERS)Irp->AssociatedIrp.SystemBuffer;
+
+      /* Initialize next stack location for call to the port driver */
+      NextStack = IoGetNextIrpStackLocation(Irp);
+
+      NextStack->Parameters = Stack->Parameters;
+      NextStack->MajorFunction = Stack->MajorFunction;
+      NextStack->MinorFunction = Stack->MinorFunction;
+
+      /* Call port driver */
+      return(IoCallDriver(DeviceExtension->PortDeviceObject,
+                         Irp));
+    }
+  if (IoControlCode == IOCTL_SCSI_GET_ADDRESS)
+    {
+      PSCSI_ADDRESS ScsiAddress;
+
+      if (OutputBufferLength < sizeof(SCSI_ADDRESS))
+       {
+         Irp->IoStatus.Information = 0;
+         Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+         return(STATUS_BUFFER_TOO_SMALL);
+       }
+
+      ScsiAddress = Irp->AssociatedIrp.SystemBuffer;
+      ScsiAddress->Length = sizeof(SCSI_ADDRESS);
+      ScsiAddress->PortNumber = DeviceExtension->PortNumber;
+      ScsiAddress->PathId = DeviceExtension->PathId;
+      ScsiAddress->TargetId = DeviceExtension->TargetId;
+      ScsiAddress->Lun = DeviceExtension->Lun;
+
+      Irp->IoStatus.Information = sizeof(SCSI_ADDRESS);
+      Irp->IoStatus.Status = STATUS_SUCCESS;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+      return(STATUS_SUCCESS);
+    }
+
+  if (IoControlCode == IOCTL_SCSI_PASS_THROUGH ||
+      IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)
+    {
+      PSCSI_PASS_THROUGH ScsiPassThrough;
+
+      DPRINT("IOCTL_SCSI_PASS_THROUGH/IOCTL_SCSI_PASS_THROUGH_DIRECT\n");
+
+      /* Check input size */
+      if (InputBufferLength < sizeof(SCSI_PASS_THROUGH))
+       {
+         Irp->IoStatus.Information = 0;
+         Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+         return(STATUS_INVALID_PARAMETER);
+       }
+
+      /* Initialize next stack location for call to the port driver */
+      NextStack = IoGetNextIrpStackLocation(Irp);
+
+      ScsiPassThrough = Irp->AssociatedIrp.SystemBuffer;
+      ScsiPassThrough->PathId = DeviceExtension->PathId;
+      ScsiPassThrough->TargetId = DeviceExtension->TargetId;
+      ScsiPassThrough->Lun = DeviceExtension->Lun;
+      ScsiPassThrough->Cdb[1] |= DeviceExtension->Lun << 5;
+
+      NextStack->Parameters = Stack->Parameters;
+      NextStack->MajorFunction = Stack->MajorFunction;
+      NextStack->MinorFunction = Stack->MinorFunction;
+
+      /* Call port driver */
+      return(IoCallDriver(DeviceExtension->PortDeviceObject,
+                         Irp));
+    }
+
+  /* Allocate an SRB */
+  Srb = ExAllocatePool (NonPagedPool,
+                       sizeof(SCSI_REQUEST_BLOCK));
+  if (Srb == NULL)
+    {
+      Irp->IoStatus.Information = 0;
+      Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+      IoCompleteRequest(Irp,
+                       IO_NO_INCREMENT);
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  /* Initialize the SRB */
+  RtlZeroMemory(Srb,
+               sizeof(SCSI_REQUEST_BLOCK));
+  Cdb = (PCDB)Srb->Cdb;
+
+  ModifiedControlCode = (IoControlCode & 0x0000FFFF) | (IOCTL_DISK_BASE << 16);
+  switch (ModifiedControlCode)
+    {
+      case IOCTL_DISK_CHECK_VERIFY:
+       DPRINT("IOCTL_DISK_CHECK_VERIFY\n");
+
+       /* Initialize SRB operation */
+       Srb->CdbLength = 6;
+       Srb->TimeOutValue = DeviceExtension->TimeOutValue;
+       Cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+       return(ScsiClassSendSrbAsynchronous(DeviceObject,
+                                           Srb,
+                                           Irp,
+                                           NULL,
+                                           0,
+                                           FALSE));
+
+      default:
+       DPRINT1("Unknown device io control code %lx\n",
+               ModifiedControlCode);
+       ExFreePool(Srb);
+
+       /* Pass the IOCTL down to the port driver */
+       NextStack = IoGetNextIrpStackLocation(Irp);
+       NextStack->Parameters = Stack->Parameters;
+       NextStack->MajorFunction = Stack->MajorFunction;
+       NextStack->MinorFunction = Stack->MinorFunction;
+
+       /* Call port driver */
+       return(IoCallDriver(DeviceExtension->PortDeviceObject,
+                           Irp));
+    }
+
+  Irp->IoStatus.Information = 0;
+  Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return(STATUS_UNSUCCESSFUL);
 }
 
 
+/*
+ * @implemented
+ */
 PVOID STDCALL
-ScsiClassFindModePage(PCHAR ModeSenseBuffer,
-                     ULONG Length,
-                     UCHAR PageMode,
-                     BOOLEAN Use6Byte)
+ScsiClassFindModePage(IN PCHAR ModeSenseBuffer,
+                     IN ULONG Length,
+                     IN UCHAR PageMode,
+                     IN BOOLEAN Use6Byte)
 {
-  UNIMPLEMENTED;
+  ULONG DescriptorLength;
+  ULONG HeaderLength;
+  PCHAR End;
+  PCHAR Ptr;
+
+  DPRINT("ScsiClassFindModePage() called\n");
+
+  /* Get header length */
+  HeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
+
+  /* Check header length */
+  if (Length < HeaderLength)
+    return NULL;
+
+  /* Get descriptor length */
+  if (Use6Byte == TRUE)
+    {
+      DescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength;
+    }
+  else
+    {
+      DescriptorLength = ((PMODE_PARAMETER_HEADER10)ModeSenseBuffer)->BlockDescriptorLength[1];
+    }
+
+  /* Set page pointers */
+  Ptr = ModeSenseBuffer + HeaderLength + DescriptorLength;
+  End = ModeSenseBuffer + Length;
+
+  /* Search for page */
+  while (Ptr < End)
+    {
+      /* Check page code */
+      if (((PMODE_DISCONNECT_PAGE)Ptr)->PageCode == PageMode)
+       return Ptr;
+
+      /* Skip to next page */
+      Ptr += ((PMODE_DISCONNECT_PAGE)Ptr)->PageLength;
+    }
+
+  return NULL;
 }
 
 
+/*
+ * @implemented
+ */
 ULONG STDCALL
-ScsiClassFindUnclaimedDevices(PCLASS_INIT_DATA InitializationData,
-                             PSCSI_ADAPTER_BUS_INFO AdapterInformation)
+ScsiClassFindUnclaimedDevices(IN PCLASS_INIT_DATA InitializationData,
+                             IN PSCSI_ADAPTER_BUS_INFO AdapterInformation)
 {
   PSCSI_INQUIRY_DATA UnitInfo;
   PINQUIRYDATA InquiryData;
@@ -437,7 +652,7 @@ ScsiClassFindUnclaimedDevices(PCLASS_INIT_DATA InitializationData,
   ULONG UnclaimedDevices = 0;
   NTSTATUS Status;
 
-  DPRINT("ScsiClassFindUnclaimedDevices() called!\n");
+  DPRINT("ScsiClassFindUnclaimedDevices() called\n");
 
   DPRINT("NumberOfBuses: %lu\n",AdapterInformation->NumberOfBuses);
   Buffer = (PUCHAR)AdapterInformation;
@@ -470,24 +685,18 @@ ScsiClassFindUnclaimedDevices(PCLASS_INIT_DATA InitializationData,
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
-ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
-                        PIO_SCSI_CAPABILITIES *PortCapabilities)
+ScsiClassGetCapabilities(IN PDEVICE_OBJECT PortDeviceObject,
+                        OUT PIO_SCSI_CAPABILITIES *PortCapabilities)
 {
-  PIO_SCSI_CAPABILITIES Buffer;
   IO_STATUS_BLOCK IoStatusBlock;
   NTSTATUS Status;
   KEVENT Event;
   PIRP Irp;
 
-  *PortCapabilities = NULL;
-  Buffer = ExAllocatePool(NonPagedPool,
-                         sizeof(IO_SCSI_CAPABILITIES));
-  if (Buffer == NULL)
-    {
-      return(STATUS_INSUFFICIENT_RESOURCES);
-    }
-
   KeInitializeEvent(&Event,
                    NotificationEvent,
                    FALSE);
@@ -496,14 +705,13 @@ ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
                                      PortDeviceObject,
                                      NULL,
                                      0,
-                                     Buffer,
-                                     sizeof(IO_SCSI_CAPABILITIES),
+                                     PortCapabilities,
+                                     sizeof(PVOID),
                                      FALSE,
                                      &Event,
                                      &IoStatusBlock);
   if (Irp == NULL)
     {
-      ExFreePool(Buffer);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -519,22 +727,18 @@ ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
       Status = IoStatusBlock.Status;
     }
 
-  if (!NT_SUCCESS(Status))
-    {
-      ExFreePool(Buffer);
-    }
-  else
-    {
-      *PortCapabilities = Buffer;
-    }
+  DPRINT("PortCapabilities at %p\n", *PortCapabilities);
 
   return(Status);
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
-ScsiClassGetInquiryData(PDEVICE_OBJECT PortDeviceObject,
-                       PSCSI_ADAPTER_BUS_INFO *ConfigInfo)
+ScsiClassGetInquiryData(IN PDEVICE_OBJECT PortDeviceObject,
+                       IN PSCSI_ADAPTER_BUS_INFO *ConfigInfo)
 {
   PSCSI_ADAPTER_BUS_INFO Buffer;
   IO_STATUS_BLOCK IoStatusBlock;
@@ -598,10 +802,13 @@ ScsiClassGetInquiryData(PDEVICE_OBJECT PortDeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 ULONG STDCALL
-ScsiClassInitialize(PVOID Argument1,
-                   PVOID Argument2,
-                   PCLASS_INIT_DATA InitializationData)
+ScsiClassInitialize(IN PVOID Argument1,
+                   IN PVOID Argument2,
+                   IN PCLASS_INIT_DATA InitializationData)
 {
   PCONFIGURATION_INFORMATION ConfigInfo;
   PDRIVER_OBJECT DriverObject = Argument1;
@@ -615,14 +822,14 @@ ScsiClassInitialize(PVOID Argument1,
 
   DPRINT("ScsiClassInitialize() called!\n");
 
-  DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose;
-  DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose;
-  DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite;
-  DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite;
-  DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassScsiDispatch;
-  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceDispatch;
-  DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush;
-  DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush;
+  DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)ScsiClassCreateClose;
+  DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)ScsiClassCreateClose;
+  DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)ScsiClassReadWrite;
+  DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)ScsiClassReadWrite;
+  DriverObject->MajorFunction[IRP_MJ_SCSI] = (PDRIVER_DISPATCH)ScsiClassInternalIoControl;
+  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)ScsiClassDeviceDispatch;
+  DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)ScsiClassShutdownFlush;
+  DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)ScsiClassShutdownFlush;
   if (InitializationData->ClassStartIo)
     {
       DriverObject->DriverStartIo = InitializationData->ClassStartIo;
@@ -691,11 +898,12 @@ ScsiClassInitialize(PVOID Argument1,
  *
  * RETURN VALUE
  *     None.
+ *
+ * @implemented
  */
-
 VOID STDCALL
-ScsiClassInitializeSrbLookasideList(PDEVICE_EXTENSION DeviceExtension,
-                                   ULONG NumberElements)
+ScsiClassInitializeSrbLookasideList(IN PDEVICE_EXTENSION DeviceExtension,
+                                   IN ULONG NumberElements)
 {
   ExInitializeNPagedLookasideList(&DeviceExtension->SrbLookasideListHead,
                                  NULL,
@@ -707,29 +915,46 @@ ScsiClassInitializeSrbLookasideList(PDEVICE_EXTENSION DeviceExtension,
 }
 
 
+/*
+ * @unimplemented
+ */
 NTSTATUS STDCALL
-ScsiClassInternalIoControl(PDEVICE_OBJECT DeviceObject,
-                          PIRP Irp)
+ScsiClassInternalIoControl(IN PDEVICE_OBJECT DeviceObject,
+                          IN PIRP Irp)
 {
-  UNIMPLEMENTED;
+  DPRINT1("ScsiClassInternalIoContol() called\n");
+
+  Irp->IoStatus.Status = STATUS_SUCCESS;
+  Irp->IoStatus.Information = 0;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return(STATUS_SUCCESS);
 }
 
 
+/*
+ * @implemented
+ */
 BOOLEAN STDCALL
-ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
-                           PSCSI_REQUEST_BLOCK Srb,
-                           UCHAR MajorFunctionCode,
-                           ULONG IoDeviceCode,
-                           ULONG RetryCount,
-                           NTSTATUS *Status)
+ScsiClassInterpretSenseInfo(IN PDEVICE_OBJECT DeviceObject,
+                           IN PSCSI_REQUEST_BLOCK Srb,
+                           IN UCHAR MajorFunctionCode,
+                           IN ULONG IoDeviceCode,
+                           IN ULONG RetryCount,
+                           OUT NTSTATUS *Status)
 {
   PDEVICE_EXTENSION DeviceExtension;
+#if 0
+  PIO_ERROR_LOG_PACKET LogPacket;
+#endif
   PSENSE_DATA SenseData;
+  NTSTATUS LogStatus;
+  BOOLEAN LogError;
   BOOLEAN Retry;
 
-  DPRINT1("ScsiClassInterpretSenseInfo() called\n");
+  DPRINT("ScsiClassInterpretSenseInfo() called\n");
 
-  DPRINT1("Srb->SrbStatus %lx\n", Srb->SrbStatus);
+  DPRINT("Srb->SrbStatus %lx\n", Srb->SrbStatus);
 
   if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_PENDING)
     {
@@ -739,6 +964,7 @@ ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
 
   DeviceExtension = DeviceObject->DeviceExtension;
   SenseData = Srb->SenseInfoBuffer;
+  LogError = FALSE;
   Retry = TRUE;
 
   if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
@@ -746,21 +972,108 @@ ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
     {
       /* Got valid sense data, interpret them */
 
-      DPRINT1("ErrorCode: %x\n", SenseData->ErrorCode);
-      DPRINT1("SenseKey: %x\n", SenseData->SenseKey);
-      DPRINT1("SenseCode: %x\n", SenseData->AdditionalSenseCode);
+      DPRINT("ErrorCode: %x\n", SenseData->ErrorCode);
+      DPRINT("SenseKey: %x\n", SenseData->SenseKey);
+      DPRINT("SenseCode: %x\n", SenseData->AdditionalSenseCode);
 
       switch (SenseData->SenseKey & 0xf)
        {
-         /* FIXME: add more sense key codes */
+         case SCSI_SENSE_NO_SENSE:
+           DPRINT("SCSI_SENSE_NO_SENSE\n");
+           if (SenseData->IncorrectLength)
+             {
+               DPRINT("Incorrect block length\n");
+               *Status = STATUS_INVALID_BLOCK_LENGTH;
+               Retry = FALSE;
+             }
+           else
+             {
+               DPRINT("Unspecified error\n");
+               *Status = STATUS_IO_DEVICE_ERROR;
+               Retry = FALSE;
+             }
+           break;
+
+         case SCSI_SENSE_RECOVERED_ERROR:
+           DPRINT("SCSI_SENSE_RECOVERED_ERROR\n");
+           *Status = STATUS_SUCCESS;
+           Retry = FALSE;
+           break;
 
          case SCSI_SENSE_NOT_READY:
-           DPRINT1("SCSI_SENSE_NOT_READY\n");
+           DPRINT("SCSI_SENSE_NOT_READY\n");
            *Status = STATUS_DEVICE_NOT_READY;
+           switch (SenseData->AdditionalSenseCode)
+             {
+               case SCSI_ADSENSE_LUN_NOT_READY:
+                 DPRINT("SCSI_ADSENSE_LUN_NOT_READY\n");
+                 break;
+
+               case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
+                 DPRINT("SCSI_ADSENSE_NO_MEDIA_IN_DEVICE\n");
+                 *Status = STATUS_NO_MEDIA_IN_DEVICE;
+                 Retry = FALSE;
+                 break;
+             }
+           break;
+
+         case SCSI_SENSE_MEDIUM_ERROR:
+           DPRINT("SCSI_SENSE_MEDIUM_ERROR\n");
+           *Status = STATUS_DEVICE_DATA_ERROR;
+           Retry = FALSE;
+           break;
+
+         case SCSI_SENSE_HARDWARE_ERROR:
+           DPRINT("SCSI_SENSE_HARDWARE_ERROR\n");
+           *Status = STATUS_IO_DEVICE_ERROR;
+           break;
+
+         case SCSI_SENSE_ILLEGAL_REQUEST:
+           DPRINT("SCSI_SENSE_ILLEGAL_REQUEST\n");
+           *Status = STATUS_INVALID_DEVICE_REQUEST;
+           switch (SenseData->AdditionalSenseCode)
+             {
+               case SCSI_ADSENSE_ILLEGAL_COMMAND:
+                 DPRINT("SCSI_ADSENSE_ILLEGAL_COMMAND\n");
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_ILLEGAL_BLOCK:
+                 DPRINT("SCSI_ADSENSE_ILLEGAL_BLOCK\n");
+                 *Status = STATUS_NONEXISTENT_SECTOR;
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_INVALID_LUN:
+                 DPRINT("SCSI_ADSENSE_INVALID_LUN\n");
+                 *Status = STATUS_NO_SUCH_DEVICE;
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_MUSIC_AREA:
+                 DPRINT("SCSI_ADSENSE_MUSIC_AREA\n");
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_DATA_AREA:
+                 DPRINT("SCSI_ADSENSE_DATA_AREA\n");
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_VOLUME_OVERFLOW:
+                 DPRINT("SCSI_ADSENSE_VOLUME_OVERFLOW\n");
+                 Retry = FALSE;
+                 break;
+
+               case SCSI_ADSENSE_INVALID_CDB:
+                 DPRINT("SCSI_ADSENSE_INVALID_CDB\n");
+                 Retry = FALSE;
+                 break;
+             }
            break;
 
          case SCSI_SENSE_UNIT_ATTENTION:
-           DPRINT1("SCSI_SENSE_UNIT_ATTENTION\n");
+           DPRINT("SCSI_SENSE_UNIT_ATTENTION\n");
            if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) &&
                (DeviceObject->Vpb->Flags & VPB_MOUNTED))
              {
@@ -774,12 +1087,16 @@ ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
              }
            break;
 
-         case SCSI_SENSE_ILLEGAL_REQUEST:
-           DPRINT1("SCSI_SENSE_ILLEGAL_REQUEST\n");
-           *Status = STATUS_INVALID_DEVICE_REQUEST;
+         case SCSI_SENSE_DATA_PROTECT:
+           DPRINT("SCSI_SENSE_DATA_PROTECT\n");
+           *Status = STATUS_MEDIA_WRITE_PROTECTED;
            Retry = FALSE;
            break;
 
+         case SCSI_SENSE_ABORTED_COMMAND:
+           DPRINT("SCSI_SENSE_ABORTED_COMMAND\n");
+           *Status = STATUS_IO_DEVICE_ERROR;
+           break;
 
          default:
            DPRINT1("SCSI error (sense key: %x)\n",
@@ -790,17 +1107,34 @@ ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
     }
   else
     {
-      /* Got no/invalid sense data, return generic error codes */
+      /* Got no or invalid sense data, return generic error codes */
       switch (SRB_STATUS(Srb->SrbStatus))
        {
          /* FIXME: add more srb status codes */
 
+         case SRB_STATUS_INVALID_PATH_ID:
+         case SRB_STATUS_INVALID_TARGET_ID:
+         case SRB_STATUS_INVALID_LUN:
+         case SRB_STATUS_NO_DEVICE:
+         case SRB_STATUS_NO_HBA:
+           *Status = STATUS_NO_SUCH_DEVICE;
+           Retry = FALSE;
+           break;
+
+         case SRB_STATUS_BUSY:
+           *Status = STATUS_DEVICE_BUSY;
+           Retry = TRUE;
+           break;
+
          case SRB_STATUS_DATA_OVERRUN:
            *Status = STATUS_DATA_OVERRUN;
            Retry = FALSE;
            break;
 
          default:
+           DPRINT1("SCSI error (SRB status: %x)\n",
+                   SRB_STATUS(Srb->SrbStatus));
+           LogError = TRUE;
            *Status = STATUS_IO_DEVICE_ERROR;
            break;
        }
@@ -815,18 +1149,57 @@ ScsiClassInterpretSenseInfo(PDEVICE_OBJECT DeviceObject,
                                  &Retry);
     }
 
-  /* FIXME: log severe errors */
+  if (LogError == TRUE)
+    {
+#if 0
+      /* Allocate error packet */
+      LogPacket = IoAllocateErrorLogEntry (DeviceObject,
+                                          sizeof(IO_ERROR_LOG_PACKET) +
+                                            5 * sizeof(ULONG));
+      if (LogPacket == NULL)
+       {
+         DPRINT1 ("Failed to allocate a log packet!\n");
+         return Retry;
+       }
+
+      /* Initialize error packet */
+      LogPacket->MajorFunctionCode = MajorFunctionCode;
+      LogPacket->RetryCount = (UCHAR)RetryCount;
+      LogPacket->DumpDataSize = 6 * sizeof(ULONG);
+      LogPacket->ErrorCode = 0; /* FIXME */
+      LogPacket->FinalStatus = *Status;
+      LogPacket->IoControlCode = IoDeviceCode;
+      LogPacket->DeviceOffset.QuadPart = 0; /* FIXME */
+      LogPacket->DumpData[0] = Srb->PathId;
+      LogPacket->DumpData[1] = Srb->TargetId;
+      LogPacket->DumpData[2] = Srb->Lun;
+      LogPacket->DumpData[3] = 0;
+      LogPacket->DumpData[4] = (Srb->SrbStatus << 8) | Srb->ScsiStatus;
+      if (SenseData != NULL)
+       {
+         LogPacket->DumpData[5] = (SenseData->SenseKey << 16) |
+                                  (SenseData->AdditionalSenseCode << 8) |
+                                  SenseData->AdditionalSenseCodeQualifier;
+       }
 
-  DPRINT1("ScsiClassInterpretSenseInfo() done\n");
+      /* Write error packet */
+      IoWriteErrorLogEntry (LogPacket);
+#endif
+    }
 
-  return(Retry);
+  DPRINT("ScsiClassInterpretSenseInfo() done\n");
+
+  return Retry;
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
-ScsiClassIoComplete(PDEVICE_OBJECT DeviceObject,
-                   PIRP Irp,
-                   PVOID Context)
+ScsiClassIoComplete(IN PDEVICE_OBJECT DeviceObject,
+                   IN PIRP Irp,
+                   IN PVOID Context)
 {
   PDEVICE_EXTENSION DeviceExtension;
   PIO_STACK_LOCATION IrpStack;
@@ -838,11 +1211,19 @@ ScsiClassIoComplete(PDEVICE_OBJECT DeviceObject,
          DeviceObject, Irp, Context);
 
   DeviceExtension = DeviceObject->DeviceExtension;
-  Srb = (PSCSI_REQUEST_BLOCK)Context;
-  DPRINT("Srb %p\n", Srb);
 
   IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
+  /*
+   * BUGBUG -> Srb = IrpStack->Parameters.Scsi.Srb;
+   * Must pass Srb as Context arg!! See comment about Completion routines in 
+   * IofCallDriver for more info.
+   */
+
+  Srb = (PSCSI_REQUEST_BLOCK)Context;
+
+  DPRINT("Srb %p\n", Srb);
+
   if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
     {
       Status = STATUS_SUCCESS;
@@ -855,47 +1236,35 @@ ScsiClassIoComplete(PDEVICE_OBJECT DeviceObject,
                                          0,
                                          MAXIMUM_RETRIES - ((ULONG)IrpStack->Parameters.Others.Argument4),
                                          &Status);
-
-      DPRINT1("Retry count: %lu\n", (ULONG)IrpStack->Parameters.Others.Argument4);
-
-      if ((Retry == TRUE) &&
+      if ((Retry) &&
          ((ULONG)IrpStack->Parameters.Others.Argument4 > 0))
        {
          ((ULONG)IrpStack->Parameters.Others.Argument4)--;
-         DPRINT1("Retry count: %lu\n", (ULONG)IrpStack->Parameters.Others.Argument4);
 
-         DPRINT1("Should try again!\n");
-#ifdef ENABLE_RETRY
          ScsiClassRetryRequest(DeviceObject,
                                Irp,
-                               Srb);
+                               Srb,
+                               FALSE);
+
          return(STATUS_MORE_PROCESSING_REQUIRED);
-#else
-         return(Status);
-#endif
        }
     }
 
-  /* FIXME: use lookaside list instead */
-  DPRINT("Freed SRB %p\n", IrpStack->Parameters.Scsi.Srb);
-  ExFreePool(IrpStack->Parameters.Scsi.Srb);
+  /* Free the SRB */
+  ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead,
+                             Srb);
 
   Irp->IoStatus.Status = Status;
-#if 0
-  if (!NT_SUCCESS(Status) &&
-      IoIsErrorUserInduced(Status))
+  if (!NT_SUCCESS(Status))
     {
-      IoSetHardErrorOrVerifyDevice(Irp,
-                                  DeviceObject);
       Irp->IoStatus.Information = 0;
+      if (IoIsErrorUserInduced(Status))
+       {
+         IoSetHardErrorOrVerifyDevice(Irp,
+                                      DeviceObject);
+       }
     }
 
-  if (Irp->PendingReturned)
-    {
-      IoMarkIrpPending(Irp);
-    }
-#endif
-
   if (DeviceExtension->ClassStartIo != NULL)
     {
       if (IrpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL)
@@ -911,32 +1280,247 @@ ScsiClassIoComplete(PDEVICE_OBJECT DeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
-ScsiClassIoCompleteAssociated(PDEVICE_OBJECT DeviceObject,
-                             PIRP Irp,
-                             PVOID Context)
+ScsiClassIoCompleteAssociated(IN PDEVICE_OBJECT DeviceObject,
+                             IN PIRP Irp,
+                             IN PVOID Context)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION IrpStack;
+  PSCSI_REQUEST_BLOCK Srb;
+  PIRP MasterIrp;
+  BOOLEAN Retry;
+  LONG RequestCount;
+  NTSTATUS Status;
+
+  DPRINT("ScsiClassIoCompleteAssociated(DeviceObject %p  Irp %p  Context %p) called\n",
+        DeviceObject, Irp, Context);
+
+  MasterIrp = Irp->AssociatedIrp.MasterIrp;
+  DeviceExtension = DeviceObject->DeviceExtension;
+
+  IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+  /*
+   * BUGBUG -> Srb = Srb = IrpStack->Parameters.Scsi.Srb;
+   * Must pass Srb as Context arg!! See comment about Completion routines in 
+   * IofCallDriver for more info.
+   */
+
+  Srb = (PSCSI_REQUEST_BLOCK)Context;
+  
+  DPRINT("Srb %p\n", Srb);
+
+  if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
+    {
+      Status = STATUS_SUCCESS;
+    }
+  else
+    {
+      /* Get more detailed status information */
+      Retry = ScsiClassInterpretSenseInfo(DeviceObject,
+                                         Srb,
+                                         IrpStack->MajorFunction,
+                                         0,
+                                         MAXIMUM_RETRIES - ((ULONG)IrpStack->Parameters.Others.Argument4),
+                                         &Status);
+
+      if ((Retry) &&
+         ((ULONG)IrpStack->Parameters.Others.Argument4 > 0))
+       {
+         ((ULONG)IrpStack->Parameters.Others.Argument4)--;
+
+         ScsiClassRetryRequest(DeviceObject,
+                               Irp,
+                               Srb,
+                               TRUE);
+
+         return(STATUS_MORE_PROCESSING_REQUIRED);
+       }
+    }
+
+  /* Free the SRB */
+  ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead,
+                             Srb);
+
+  Irp->IoStatus.Status = Status;
+
+  IrpStack = IoGetNextIrpStackLocation(MasterIrp);
+  if (!NT_SUCCESS(Status))
+    {
+      MasterIrp->IoStatus.Status = Status;
+      MasterIrp->IoStatus.Information = 0;
+
+      if (IoIsErrorUserInduced(Status))
+       {
+         IoSetHardErrorOrVerifyDevice(MasterIrp,
+                                      DeviceObject);
+       }
+    }
+
+  /* Decrement the request counter in the Master IRP */
+  RequestCount = InterlockedDecrement((PLONG)&IrpStack->Parameters.Others.Argument1);
+
+  if (RequestCount == 0)
+    {
+      /* Complete the Master IRP */
+      IoCompleteRequest(MasterIrp,
+                       IO_DISK_INCREMENT);
+
+      if (DeviceExtension->ClassStartIo)
+       {
+         IoStartNextPacket(DeviceObject,
+                           FALSE);
+       }
+    }
+
+  /* Free the current IRP */
+  IoFreeIrp(Irp);
+
+  return(STATUS_MORE_PROCESSING_REQUIRED);
 }
 
 
+/*
+ * @implemented
+ */
 ULONG STDCALL
-ScsiClassModeSense(PDEVICE_OBJECT DeviceObject,
-                  CHAR ModeSenseBuffer,
-                  ULONG Length,
-                  UCHAR PageMode)
+ScsiClassModeSense(IN PDEVICE_OBJECT DeviceObject,
+                  IN PCHAR ModeSenseBuffer,
+                  IN ULONG Length,
+                  IN UCHAR PageMode)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  SCSI_REQUEST_BLOCK Srb;
+  ULONG RetryCount;
+  PCDB Cdb;
+  NTSTATUS Status;
+
+  DPRINT("ScsiClassModeSense() called\n");
+
+  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
+  RetryCount = 1;
+
+  /* Initialize the SRB */
+  RtlZeroMemory (&Srb,
+                sizeof(SCSI_REQUEST_BLOCK));
+  Srb.CdbLength = 6;
+  Srb.TimeOutValue = DeviceExtension->TimeOutValue;
+
+  /* Initialize the CDB */
+  Cdb = (PCDB)&Srb.Cdb;
+  Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+  Cdb->MODE_SENSE.PageCode = PageMode;
+  Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length;
+
+TryAgain:
+  Status = ScsiClassSendSrbSynchronous (DeviceObject,
+                                       &Srb,
+                                       ModeSenseBuffer,
+                                       Length,
+                                       FALSE);
+  if (Status == STATUS_VERIFY_REQUIRED)
+    {
+      if (RetryCount != 0)
+       {
+         RetryCount--;
+         goto TryAgain;
+       }
+    }
+  else if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
+    {
+      Status = STATUS_SUCCESS;
+    }
+
+  if (!NT_SUCCESS(Status))
+    {
+      return 0;
+    }
+
+  return Srb.DataTransferLength;
 }
 
 
+/*
+ * @implemented
+ */
 ULONG STDCALL
 ScsiClassQueryTimeOutRegistryValue(IN PUNICODE_STRING RegistryPath)
 {
-  UNIMPLEMENTED;
+  PRTL_QUERY_REGISTRY_TABLE Table;
+  ULONG TimeOutValue;
+  ULONG ZeroTimeOut;
+  ULONG Size;
+  PWSTR Path;
+  NTSTATUS Status;
+
+  if (RegistryPath == NULL)
+    {
+      return 0;
+    }
+
+  TimeOutValue = 0;
+  ZeroTimeOut = 0;
+
+  /* Allocate zero-terminated path string */
+  Size = RegistryPath->Length + sizeof(WCHAR);
+  Path = (PWSTR)ExAllocatePool (NonPagedPool,
+                               Size);
+  if (Path == NULL)
+    {
+      return 0;
+    }
+  RtlZeroMemory (Path,
+                Size);
+  RtlCopyMemory (Path,
+                RegistryPath->Buffer,
+                Size - sizeof(WCHAR));
+
+  /* Allocate query table */
+  Size = sizeof(RTL_QUERY_REGISTRY_TABLE) * 2;
+  Table = (PRTL_QUERY_REGISTRY_TABLE)ExAllocatePool (NonPagedPool,
+                                                    Size);
+  if (Table == NULL)
+    {
+      ExFreePool (Path);
+      return 0;
+    }
+  RtlZeroMemory (Table,
+                Size);
+
+  Table[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+  Table[0].Name = L"TimeOutValue";
+  Table[0].EntryContext = &TimeOutValue;
+  Table[0].DefaultType = REG_DWORD;
+  Table[0].DefaultData = &ZeroTimeOut;
+  Table[0].DefaultLength = sizeof(ULONG);
+
+  Status = RtlQueryRegistryValues (RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+                                  Path,
+                                  Table,
+                                  NULL,
+                                  NULL);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("RtlQueryRegistryValue() failed (Status %lx)\n", Status);
+      TimeOutValue = 0;
+    }
+
+  ExFreePool (Table);
+  ExFreePool (Path);
+
+  DPRINT("TimeOut: %lu\n", TimeOutValue);
+
+  return TimeOutValue;
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject)
 {
@@ -1013,7 +1597,7 @@ ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject)
   else
     {
       /* Use default values if disk geometry cannot be read */
-      RtlZeroMemory(&DeviceExtension->DiskGeometry,
+      RtlZeroMemory(DeviceExtension->DiskGeometry,
                    sizeof(DISK_GEOMETRY));
       DeviceExtension->DiskGeometry->BytesPerSector = 512;
       DeviceExtension->SectorShift = 9;
@@ -1027,6 +1611,8 @@ ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject)
        {
          DeviceExtension->DiskGeometry->MediaType = FixedMedia;
        }
+
+      DPRINT("SectorSize: 512  SectorCount: 0\n");
     }
 
   ExFreePool(CapacityBuffer);
@@ -1037,6 +1623,9 @@ ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject)
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID STDCALL
 ScsiClassReleaseQueue(IN PDEVICE_OBJECT DeviceObject)
 {
@@ -1044,6 +1633,9 @@ ScsiClassReleaseQueue(IN PDEVICE_OBJECT DeviceObject)
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 ScsiClassSendSrbAsynchronous(PDEVICE_OBJECT DeviceObject,
                             PSCSI_REQUEST_BLOCK Srb,
@@ -1052,10 +1644,85 @@ ScsiClassSendSrbAsynchronous(PDEVICE_OBJECT DeviceObject,
                             ULONG BufferLength,
                             BOOLEAN WriteToDevice)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION Stack;
+
+  DPRINT("ScsiClassSendSrbAsynchronous() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+
+  /* Initialize the 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->SenseInfoBuffer = DeviceExtension->SenseData;
+  Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+  Srb->DataBuffer = BufferAddress;
+  Srb->DataTransferLength = BufferLength;
+
+  Srb->ScsiStatus = 0;
+  Srb->SrbStatus = 0;
+  Srb->NextSrb = NULL;
+
+  if (BufferAddress != NULL)
+    {
+      if (Irp->MdlAddress == NULL)
+       {
+         /* Allocate an MDL */
+         if (!IoAllocateMdl(BufferAddress,
+                            BufferLength,
+                            FALSE,
+                            FALSE,
+                            Irp))
+           {
+             DPRINT1("Mdl-Allocation failed\n");
+             return(STATUS_INSUFFICIENT_RESOURCES);
+           }
+
+         MmBuildMdlForNonPagedPool(Irp->MdlAddress);
+       }
+
+      /* Set data direction */
+      Srb->SrbFlags = (WriteToDevice) ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
+    }
+  else
+    {
+      /* Set data direction */
+      Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
+    }
+
+  /* Set the retry counter */
+  Stack = IoGetCurrentIrpStackLocation(Irp);
+  Stack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+  /* Set the completion routine */
+  IoSetCompletionRoutine(Irp,
+                        ScsiClassIoComplete,
+                        Srb,
+                        TRUE,
+                        TRUE,
+                        TRUE);
+
+  /* Attach Srb to the Irp */
+  Stack = IoGetNextIrpStackLocation(Irp);
+  Stack->MajorFunction = IRP_MJ_SCSI;
+  Stack->Parameters.Scsi.Srb = Srb;
+  Srb->OriginalRequest = Irp;
+
+  /* Call the port driver */
+  return(IoCallDriver(DeviceExtension->PortDeviceObject,
+                     Irp));
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
                            PSCSI_REQUEST_BLOCK Srb,
@@ -1065,15 +1732,14 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
 {
   PDEVICE_EXTENSION DeviceExtension;
   IO_STATUS_BLOCK IoStatusBlock;
-  PIO_STACK_LOCATION IoStack;
+  PIO_STACK_LOCATION IrpStack;
   ULONG RequestType;
   BOOLEAN Retry;
   ULONG RetryCount;
-  KEVENT Event;
+  PKEVENT Event;
   PIRP Irp;
   NTSTATUS Status;
 
-
   DPRINT("ScsiClassSendSrbSynchronous() called\n");
 
   RetryCount = MAXIMUM_RETRIES;
@@ -1085,8 +1751,6 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
   Srb->Lun = DeviceExtension->Lun;
   Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
 
-  /* FIXME: more srb initialization required? */
-
   Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
   Srb->SenseInfoBuffer = ExAllocatePool(NonPagedPool,
                                        SENSE_BUFFER_SIZE);
@@ -1103,12 +1767,12 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
     {
       if (WriteToDevice == TRUE)
        {
-         RequestType = IOCTL_SCSI_EXECUTE_OUT;
-         Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
+         RequestType = IOCTL_SCSI_EXECUTE_IN;  // needs _in_ to the device
+         Srb->SrbFlags = SRB_FLAGS_DATA_OUT;   // needs _out_ from the caller
        }
       else
        {
-         RequestType = IOCTL_SCSI_EXECUTE_IN;
+         RequestType = IOCTL_SCSI_EXECUTE_OUT;
          Srb->SrbFlags = SRB_FLAGS_DATA_IN;
        }
     }
@@ -1116,8 +1780,10 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
   Srb->DataTransferLength = BufferLength;
   Srb->DataBuffer = BufferAddress;
 
-
-  KeInitializeEvent(&Event,
+  Event = ExAllocatePool(NonPagedPool,
+                        sizeof(KEVENT));
+TryAgain:
+  KeInitializeEvent(Event,
                    NotificationEvent,
                    FALSE);
 
@@ -1128,30 +1794,27 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
                                      BufferAddress,
                                      BufferLength,
                                      TRUE,
-                                     &Event,
+                                     Event,
                                      &IoStatusBlock);
   if (Irp == NULL)
     {
-      DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
+      DPRINT("IoBuildDeviceIoControlRequest() failed\n");
       ExFreePool(Srb->SenseInfoBuffer);
+      ExFreePool(Event);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
-  /* FIXME: more irp initialization required? */
-
-
   /* Attach Srb to the Irp */
-  IoStack = IoGetNextIrpStackLocation(Irp);
-  IoStack->Parameters.Scsi.Srb = Srb;
+  IrpStack = IoGetNextIrpStackLocation(Irp);
+  IrpStack->Parameters.Scsi.Srb = Srb;
   Srb->OriginalRequest = Irp;
 
-
   /* Call the SCSI port driver */
   Status = IoCallDriver(DeviceExtension->PortDeviceObject,
                        Irp);
   if (Status == STATUS_PENDING)
     {
-      KeWaitForSingleObject(&Event,
+      KeWaitForSingleObject(Event,
                            Suspended,
                            KernelMode,
                            FALSE,
@@ -1168,9 +1831,12 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
                                          &Status);
       if (Retry == TRUE)
        {
-         /* FIXME!! */
-         DPRINT1("Should try again!\n");
+         DPRINT("Try again (RetryCount %lu)\n", RetryCount);
 
+         /* FIXME: Wait a little if we got a timeout error */
+
+         if (RetryCount--)
+           goto TryAgain;
        }
     }
   else
@@ -1179,6 +1845,7 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
     }
 
   ExFreePool(Srb->SenseInfoBuffer);
+  ExFreePool(Event);
 
   DPRINT("ScsiClassSendSrbSynchronous() done\n");
 
@@ -1186,12 +1853,100 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
 }
 
 
+/*
+ * @implemented
+ */
 VOID STDCALL
-ScsiClassSplitRequest(PDEVICE_OBJECT DeviceObject,
-                     PIRP Irp,
-                     ULONG MaximumBytes)
+ScsiClassSplitRequest(IN PDEVICE_OBJECT DeviceObject,
+                     IN PIRP Irp,
+                     IN ULONG MaximumBytes)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION CurrentStack;
+  PIO_STACK_LOCATION NextStack;
+  PIO_STACK_LOCATION NewStack;
+  PSCSI_REQUEST_BLOCK Srb;
+  LARGE_INTEGER Offset;
+  PIRP NewIrp;
+  PVOID DataBuffer;
+  ULONG TransferLength;
+  ULONG RequestCount;
+  ULONG DataLength;
+  ULONG i;
+
+  DPRINT("ScsiClassSplitRequest(DeviceObject %lx  Irp %lx  MaximumBytes %lu)\n",
+        DeviceObject, Irp, MaximumBytes);
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  CurrentStack = IoGetCurrentIrpStackLocation(Irp);
+  NextStack = IoGetNextIrpStackLocation(Irp);
+  DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
+
+  /* Initialize transfer data for first request */
+  Offset = CurrentStack->Parameters.Read.ByteOffset;
+  TransferLength = CurrentStack->Parameters.Read.Length;
+  DataLength = MaximumBytes;
+  RequestCount = ROUND_UP(TransferLength, MaximumBytes) / MaximumBytes;
+
+  /* Save request count in the original IRP */
+  NextStack->Parameters.Others.Argument1 = (PVOID)RequestCount;
+
+  DPRINT("RequestCount %lu\n", RequestCount);
+
+  for (i = 0; i < RequestCount; i++)
+    {
+      /* Create a new IRP */
+      NewIrp = IoAllocateIrp(DeviceObject->StackSize,
+                            FALSE);
+      if (NewIrp == NULL)
+       {
+         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+         Irp->IoStatus.Information = 0;
+
+         if (i == 0)
+           IoCompleteRequest(Irp,
+                             IO_NO_INCREMENT);
+         return;
+       }
+
+      /* Initialize the new IRP */
+      NewIrp->MdlAddress = Irp->MdlAddress;
+
+      IoSetNextIrpStackLocation(NewIrp);
+      NewStack = IoGetCurrentIrpStackLocation(NewIrp);
+
+      NewStack->MajorFunction = CurrentStack->MajorFunction;
+      NewStack->Parameters.Read.ByteOffset = Offset;
+      NewStack->Parameters.Read.Length = DataLength;
+      NewStack->DeviceObject = DeviceObject;
+
+      ScsiClassBuildRequest(DeviceObject,
+                           NewIrp);
+
+      NewStack = IoGetNextIrpStackLocation(NewIrp);
+      Srb = NewStack->Parameters.Others.Argument1;
+      Srb->DataBuffer = DataBuffer;
+
+      NewIrp->AssociatedIrp.MasterIrp = Irp;
+
+      /* Initialize completion routine */
+      IoSetCompletionRoutine(NewIrp,
+                            ScsiClassIoCompleteAssociated,
+                            Srb,
+                            TRUE,
+                            TRUE,
+                            TRUE);
+
+      /* Send the new IRP down to the port driver */
+      IoCallDriver(DeviceExtension->PortDeviceObject,
+                  NewIrp);
+
+      /* Adjust transfer data for next request */
+      DataBuffer = (PCHAR)DataBuffer + MaximumBytes;
+      TransferLength -= MaximumBytes;
+      DataLength = (TransferLength > MaximumBytes) ? MaximumBytes : TransferLength;
+      Offset.QuadPart = Offset.QuadPart + MaximumBytes;
+    }
 }
 
 
@@ -1225,8 +1980,10 @@ ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
 {
   PDEVICE_EXTENSION DeviceExtension;
   PIO_STACK_LOCATION IrpStack;
-  ULONG TransferLength;
-  ULONG TransferPages;
+  ULONG MaximumTransferLength;
+  ULONG CurrentTransferLength;
+  ULONG MaximumTransferPages;
+  ULONG CurrentTransferPages;
   NTSTATUS Status;
 
   DPRINT("ScsiClassReadWrite() called\n");
@@ -1238,7 +1995,10 @@ ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
         IrpStack->Parameters.Read.ByteOffset.QuadPart,
         IrpStack->Parameters.Read.Length);
 
-  TransferLength = IrpStack->Parameters.Read.Length;
+  MaximumTransferLength = DeviceExtension->PortCapabilities->MaximumTransferLength;
+  MaximumTransferPages = DeviceExtension->PortCapabilities->MaximumPhysicalPages;
+
+  CurrentTransferLength = IrpStack->Parameters.Read.Length;
 
   if ((DeviceObject->Flags & DO_VERIFY_VOLUME) &&
       !(IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
@@ -1270,7 +2030,7 @@ ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
     }
 
   /* Finish a zero-byte transfer */
-  if (TransferLength == 0)
+  if (CurrentTransferLength == 0)
     {
       Irp->IoStatus.Status = STATUS_SUCCESS;
       Irp->IoStatus.Information = 0;
@@ -1292,22 +2052,39 @@ ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
       return(STATUS_PENDING);
     }
 
-  IoMarkIrpPending(Irp);
-
   /* Adjust partition-relative starting offset to absolute offset */
-  IrpStack->Parameters.Read.ByteOffset.QuadPart += DeviceExtension->StartingOffset.QuadPart;
+  IrpStack->Parameters.Read.ByteOffset.QuadPart += 
+    (DeviceExtension->StartingOffset.QuadPart + DeviceExtension->DMByteSkew);
 
   /* Calculate number of pages in this transfer */
-  TransferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
-                                                IrpStack->Parameters.Read.Length);
+  CurrentTransferPages =
+    ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
+                                  IrpStack->Parameters.Read.Length);
 
-#if 0
-  if (TransferLength > maximumTransferLength ||
-      TransferPages > DeviceExtension->PortCapabilities->MaximumPhysicalPages)
+  if (CurrentTransferLength > MaximumTransferLength ||
+      CurrentTransferPages > MaximumTransferPages)
     {
-      /* FIXME: split request */
+       DPRINT("Split current request: MaximumTransferLength %lu  CurrentTransferLength %lu\n",
+             MaximumTransferLength, CurrentTransferLength);
+
+      /* Adjust the maximum transfer length */
+      CurrentTransferPages = DeviceExtension->PortCapabilities->MaximumPhysicalPages;
+
+      if (MaximumTransferLength > CurrentTransferPages * PAGE_SIZE)
+         MaximumTransferLength = CurrentTransferPages * PAGE_SIZE;
+
+      if (MaximumTransferLength == 0)
+         MaximumTransferLength = PAGE_SIZE;
+
+      IoMarkIrpPending(Irp);
+
+      /* Split current request */
+      ScsiClassSplitRequest(DeviceObject,
+                           Irp,
+                           MaximumTransferLength);
+
+      return(STATUS_PENDING);
     }
-#endif
 
   ScsiClassBuildRequest(DeviceObject,
                        Irp);
@@ -1320,20 +2097,6 @@ ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
 }
 
 
-static NTSTATUS STDCALL
-ScsiClassScsiDispatch(IN PDEVICE_OBJECT DeviceObject,
-                     IN PIRP Irp)
-{
-  DPRINT1("ScsiClassScsiDispatch() called\n");
-
-  Irp->IoStatus.Status = STATUS_SUCCESS;
-  Irp->IoStatus.Information = 0;
-  IoCompleteRequest(Irp, IO_NO_INCREMENT);
-
-  return(STATUS_SUCCESS);
-}
-
-
 static NTSTATUS STDCALL
 ScsiClassDeviceDispatch(IN PDEVICE_OBJECT DeviceObject,
                        IN PIRP Irp)
@@ -1379,62 +2142,71 @@ ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
 static VOID
 ScsiClassRetryRequest(PDEVICE_OBJECT DeviceObject,
                      PIRP Irp,
-                     PSCSI_REQUEST_BLOCK Srb)
+                     PSCSI_REQUEST_BLOCK Srb,
+                     BOOLEAN Associated)
 {
   PDEVICE_EXTENSION DeviceExtension;
   PIO_STACK_LOCATION CurrentIrpStack;
   PIO_STACK_LOCATION NextIrpStack;
+
   ULONG TransferLength;
 
-  DPRINT1("ScsiPortRetryRequest() called\n");
+  DPRINT("ScsiPortRetryRequest() called\n");
 
   DeviceExtension = DeviceObject->DeviceExtension;
   CurrentIrpStack = IoGetCurrentIrpStackLocation(Irp);
   NextIrpStack = IoGetNextIrpStackLocation(Irp);
 
-  if (CurrentIrpStack->MajorFunction == IRP_MJ_READ ||
-      CurrentIrpStack->MajorFunction == IRP_MJ_WRITE)
-    {
-      TransferLength = CurrentIrpStack->Parameters.Read.Length;
-    }
-  else if (Irp->MdlAddress != NULL)
-    {
-      TransferLength = Irp->MdlAddress->ByteCount;
-    }
-  else
+  if (CurrentIrpStack->MajorFunction != IRP_MJ_READ &&
+      CurrentIrpStack->MajorFunction != IRP_MJ_WRITE)
     {
-      TransferLength = 0;
+      /* We shouldn't setup the buffer pointer and transfer length on read/write requests. */
+      if (Irp->MdlAddress != NULL)
+        {
+          TransferLength = Irp->MdlAddress->ByteCount;
+        }
+      else
+        {
+          TransferLength = 0;
+        }
+
+      Srb->DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
+      Srb->DataTransferLength = TransferLength;
     }
 
-  Srb->DataTransferLength = TransferLength;
   Srb->SrbStatus = 0;
   Srb->ScsiStatus = 0;
-//  Srb->QueueTag = SP_UNTAGGED;
 
   /* Don't modify the flags */
 //  Srb->Flags = 
+//  Srb->QueueTag = SP_UNTAGGED;
 
-//  CurrentIrpStack->MajorFunction = IRP_MJ_SCSI;
-//  CurrentIrpStack->Parameters.Scsi.Srb = Srb;
-
-//  IoSkipCurrentIrpStackLocation(Irp);
-
-  *NextIrpStack = *CurrentIrpStack;
   NextIrpStack->MajorFunction = IRP_MJ_SCSI;
   NextIrpStack->Parameters.Scsi.Srb = Srb;
 
-
-  IoSetCompletionRoutine(Irp,
-                        ScsiClassIoComplete,
-                        Srb,
-                        TRUE,
-                        TRUE,
-                        TRUE);
-
-  DPRINT1("ScsiPortRetryRequest() done\n");
+  if (Associated == FALSE)
+    {
+      IoSetCompletionRoutine(Irp,
+                            ScsiClassIoComplete,
+                            Srb,
+                            TRUE,
+                            TRUE,
+                            TRUE);
+    }                  
+  else
+    {
+      IoSetCompletionRoutine(Irp,
+                            ScsiClassIoCompleteAssociated,
+                            Srb,
+                            TRUE,
+                            TRUE,
+                            TRUE);
+    }
 
   IoCallDriver(DeviceExtension->PortDeviceObject,
               Irp);
+
+  DPRINT("ScsiPortRetryRequest() done\n");
 }
 
 /* EOF */