Started using lookaside lists for SRBs.
[reactos.git] / reactos / drivers / storage / class2 / class2.c
index be78744..6815b41 100644 (file)
@@ -1,4 +1,22 @@
-/* $Id: class2.c,v 1.2 2002/01/14 01:43:26 ekohl Exp $
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2001, 2002 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* $Id: class2.c,v 1.13 2002/03/22 23:06:58 ekohl Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
  */
 
+/*
+ * TODO:
+ *     - a lot ;-)
+ */
+
+/* INCLUDES *****************************************************************/
+
 #include <ddk/ntddk.h>
 #include "../include/scsi.h"
 #include "../include/class2.h"
 
-//#define NDEBUG
+#define NDEBUG
 #include <debug.h>
 
-//#define UNIMPLEMENTED do {DbgPrint("%s:%d: Function not implemented", __FILE__, __LINE__); for(;;);} while (0)
 
 #define VERSION "0.0.1"
 
+#define TAG_SRBT  TAG('S', 'r', 'b', 'T')
+
 #define INQUIRY_DATA_SIZE 2048
 
 
@@ -41,7 +67,8 @@ static NTSTATUS STDCALL
 ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
                       IN PIRP Irp);
 
-//  -------------------------------------------------------  Public Interface
+
+/* FUNCTIONS ****************************************************************/
 
 //    DriverEntry
 //
@@ -103,7 +130,126 @@ VOID STDCALL
 ScsiClassBuildRequest(PDEVICE_OBJECT DeviceObject,
                      PIRP Irp)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION CurrentIrpStack;
+  PIO_STACK_LOCATION NextIrpStack;
+  LARGE_INTEGER StartingOffset;
+  LARGE_INTEGER StartingBlock;
+  PSCSI_REQUEST_BLOCK Srb;
+  PCDB Cdb;
+  ULONG LogicalBlockAddress;
+  USHORT TransferBlocks;
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  CurrentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+  NextIrpStack = IoGetNextIrpStackLocation(Irp);
+  StartingOffset = CurrentIrpStack->Parameters.Read.ByteOffset;
+
+  /* calculate logical block address */
+  StartingBlock.QuadPart = StartingOffset.QuadPart >> DeviceExtension->SectorShift;
+  LogicalBlockAddress = (ULONG)StartingBlock.u.LowPart;
+
+  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->SrbFlags = 0;
+  Srb->Length = sizeof(SCSI_REQUEST_BLOCK); //SCSI_REQUEST_BLOCK_SIZE;
+  Srb->OriginalRequest = Irp;
+  Srb->PathId = DeviceExtension->PathId;
+  Srb->TargetId = DeviceExtension->TargetId;
+  Srb->Lun = DeviceExtension->Lun;
+  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+  Srb->DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
+  Srb->DataTransferLength = CurrentIrpStack->Parameters.Read.Length;
+  Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+  Srb->QueueSortKey = LogicalBlockAddress;
+
+  Srb->SenseInfoBuffer = DeviceExtension->SenseData;
+  Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+  Srb->TimeOutValue =
+    ((Srb->DataTransferLength + 0xFFFF) >> 16) * DeviceExtension->TimeOutValue;
+
+  Srb->SrbStatus = SRB_STATUS_SUCCESS;
+  Srb->ScsiStatus = 0;
+  Srb->NextSrb = 0;
+
+  Srb->CdbLength = 10;
+  Cdb = (PCDB)Srb->Cdb;
+
+  /* Initialize ATAPI packet (12 bytes) */
+  RtlZeroMemory(Cdb,
+               MAXIMUM_CDB_SIZE);
+
+  Cdb->CDB10.LogicalUnitNumber = DeviceExtension->Lun;
+  TransferBlocks = (USHORT)(CurrentIrpStack->Parameters.Read.Length >> DeviceExtension->SectorShift);
+
+  /* Copy little endian values into CDB in big endian format */
+  Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte3;
+  Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte2;
+  Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte1;
+  Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte0;
+
+  Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1;
+  Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0;
+
+
+  if (CurrentIrpStack->MajorFunction == IRP_MJ_READ)
+    {
+      DPRINT("ScsiClassBuildRequest: Read Command\n");
+
+      Srb->SrbFlags |= SRB_FLAGS_DATA_IN;
+      Cdb->CDB10.OperationCode = SCSIOP_READ;
+    }
+  else
+    {
+      DPRINT("ScsiClassBuildRequest: Write Command\n");
+
+      Srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
+      Cdb->CDB10.OperationCode = SCSIOP_WRITE;
+    }
+
+
+#if 0
+  /* if this is not a write-through request, then allow caching */
+  if (!(CurrentIrpStack->Flags & SL_WRITE_THROUGH))
+    {
+      Srb->SrbFlags |= SRB_FLAGS_ADAPTER_CACHE_ENABLE;
+    }
+  else
+    {
+      /* if write caching is enable then force media access in the cdb */
+      if (DeviceExtension->DeviceFlags & DEV_WRITE_CACHE)
+       {
+         Cdb->CDB10.ForceUnitAccess = TRUE;
+       }
+    }
+#endif
+
+  /* or in the default flags from the device object. */
+  Srb->SrbFlags |= DeviceExtension->SrbFlags;
+
+
+  NextIrpStack->MajorFunction = IRP_MJ_SCSI;
+  NextIrpStack->Parameters.Scsi.Srb = Srb;
+  NextIrpStack->DeviceObject = DeviceObject;
+
+#if 0
+  /* save retry count in current IRP stack */
+  CurrentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+#endif
+
+  DPRINT("IoSetCompletionRoutine (Irp %p  Srb %p)\n", Irp, Srb);
+  IoSetCompletionRoutine(Irp,
+                        ScsiClassIoComplete,
+                        Srb,
+                        TRUE,
+                        TRUE,
+                        TRUE);
 }
 
 
@@ -113,7 +259,84 @@ ScsiClassClaimDevice(PDEVICE_OBJECT PortDeviceObject,
                     BOOLEAN Release,
                     PDEVICE_OBJECT *NewPortDeviceObject OPTIONAL)
 {
-  UNIMPLEMENTED;
+  PIO_STACK_LOCATION IoStack;
+  IO_STATUS_BLOCK IoStatusBlock;
+  SCSI_REQUEST_BLOCK Srb;
+  KEVENT Event;
+  PIRP Irp;
+  NTSTATUS Status;
+
+  DPRINT("ScsiClassClaimDevice() called\n");
+
+  if (NewPortDeviceObject != NULL)
+    *NewPortDeviceObject = NULL;
+
+  /* initialize an SRB */
+  RtlZeroMemory(&Srb,
+               sizeof(SCSI_REQUEST_BLOCK));
+  Srb.Length = SCSI_REQUEST_BLOCK_SIZE;
+  Srb.PathId = LunInfo->PathId;
+  Srb.TargetId = LunInfo->TargetId;
+  Srb.Lun = LunInfo->Lun;
+  Srb.Function =
+    (Release == TRUE) ? SRB_FUNCTION_RELEASE_DEVICE : SRB_FUNCTION_CLAIM_DEVICE;
+
+  KeInitializeEvent(&Event,
+                   NotificationEvent,
+                   FALSE);
+
+  Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
+                                     PortDeviceObject,
+                                     NULL,
+                                     0,
+                                     NULL,
+                                     0,
+                                     TRUE,
+                                     &Event,
+                                     &IoStatusBlock);
+  if (Irp == NULL)
+    {
+      DPRINT1("Failed to allocate Irp!\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  /* Link Srb and Irp */
+  IoStack = IoGetNextIrpStackLocation(Irp);
+  IoStack->Parameters.Scsi.Srb = &Srb;
+  Srb.OriginalRequest = Irp;
+
+  /* Call SCSI port driver */
+  Status = IoCallDriver(PortDeviceObject,
+                       Irp);
+  if (Status == STATUS_PENDING)
+    {
+      KeWaitForSingleObject(&Event,
+                           Suspended,
+                           KernelMode,
+                           FALSE,
+                           NULL);
+      Status = IoStatusBlock.Status;
+    }
+
+  if (Release == TRUE)
+    {
+      ObDereferenceObject(PortDeviceObject);
+      return(STATUS_SUCCESS);
+    }
+
+//  Status = ObReferenceObjectByPointer(Srb.DataBuffer,
+  Status = ObReferenceObjectByPointer(PortDeviceObject,
+                                     0,
+                                     NULL,
+                                     KernelMode);
+
+  if (NewPortDeviceObject != NULL)
+    {
+//      *NewPortDeviceObject = Srb.DataBuffer;
+      *NewPortDeviceObject = PortDeviceObject;
+    }
+
+  return(STATUS_SUCCESS);
 }
 
 
@@ -124,7 +347,65 @@ ScsiClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
                            IN OUT PDEVICE_OBJECT *DeviceObject,
                            IN PCLASS_INIT_DATA InitializationData)
 {
-  UNIMPLEMENTED;
+  PDEVICE_OBJECT InternalDeviceObject;
+  PDEVICE_EXTENSION DeviceExtension;
+  ANSI_STRING AnsiName;
+  UNICODE_STRING DeviceName;
+  NTSTATUS Status;
+
+  DPRINT("ScsiClassCreateDeviceObject() called\n");
+
+  *DeviceObject = NULL;
+
+  RtlInitAnsiString(&AnsiName,
+                   ObjectNameBuffer);
+
+  Status = RtlAnsiStringToUnicodeString(&DeviceName,
+                                       &AnsiName,
+                                       TRUE);
+  if (!NT_SUCCESS(Status))
+    {
+      return(Status);
+    }
+
+  DPRINT("Device name: '%wZ'\n", &DeviceName);
+
+  Status = IoCreateDevice(DriverObject,
+                         InitializationData->DeviceExtensionSize,
+                         &DeviceName,
+                         InitializationData->DeviceType,
+                         InitializationData->DeviceCharacteristics,
+                         FALSE,
+                         &InternalDeviceObject);
+  if (NT_SUCCESS(Status))
+    {
+      DeviceExtension = InternalDeviceObject->DeviceExtension;
+
+      DeviceExtension->ClassError = InitializationData->ClassError;
+      DeviceExtension->ClassReadWriteVerification = InitializationData->ClassReadWriteVerification;
+      DeviceExtension->ClassFindDevices = InitializationData->ClassFindDevices;
+      DeviceExtension->ClassDeviceControl = InitializationData->ClassDeviceControl;
+      DeviceExtension->ClassShutdownFlush = InitializationData->ClassShutdownFlush;
+      DeviceExtension->ClassCreateClose = InitializationData->ClassCreateClose;
+      DeviceExtension->ClassStartIo = InitializationData->ClassStartIo;
+
+      DeviceExtension->MediaChangeCount = 0;
+
+      if (PhysicalDeviceObject != NULL)
+       {
+         DeviceExtension->PhysicalDevice = PhysicalDeviceObject;
+       }
+      else
+       {
+         DeviceExtension->PhysicalDevice = InternalDeviceObject;
+        }
+
+      *DeviceObject = InternalDeviceObject;
+    }
+
+  RtlFreeUnicodeString(&DeviceName);
+
+  return(Status);
 }
 
 
@@ -150,8 +431,43 @@ ULONG STDCALL
 ScsiClassFindUnclaimedDevices(PCLASS_INIT_DATA InitializationData,
                              PSCSI_ADAPTER_BUS_INFO AdapterInformation)
 {
+  PSCSI_INQUIRY_DATA UnitInfo;
+  PINQUIRYDATA InquiryData;
+  PUCHAR Buffer;
+  ULONG Bus;
+  ULONG UnclaimedDevices = 0;
+  NTSTATUS Status;
+
   DPRINT("ScsiClassFindUnclaimedDevices() called!\n");
-  return(0);
+
+  DPRINT("NumberOfBuses: %lu\n",AdapterInformation->NumberOfBuses);
+  Buffer = (PUCHAR)AdapterInformation;
+  for (Bus = 0; Bus < (ULONG)AdapterInformation->NumberOfBuses; Bus++)
+    {
+      DPRINT("Searching bus %lu\n", Bus);
+
+      UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterInformation->BusData[Bus].InquiryDataOffset);
+
+      while (AdapterInformation->BusData[Bus].InquiryDataOffset)
+       {
+         InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData;
+
+         DPRINT("Device: '%.8s'\n", InquiryData->VendorId);
+
+         if ((InitializationData->ClassFindDeviceCallBack(InquiryData) == TRUE) &&
+             (UnitInfo->DeviceClaimed == FALSE))
+           {
+             UnclaimedDevices++;
+           }
+
+         if (UnitInfo->NextInquiryDataOffset == 0)
+           break;
+
+         UnitInfo = (PSCSI_INQUIRY_DATA) (Buffer + UnitInfo->NextInquiryDataOffset);
+       }
+    }
+
+  return(UnclaimedDevices);
 }
 
 
@@ -159,11 +475,20 @@ NTSTATUS STDCALL
 ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
                         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);
@@ -172,13 +497,14 @@ ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
                                      PortDeviceObject,
                                      NULL,
                                      0,
-                                     PortCapabilities,
-                                     sizeof(PVOID),
+                                     Buffer,
+                                     sizeof(IO_SCSI_CAPABILITIES),
                                      FALSE,
                                      &Event,
                                      &IoStatusBlock);
   if (Irp == NULL)
     {
+      ExFreePool(Buffer);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -191,7 +517,16 @@ ScsiClassGetCapabilities(PDEVICE_OBJECT PortDeviceObject,
                            KernelMode,
                            FALSE,
                            NULL);
-      return(IoStatusBlock.Status);
+      Status = IoStatusBlock.Status;
+    }
+
+  if (!NT_SUCCESS(Status))
+    {
+      ExFreePool(Buffer);
+    }
+  else
+    {
+      *PortCapabilities = Buffer;
     }
 
   return(Status);
@@ -208,10 +543,11 @@ ScsiClassGetInquiryData(PDEVICE_OBJECT PortDeviceObject,
   KEVENT Event;
   PIRP Irp;
 
-  Buffer = ExAllocatePool(NonPagedPool, /* FIXME: use paged pool */
-                         INQUIRY_DATA_SIZE);
-  *ConfigInfo = Buffer;
+  DPRINT("ScsiClassGetInquiryData() called\n");
 
+  *ConfigInfo = NULL;
+  Buffer = ExAllocatePool(NonPagedPool,
+                         INQUIRY_DATA_SIZE);
   if (Buffer == NULL)
     {
       return(STATUS_INSUFFICIENT_RESOURCES);
@@ -232,6 +568,7 @@ ScsiClassGetInquiryData(PDEVICE_OBJECT PortDeviceObject,
                                      &IoStatusBlock);
   if (Irp == NULL)
     {
+      ExFreePool(Buffer);
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
@@ -250,8 +587,13 @@ ScsiClassGetInquiryData(PDEVICE_OBJECT PortDeviceObject,
   if (!NT_SUCCESS(Status))
     {
       ExFreePool(Buffer);
-      *ConfigInfo = NULL;
     }
+  else
+    {
+      *ConfigInfo = Buffer;
+    }
+
+  DPRINT("ScsiClassGetInquiryData() done\n");
 
   return(Status);
 }
@@ -262,15 +604,18 @@ ScsiClassInitialize(PVOID Argument1,
                    PVOID Argument2,
                    PCLASS_INIT_DATA InitializationData)
 {
+  PCONFIGURATION_INFORMATION ConfigInfo;
   PDRIVER_OBJECT DriverObject = Argument1;
   WCHAR NameBuffer[80];
   UNICODE_STRING PortName;
-  ULONG PortNumber = 0;
+  ULONG PortNumber;
   PDEVICE_OBJECT PortDeviceObject;
   PFILE_OBJECT FileObject;
   BOOLEAN DiskFound = FALSE;
   NTSTATUS Status;
 
+  DPRINT("ScsiClassInitialize() called!\n");
+
   DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose;
   DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose;
   DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite;
@@ -284,25 +629,29 @@ ScsiClassInitialize(PVOID Argument1,
       DriverObject->DriverStartIo = InitializationData->ClassStartIo;
     }
 
+  ConfigInfo = IoGetConfigurationInformation();
+
+  DPRINT("ScsiPorts: %lu\n", ConfigInfo->ScsiPortCount);
+
   /* look for ScsiPortX scsi port devices */
-  do
+  for (PortNumber = 0; PortNumber < ConfigInfo->ScsiPortCount; PortNumber++)
     {
       swprintf(NameBuffer,
               L"\\Device\\ScsiPort%lu",
                PortNumber);
       RtlInitUnicodeString(&PortName,
                           NameBuffer);
-      DPRINT1("Checking scsi port %ld\n", PortNumber);
+      DPRINT("Checking scsi port %ld\n", PortNumber);
       Status = IoGetDeviceObjectPointer(&PortName,
                                        FILE_READ_ATTRIBUTES,
                                        &FileObject,
                                        &PortDeviceObject);
-      DPRINT1("Status 0x%08lX\n", Status);
+      DPRINT("Status 0x%08lX\n", Status);
       if (NT_SUCCESS(Status))
        {
-         DPRINT1("ScsiPort%lu found.\n", PortNumber);
+         DPRINT("ScsiPort%lu found.\n", PortNumber);
 
-         /* Check scsi port for attached disk drives */
+         /* check scsi port for attached disk drives */
          if (InitializationData->ClassFindDevices(DriverObject,
                                                   Argument2,
                                                   InitializationData,
@@ -311,25 +660,51 @@ ScsiClassInitialize(PVOID Argument1,
            {
              DiskFound = TRUE;
            }
-
-         ObDereferenceObject(PortDeviceObject);
-         ObDereferenceObject(FileObject);
        }
-       PortNumber++;
+      else
+       {
+         DbgPrint("Couldn't find ScsiPort%lu (Status %lx)\n", PortNumber, Status);
+       }
     }
-  while (NT_SUCCESS(Status));
 
-for(;;);
+  DPRINT("ScsiClassInitialize() done!\n");
 
   return((DiskFound == TRUE) ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE);
 }
 
 
+/**********************************************************************
+ * NAME                                                        EXPORTED
+ *     ScsiClassInitializeSrbLookasideList
+ *
+ * DESCRIPTION
+ *     Initializes a lookaside list for SRBs.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DeviceExtension
+ *             Class specific device extension.
+ *
+ *     NumberElements
+ *             Maximum number of elements of the lookaside list.
+ *
+ * RETURN VALUE
+ *     None.
+ */
+
 VOID STDCALL
 ScsiClassInitializeSrbLookasideList(PDEVICE_EXTENSION DeviceExtension,
                                    ULONG NumberElements)
 {
-  UNIMPLEMENTED;
+  ExInitializeNPagedLookasideList(&DeviceExtension->SrbLookasideListHead,
+                                 NULL,
+                                 NULL,
+                                 NonPagedPool,
+                                 sizeof(SCSI_REQUEST_BLOCK),
+                                 TAG_SRBT,
+                                 (USHORT)NumberElements);
 }
 
 
@@ -358,7 +733,72 @@ ScsiClassIoComplete(PDEVICE_OBJECT DeviceObject,
                    PIRP Irp,
                    PVOID Context)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION IrpStack;
+  PSCSI_REQUEST_BLOCK Srb;
+  NTSTATUS Status;
+
+  DPRINT("ScsiClassIoComplete(DeviceObject %p  Irp %p  Context %p) called\n",
+         DeviceObject, Irp, Context);
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  Srb = (PSCSI_REQUEST_BLOCK)Context;
+  DPRINT("Srb %p\n", Srb);
+
+  IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+#if 0
+  if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
+    {
+      Status = STATUS_SUCCESS;
+    }
+  else
+    {
+      /* FIXME: improve error handling */
+      DPRINT1("Srb->SrbStatus %lx\n", Srb->SrbStatus);
+
+      if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_PENDING)
+       {
+         Status = STATUS_SUCCESS;
+       }
+      else
+      Status = STATUS_UNSUCCESSFUL;
+    }
+#endif
+
+  /* FIXME: use lookaside list instead */
+  DPRINT("Freed SRB %p\n", IrpStack->Parameters.Scsi.Srb);
+  ExFreePool(IrpStack->Parameters.Scsi.Srb);
+
+//  Irp->IoStatus.Status = Status;
+#if 0
+  if (!NT_SUCCESS(Status) &&
+      IoIsErrorUserInduced(Status))
+    {
+      IoSetHardErrorOrVerifyDevice(Irp,
+                                  DeviceObject);
+      Irp->IoStatus.Information = 0;
+    }
+
+  if (Irp->PendingReturned)
+    {
+      IoMarkIrpPending(Irp);
+    }
+#endif
+
+  if (DeviceExtension->ClassStartIo != NULL)
+    {
+      if (IrpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL)
+       {
+         IoStartNextPacket(DeviceObject,
+                           FALSE);
+       }
+    }
+
+  DPRINT("ScsiClassIoComplete() done (Status %lx)\n", Status);
+
+//  return(Status);
+  return(STATUS_SUCCESS);
 }
 
 
@@ -391,7 +831,100 @@ ScsiClassQueryTimeOutRegistryValue(IN PUNICODE_STRING RegistryPath)
 NTSTATUS STDCALL
 ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  PREAD_CAPACITY_DATA CapacityBuffer;
+  SCSI_REQUEST_BLOCK Srb;
+  PCDB Cdb;
+  NTSTATUS Status;
+  ULONG LastSector;
+  ULONG SectorSize;
+
+  DPRINT("ScsiClassReadDriveCapacity() called\n");
+
+  DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  CapacityBuffer = ExAllocatePool(NonPagedPool,
+                                 sizeof(READ_CAPACITY_DATA));
+  if (CapacityBuffer == NULL)
+    {
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+  Srb.CdbLength = 10;
+  Srb.TimeOutValue = DeviceExtension->TimeOutValue;
+
+  Cdb = (PCDB)Srb.Cdb;
+  Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+
+
+  Status = ScsiClassSendSrbSynchronous(DeviceObject,
+                                      &Srb,
+                                      CapacityBuffer,
+                                      sizeof(READ_CAPACITY_DATA),
+                                      FALSE);
+  DPRINT("Status: %lx\n", Status);
+  DPRINT("Srb: %p\n", &Srb);
+  if (NT_SUCCESS(Status))
+    {
+      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];
+
+      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;
+
+      DPRINT("SectorSize: %lu  SectorCount: %lu\n", SectorSize, LastSector + 1);
+    }
+  else
+    {
+      /* Use default values if disk geometry cannot be read */
+      RtlZeroMemory(&DeviceExtension->DiskGeometry,
+                   sizeof(DISK_GEOMETRY));
+      DeviceExtension->DiskGeometry->BytesPerSector = 512;
+      DeviceExtension->SectorShift = 9;
+      DeviceExtension->PartitionLength.QuadPart = 0;
+
+      if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+       {
+         DeviceExtension->DiskGeometry->MediaType = RemovableMedia;
+       }
+      else
+       {
+         DeviceExtension->DiskGeometry->MediaType = FixedMedia;
+       }
+    }
+
+  ExFreePool(CapacityBuffer);
+
+  DPRINT("ScsiClassReadDriveCapacity() done\n");
+
+  return(Status);
 }
 
 
@@ -421,7 +954,105 @@ ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject,
                            ULONG BufferLength,
                            BOOLEAN WriteToDevice)
 {
-  UNIMPLEMENTED;
+  PDEVICE_EXTENSION DeviceExtension;
+  IO_STATUS_BLOCK IoStatusBlock;
+  PIO_STACK_LOCATION IoStack;
+  ULONG RequestType;
+  KEVENT Event;
+  PIRP Irp;
+  NTSTATUS Status;
+
+
+  DPRINT("ScsiClassSendSrbSynchronous() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+
+  Srb->PathId = DeviceExtension->PathId;
+  Srb->TargetId = DeviceExtension->TargetId;
+  Srb->Lun = DeviceExtension->Lun;
+  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+  /* FIXME: more srb initialization required? */
+
+
+  if (BufferAddress == NULL)
+    {
+        BufferLength = 0;
+        RequestType = IOCTL_SCSI_EXECUTE_NONE;
+        Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
+    }
+  else
+    {
+      if (WriteToDevice == TRUE)
+       {
+         RequestType = IOCTL_SCSI_EXECUTE_OUT;
+         Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
+       }
+      else
+       {
+         RequestType = IOCTL_SCSI_EXECUTE_IN;
+         Srb->SrbFlags = SRB_FLAGS_DATA_IN;
+       }
+    }
+
+  Srb->DataTransferLength = BufferLength;
+  Srb->DataBuffer = BufferAddress;
+
+
+  KeInitializeEvent(&Event,
+                   NotificationEvent,
+                   FALSE);
+
+  Irp = IoBuildDeviceIoControlRequest(RequestType,
+                                     DeviceExtension->PortDeviceObject,
+                                     NULL,
+                                     0,
+                                     BufferAddress,
+                                     BufferLength,
+                                     TRUE,
+                                     &Event,
+                                     &IoStatusBlock);
+  if (Irp == NULL)
+    {
+      DPRINT1("IoBuildDeviceIoControlRequest() failed\n");
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  /* FIXME: more irp initialization required? */
+
+
+  /* Attach Srb to the Irp */
+  IoStack = IoGetNextIrpStackLocation(Irp);
+  IoStack->Parameters.Scsi.Srb = Srb;
+  Srb->OriginalRequest = Irp;
+
+
+  /* Call the SCSI port driver */
+  Status = IoCallDriver(DeviceExtension->PortDeviceObject,
+                       Irp);
+  if (Status == STATUS_PENDING)
+    {
+      KeWaitForSingleObject(&Event,
+                           Suspended,
+                           KernelMode,
+                           FALSE,
+                           NULL);
+    }
+
+  if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
+    {
+      /* FIXME!! */
+      DPRINT1("Fix return value!\n");
+      Status = STATUS_UNSUCCESSFUL;
+    }
+  else
+    {
+      Status = STATUS_SUCCESS;
+    }
+
+  DPRINT("ScsiClassSendSrbSynchronous() done\n");
+
+  return(Status);
 }
 
 
@@ -434,12 +1065,22 @@ ScsiClassSplitRequest(PDEVICE_OBJECT DeviceObject,
 }
 
 
-/* Internal Routines **************/
+/* INTERNAL FUNCTIONS *******************************************************/
 
 static NTSTATUS STDCALL
 ScsiClassCreateClose(IN PDEVICE_OBJECT DeviceObject,
                     IN PIRP Irp)
 {
+  PDEVICE_EXTENSION DeviceExtension;
+
+  DPRINT("ScsiClassCreateClose() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+
+  if (DeviceExtension->ClassCreateClose)
+    return(DeviceExtension->ClassCreateClose(DeviceObject,
+                                            Irp));
+
   Irp->IoStatus.Status = STATUS_SUCCESS;
   Irp->IoStatus.Information = 0;
   IoCompleteRequest(Irp, IO_NO_INCREMENT);
@@ -447,15 +1088,105 @@ ScsiClassCreateClose(IN PDEVICE_OBJECT DeviceObject,
   return(STATUS_SUCCESS);
 }
 
+
 static NTSTATUS STDCALL
 ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject,
                   IN PIRP Irp)
 {
-  Irp->IoStatus.Status = STATUS_SUCCESS;
-  Irp->IoStatus.Information = 0;
-  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+  PDEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION IrpStack;
+  ULONG TransferLength;
+  ULONG TransferPages;
+  NTSTATUS Status;
 
-  return(STATUS_SUCCESS);
+  DPRINT("ScsiClassReadWrite() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  IrpStack  = IoGetCurrentIrpStackLocation(Irp);
+
+  DPRINT("Relative Offset: %I64u  Length: %lu\n",
+        IrpStack->Parameters.Read.ByteOffset.QuadPart,
+        IrpStack->Parameters.Read.Length);
+
+  TransferLength = IrpStack->Parameters.Read.Length;
+
+  if ((DeviceObject->Flags & DO_VERIFY_VOLUME) &&
+      !(IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
+    {
+      IoSetHardErrorOrVerifyDevice(Irp,
+                                  DeviceObject);
+
+      Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+      Irp->IoStatus.Information = 0;
+
+      IoCompleteRequest(Irp,
+                       IO_NO_INCREMENT);
+      return(STATUS_VERIFY_REQUIRED);
+    }
+
+  /* Class driver verifies the IRP */
+  Status = DeviceExtension->ClassReadWriteVerification(DeviceObject,
+                                                      Irp);
+  if (!NT_SUCCESS(Status))
+    {
+      IoCompleteRequest(Irp,
+                       IO_NO_INCREMENT);
+      return(Status);
+    }
+  else if (Status == STATUS_PENDING)
+    {
+      IoMarkIrpPending(Irp);
+      return(STATUS_PENDING);
+    }
+
+  /* Finish a zero-byte transfer */
+  if (TransferLength == 0)
+    {
+      Irp->IoStatus.Status = STATUS_SUCCESS;
+      Irp->IoStatus.Information = 0;
+      IoCompleteRequest(Irp,
+                       IO_NO_INCREMENT);
+      return(STATUS_SUCCESS);
+    }
+
+  if (DeviceExtension->ClassStartIo != NULL)
+    {
+      DPRINT("ScsiClassReadWrite() starting packet\n");
+
+      IoMarkIrpPending(Irp);
+      IoStartPacket(DeviceObject,
+                   Irp,
+                   NULL,
+                   NULL);
+
+      return(STATUS_PENDING);
+    }
+
+  IoMarkIrpPending(Irp);
+
+  /* Adjust partition-relative starting offset to absolute offset */
+  IrpStack->Parameters.Read.ByteOffset.QuadPart += DeviceExtension->StartingOffset.QuadPart;
+
+  /* Calculate number of pages in this transfer */
+  TransferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
+                                                IrpStack->Parameters.Read.Length);
+
+#if 0
+  if (TransferLength > maximumTransferLength ||
+      TransferPages > DeviceExtension->PortCapabilities->MaximumPhysicalPages)
+    {
+      /* FIXME: split request */
+    }
+#endif
+
+  ScsiClassBuildRequest(DeviceObject,
+                       Irp);
+
+  DPRINT("ScsiClassReadWrite() done\n");
+
+  /* Call the port driver */
+  return(IoCallDriver(DeviceExtension->PortDeviceObject,
+                     Irp));
 }
 
 
@@ -463,6 +1194,8 @@ 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);
@@ -475,11 +1208,20 @@ static NTSTATUS STDCALL
 ScsiClassDeviceDispatch(IN PDEVICE_OBJECT DeviceObject,
                        IN PIRP Irp)
 {
-  Irp->IoStatus.Status = STATUS_SUCCESS;
-  Irp->IoStatus.Information = 0;
+  PDEVICE_EXTENSION DeviceExtension;
+
+  DPRINT("ScsiClassDeviceDispatch() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  if (DeviceExtension->ClassDeviceControl)
+    {
+      return(DeviceExtension->ClassDeviceControl(DeviceObject, Irp));
+    }
+
+  Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
   IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
-  return(STATUS_SUCCESS);
+  return(STATUS_INVALID_DEVICE_REQUEST);
 }
 
 
@@ -487,11 +1229,20 @@ static NTSTATUS STDCALL
 ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
                       IN PIRP Irp)
 {
-  Irp->IoStatus.Status = STATUS_SUCCESS;
-  Irp->IoStatus.Information = 0;
+  PDEVICE_EXTENSION DeviceExtension;
+
+  DPRINT("ScsiClassShutdownFlush() called\n");
+
+  DeviceExtension = DeviceObject->DeviceExtension;
+  if (DeviceExtension->ClassShutdownFlush)
+    {
+      return(DeviceExtension->ClassShutdownFlush(DeviceObject, Irp));
+    }
+
+  Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
   IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
-  return(STATUS_SUCCESS);
+  return(STATUS_INVALID_DEVICE_REQUEST);
 }
 
 /* EOF */