/*
 *  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.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS Storage Stack
 * FILE:            drivers/storage/scsiport/scsiport.c
 * PURPOSE:         SCSI port driver
 * PROGRAMMER:      Eric Kohl
 *                  Aleksey Bragin (aleksey reactos org)
 */

/* INCLUDES *****************************************************************/

#include "precomp.h"

#include <ntddk.h>
#include <stdio.h>
#include <scsi.h>
#include <ntddscsi.h>
#include <ntdddisk.h>
#include <mountdev.h>

#define NDEBUG
#include <debug.h>

#include "scsiport_int.h"

ULONG InternalDebugLevel = 0x00;

#undef ScsiPortMoveMemory

/* GLOBALS *******************************************************************/

static BOOLEAN
SpiGetPciConfigData(IN PDRIVER_OBJECT DriverObject,
                    IN PDEVICE_OBJECT DeviceObject,
                    IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
                    IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig,
                    IN PUNICODE_STRING RegistryPath,
                    IN ULONG BusNumber,
                    IN OUT PPCI_SLOT_NUMBER NextSlotNumber);

static NTSTATUS NTAPI
ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject,
		    IN PIRP Irp);

static DRIVER_DISPATCH ScsiPortDispatchScsi;
static NTSTATUS NTAPI
ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject,
		     IN PIRP Irp);

static NTSTATUS NTAPI
ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject,
		      IN PIRP Irp);

static DRIVER_STARTIO ScsiPortStartIo;
static VOID NTAPI
ScsiPortStartIo(IN PDEVICE_OBJECT DeviceObject,
		IN PIRP Irp);

static BOOLEAN NTAPI
ScsiPortStartPacket(IN OUT PVOID Context);

IO_ALLOCATION_ACTION
NTAPI
SpiAdapterControl(PDEVICE_OBJECT DeviceObject, PIRP Irp,
                  PVOID MapRegisterBase, PVOID Context);

static PSCSI_PORT_LUN_EXTENSION
SpiAllocateLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension);

static PSCSI_PORT_LUN_EXTENSION
SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		    IN UCHAR PathId,
		    IN UCHAR TargetId,
		    IN UCHAR Lun);

static PSCSI_REQUEST_BLOCK_INFO
SpiAllocateSrbStructures(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                         PSCSI_PORT_LUN_EXTENSION LunExtension,
                         PSCSI_REQUEST_BLOCK Srb);

static NTSTATUS
SpiSendInquiry (IN PDEVICE_OBJECT DeviceObject,
		IN PSCSI_LUN_INFO LunInfo);

static VOID
SpiScanAdapter (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension);

static NTSTATUS
SpiGetInquiryData (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		   IN PIRP Irp);

static PSCSI_REQUEST_BLOCK_INFO
SpiGetSrbData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
              IN UCHAR PathId,
              IN UCHAR TargetId,
              IN UCHAR Lun,
              IN UCHAR QueueTag);

static BOOLEAN NTAPI
ScsiPortIsr(IN PKINTERRUPT Interrupt,
	    IN PVOID ServiceContext);

static VOID NTAPI
ScsiPortDpcForIsr(IN PKDPC Dpc,
		  IN PDEVICE_OBJECT DpcDeviceObject,
		  IN PIRP DpcIrp,
		  IN PVOID DpcContext);

static VOID NTAPI
ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject,
		PVOID Context);

IO_ALLOCATION_ACTION
NTAPI
ScsiPortAllocateAdapterChannel(IN PDEVICE_OBJECT DeviceObject,
                               IN PIRP Irp,
                               IN PVOID MapRegisterBase,
                               IN PVOID Context);

static NTSTATUS
SpiBuildDeviceMap (PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		   PUNICODE_STRING RegistryPath);

static NTSTATUS
SpiStatusSrbToNt(UCHAR SrbStatus);

static VOID
SpiSendRequestSense(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    IN PSCSI_REQUEST_BLOCK Srb);

static IO_COMPLETION_ROUTINE SpiCompletionRoutine;
NTSTATUS NTAPI
SpiCompletionRoutine(PDEVICE_OBJECT DeviceObject,
                     PIRP Irp,
                     PVOID Context);

static VOID
NTAPI
SpiProcessCompletedRequest(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                           IN PSCSI_REQUEST_BLOCK_INFO SrbInfo,
                           OUT PBOOLEAN NeedToCallStartIo);

VOID NTAPI
SpiGetNextRequestFromLun(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                         IN PSCSI_PORT_LUN_EXTENSION LunExtension);

VOID NTAPI
SpiMiniportTimerDpc(IN struct _KDPC *Dpc,
                    IN PVOID DeviceObject,
                    IN PVOID SystemArgument1,
                    IN PVOID SystemArgument2);

static NTSTATUS
SpiCreatePortConfig(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    PHW_INITIALIZATION_DATA HwInitData,
                    PCONFIGURATION_INFO InternalConfigInfo,
                    PPORT_CONFIGURATION_INFORMATION ConfigInfo,
                    BOOLEAN FirstCall);

NTSTATUS NTAPI
SpQueryDeviceCallout(IN PVOID  Context,
                     IN PUNICODE_STRING  PathName,
                     IN INTERFACE_TYPE  BusType,
                     IN ULONG  BusNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *BusInformation,
                     IN CONFIGURATION_TYPE  ControllerType,
                     IN ULONG  ControllerNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *ControllerInformation,
                     IN CONFIGURATION_TYPE  PeripheralType,
                     IN ULONG  PeripheralNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *PeripheralInformation);

static VOID
SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                   IN HANDLE Key,
                   IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
                   IN PCONFIGURATION_INFO InternalConfigInfo,
                   IN PUCHAR Buffer);

static VOID
SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData,
                    IN PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor,
                    IN PPORT_CONFIGURATION_INFORMATION PortConfig);

static PCM_RESOURCE_LIST
SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    PPORT_CONFIGURATION_INFORMATION PortConfig);

static VOID
SpiCleanupAfterInit(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension);

static NTSTATUS
SpiHandleAttachRelease(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                       PIRP Irp);

static NTSTATUS
SpiAllocateCommonBuffer(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ULONG NonCachedSize);

NTHALAPI ULONG NTAPI HalGetBusData(BUS_DATA_TYPE, ULONG, ULONG, PVOID, ULONG);
NTHALAPI ULONG NTAPI HalGetInterruptVector(INTERFACE_TYPE, ULONG, ULONG, ULONG, PKIRQL, PKAFFINITY);
NTHALAPI NTSTATUS NTAPI HalAssignSlotResources(PUNICODE_STRING, PUNICODE_STRING, PDRIVER_OBJECT, PDEVICE_OBJECT, INTERFACE_TYPE, ULONG, ULONG, PCM_RESOURCE_LIST *);

/* FUNCTIONS *****************************************************************/

/**********************************************************************
 * 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.
 *
 * RETURN VALUE
 * 	Status.
 */

NTSTATUS NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,
	    IN PUNICODE_STRING RegistryPath)
{
    DPRINT("ScsiPort Driver %s\n", VERSION);
    return(STATUS_SUCCESS);
}


/**********************************************************************
 * NAME							EXPORTED
 *	ScsiDebugPrint
 *
 * DESCRIPTION
 *	Prints debugging messages.
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	DebugPrintLevel
 *		Debug level of the given message.
 *
 *	DebugMessage
 *		Pointer to printf()-compatible format string.
 *
 *	...
  		Additional output data (see printf()).
 *
 * RETURN VALUE
 * 	None.
 *
 * @implemented
 */

VOID
ScsiDebugPrint(IN ULONG DebugPrintLevel,
	       IN PCHAR DebugMessage,
	       ...)
{
    char Buffer[256];
    va_list ap;

    if (DebugPrintLevel > InternalDebugLevel)
        return;

    va_start(ap, DebugMessage);
    vsprintf(Buffer, DebugMessage, ap);
    va_end(ap);

    DbgPrint(Buffer);
}

/* An internal helper function for ScsiPortCompleteRequest */
VOID
NTAPI
SpiCompleteRequest(IN PVOID HwDeviceExtension,
                   IN PSCSI_REQUEST_BLOCK_INFO SrbInfo,
                   IN UCHAR SrbStatus)
{
    PSCSI_REQUEST_BLOCK Srb;

    /* Get current SRB */
    Srb = SrbInfo->Srb;

    /* Return if there is no SRB or it is not active */
    if (!Srb || !(Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) return;

    /* Set status */
    Srb->SrbStatus = SrbStatus;

    /* Set data transfered to 0 */
    Srb->DataTransferLength = 0;

    /* Notify */
    ScsiPortNotification(RequestComplete,
                         HwDeviceExtension,
                         Srb);
}

/*
 * @unimplemented
 */
VOID NTAPI
ScsiPortCompleteRequest(IN PVOID HwDeviceExtension,
                        IN UCHAR PathId,
                        IN UCHAR TargetId,
                        IN UCHAR Lun,
                        IN UCHAR SrbStatus)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;
    PLIST_ENTRY ListEntry;
    ULONG BusNumber;
    ULONG Target;

    DPRINT("ScsiPortCompleteRequest() called\n");

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    /* Go through all buses */
    for (BusNumber = 0; BusNumber < 8; BusNumber++)
    {
        /* Go through all targets */
        for (Target = 0; Target < DeviceExtension->MaxTargedIds; Target++)
        {
            /* Get logical unit list head */
            LunExtension = DeviceExtension->LunExtensionList[Target % 8];

            /* Go through all logical units */
            while (LunExtension)
            {
                /* Now match what caller asked with what we are at now */
                if ((PathId == SP_UNTAGGED || PathId == LunExtension->PathId) &&
                    (TargetId == SP_UNTAGGED || TargetId == LunExtension->TargetId) &&
                    (Lun == SP_UNTAGGED || Lun == LunExtension->Lun))
                {
                    /* Yes, that's what caller asked for. Complete abort requests */
                    if (LunExtension->CompletedAbortRequests)
                    {
                        /* TODO: Save SrbStatus in this request */
                        DPRINT1("Completing abort request without setting SrbStatus!\n");

                        /* Issue a notification request */
                        ScsiPortNotification(RequestComplete,
                                             HwDeviceExtension,
                                             LunExtension->CompletedAbortRequests);
                    }

                    /* Complete the request using our helper */
                    SpiCompleteRequest(HwDeviceExtension,
                                       &LunExtension->SrbInfo,
                                       SrbStatus);

                    /* Go through the queue and complete everything there too */
                    ListEntry = LunExtension->SrbInfo.Requests.Flink;
                    while (ListEntry != &LunExtension->SrbInfo.Requests)
                    {
                        /* Get the actual SRB info entry */
                        SrbInfo = CONTAINING_RECORD(ListEntry,
                                                    SCSI_REQUEST_BLOCK_INFO,
                                                    Requests);

                        /* Complete it */
                        SpiCompleteRequest(HwDeviceExtension,
                                           SrbInfo,
                                           SrbStatus);

                        /* Advance to the next request in queue */
                        ListEntry = SrbInfo->Requests.Flink;
                    }
                }

                /* Advance to the next one */
                LunExtension = LunExtension->Next;
            }
        }
    }
}

/*
 * @unimplemented
 */
VOID NTAPI
ScsiPortFlushDma(IN PVOID HwDeviceExtension)
{
  DPRINT("ScsiPortFlushDma()\n");
  UNIMPLEMENTED;
}


/*
 * @implemented
 */
VOID NTAPI
ScsiPortFreeDeviceBase(IN PVOID HwDeviceExtension,
		       IN PVOID MappedAddress)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PMAPPED_ADDRESS NextMa, LastMa;

    //DPRINT("ScsiPortFreeDeviceBase() called\n");

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    /* Initialize our pointers */
    NextMa = DeviceExtension->MappedAddressList;
    LastMa = NextMa;

    while (NextMa)
    {
        if (NextMa->MappedAddress == MappedAddress)
        {
            /* Unmap it first */
            MmUnmapIoSpace(MappedAddress, NextMa->NumberOfBytes);

            /* Remove it from the list */
            if (NextMa == DeviceExtension->MappedAddressList)
            {
                /* Remove the first entry */
                DeviceExtension->MappedAddressList = NextMa->NextMappedAddress;
            }
            else
            {
                LastMa->NextMappedAddress = NextMa->NextMappedAddress;
            }

            /* Free the resources and quit */
            ExFreePool(NextMa);

            return;
        }
        else
        {
            LastMa = NextMa;
            NextMa = NextMa->NextMappedAddress;
        }
    }
}


/*
 * @implemented
 */
ULONG NTAPI
ScsiPortGetBusData(IN PVOID DeviceExtension,
		   IN ULONG BusDataType,
		   IN ULONG SystemIoBusNumber,
		   IN ULONG SlotNumber,
		   IN PVOID Buffer,
		   IN ULONG Length)
{
    DPRINT("ScsiPortGetBusData()\n");

    if (Length)
    {
        /* If Length is non-zero, just forward the call to
        HalGetBusData() function */
        return HalGetBusData(BusDataType,
                             SystemIoBusNumber,
                             SlotNumber,
                             Buffer,
                             Length);
    }

    /* We have a more complex case here */
    UNIMPLEMENTED;
    return 0;
}

/*
 * @implemented
 */
ULONG NTAPI
ScsiPortSetBusDataByOffset(IN PVOID DeviceExtension,
                           IN ULONG BusDataType,
                           IN ULONG SystemIoBusNumber,
                           IN ULONG SlotNumber,
                           IN PVOID Buffer,
                           IN ULONG Offset,
                           IN ULONG Length)
{
    DPRINT("ScsiPortSetBusDataByOffset()\n");
    return HalSetBusDataByOffset(BusDataType,
                                 SystemIoBusNumber,
                                 SlotNumber,
                                 Buffer,
                                 Offset,
                                 Length);
}

/*
 * @implemented
 */
PVOID NTAPI
ScsiPortGetDeviceBase(IN PVOID HwDeviceExtension,
		      IN INTERFACE_TYPE BusType,
		      IN ULONG SystemIoBusNumber,
		      IN SCSI_PHYSICAL_ADDRESS IoAddress,
		      IN ULONG NumberOfBytes,
		      IN BOOLEAN InIoSpace)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PHYSICAL_ADDRESS TranslatedAddress;
    PMAPPED_ADDRESS DeviceBase;
    ULONG AddressSpace;
    PVOID MappedAddress;

    //DPRINT ("ScsiPortGetDeviceBase() called\n");

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    AddressSpace = (ULONG)InIoSpace;
    if (HalTranslateBusAddress(BusType,
                               SystemIoBusNumber,
                               IoAddress,
                               &AddressSpace,
                               &TranslatedAddress) == FALSE)
    {
        return NULL;
    }

    /* i/o space */
    if (AddressSpace != 0)
        return((PVOID)(ULONG_PTR)TranslatedAddress.QuadPart);

    MappedAddress = MmMapIoSpace(TranslatedAddress,
                                 NumberOfBytes,
                                 FALSE);

    DeviceBase = ExAllocatePoolWithTag(NonPagedPool,
                                sizeof(MAPPED_ADDRESS), TAG_SCSIPORT);

    if (DeviceBase == NULL)
        return MappedAddress;

    DeviceBase->MappedAddress = MappedAddress;
    DeviceBase->NumberOfBytes = NumberOfBytes;
    DeviceBase->IoAddress = IoAddress;
    DeviceBase->BusNumber = SystemIoBusNumber;

    /* Link it to the Device Extension list */
    DeviceBase->NextMappedAddress = DeviceExtension->MappedAddressList;
    DeviceExtension->MappedAddressList = DeviceBase;

    return MappedAddress;
}

/*
 * @unimplemented
 */
PVOID NTAPI
ScsiPortGetLogicalUnit(IN PVOID HwDeviceExtension,
		       IN UCHAR PathId,
		       IN UCHAR TargetId,
		       IN UCHAR Lun)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PSCSI_PORT_LUN_EXTENSION LunExtension;

    DPRINT("ScsiPortGetLogicalUnit() called\n");

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    /* Check the extension size */
    if (!DeviceExtension->LunExtensionSize)
    {
        /* They didn't want one */
        return NULL;
    }

    LunExtension = SpiGetLunExtension(DeviceExtension,
                                      PathId,
                                      TargetId,
                                      Lun);
    /* Check that the logical unit exists */
    if (!LunExtension)
    {
        /* Nope, return NULL */
        return NULL;
    }

    /* Return the logical unit miniport extension */
    return (LunExtension + 1);
}


/*
 * @implemented
 */
SCSI_PHYSICAL_ADDRESS NTAPI
ScsiPortGetPhysicalAddress(IN PVOID HwDeviceExtension,
                           IN PSCSI_REQUEST_BLOCK Srb OPTIONAL,
                           IN PVOID VirtualAddress,
                           OUT ULONG *Length)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    SCSI_PHYSICAL_ADDRESS PhysicalAddress;
    SIZE_T BufferLength = 0;
    ULONG_PTR Offset;
    PSCSI_SG_ADDRESS SGList;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;

    DPRINT("ScsiPortGetPhysicalAddress(%p %p %p %p)\n",
        HwDeviceExtension, Srb, VirtualAddress, Length);

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    if (Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress)
    {
        /* Simply look it up in the allocated common buffer */
        Offset = (PUCHAR)VirtualAddress - (PUCHAR)DeviceExtension->SrbExtensionBuffer;

        BufferLength = DeviceExtension->CommonBufferLength - Offset;
        PhysicalAddress.QuadPart = DeviceExtension->PhysicalAddress.QuadPart + Offset;
    }
    else if (DeviceExtension->MapRegisters)
    {
        /* Scatter-gather list must be used */
        SrbInfo = SpiGetSrbData(DeviceExtension,
                               Srb->PathId,
                               Srb->TargetId,
                               Srb->Lun,
                               Srb->QueueTag);

        SGList = SrbInfo->ScatterGather;

        /* Find needed item in the SG list */
        Offset = (PCHAR)VirtualAddress - (PCHAR)Srb->DataBuffer;
        while (Offset >= SGList->Length)
        {
            Offset -= SGList->Length;
            SGList++;
        }

        /* We're done, store length and physical address */
        BufferLength = SGList->Length - Offset;
        PhysicalAddress.QuadPart = SGList->PhysicalAddress.QuadPart + Offset;
    }
    else
    {
        /* Nothing */
        *Length = 0;
        PhysicalAddress.QuadPart = (LONGLONG)(SP_UNINITIALIZED_VALUE);
    }

    *Length = (ULONG)BufferLength;
    return PhysicalAddress;
}


/*
 * @unimplemented
 */
PSCSI_REQUEST_BLOCK NTAPI
ScsiPortGetSrb(IN PVOID DeviceExtension,
	       IN UCHAR PathId,
	       IN UCHAR TargetId,
	       IN UCHAR Lun,
	       IN LONG QueueTag)
{
  DPRINT1("ScsiPortGetSrb() unimplemented\n");
  UNIMPLEMENTED;
  return NULL;
}


/*
 * @implemented
 */
PVOID NTAPI
ScsiPortGetUncachedExtension(IN PVOID HwDeviceExtension,
			     IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
			     IN ULONG NumberOfBytes)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    DEVICE_DESCRIPTION DeviceDescription;
    ULONG MapRegistersCount;
    NTSTATUS Status;

    DPRINT("ScsiPortGetUncachedExtension(%p %p %lu)\n",
        HwDeviceExtension, ConfigInfo, NumberOfBytes);

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    /* Check for allocated common DMA buffer */
    if (DeviceExtension->SrbExtensionBuffer != NULL)
    {
        DPRINT1("The HBA has already got a common DMA buffer!\n");
        return NULL;
    }

    /* Check for DMA adapter object */
    if (DeviceExtension->AdapterObject == NULL)
    {
        /* Initialize DMA adapter description */
        RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));

        DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
        DeviceDescription.Master = ConfigInfo->Master;
        DeviceDescription.ScatterGather = ConfigInfo->ScatterGather;
        DeviceDescription.DemandMode = ConfigInfo->DemandMode;
        DeviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses;
        DeviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber;
        DeviceDescription.DmaChannel = ConfigInfo->DmaChannel;
        DeviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType;
        DeviceDescription.DmaWidth = ConfigInfo->DmaWidth;
        DeviceDescription.DmaSpeed = ConfigInfo->DmaSpeed;
        DeviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength;
        DeviceDescription.DmaPort = ConfigInfo->DmaPort;

        /* Get a DMA adapter object */
        DeviceExtension->AdapterObject =
            HalGetAdapter(&DeviceDescription, &MapRegistersCount);

        /* Fail in case of error */
        if (DeviceExtension->AdapterObject == NULL)
        {
            DPRINT1("HalGetAdapter() failed\n");
            return NULL;
        }

        /* Set number of physical breaks */
        if (ConfigInfo->NumberOfPhysicalBreaks != 0 &&
            MapRegistersCount > ConfigInfo->NumberOfPhysicalBreaks)
        {
            DeviceExtension->PortCapabilities.MaximumPhysicalPages =
                ConfigInfo->NumberOfPhysicalBreaks;
        }
        else
        {
            DeviceExtension->PortCapabilities.MaximumPhysicalPages = MapRegistersCount;
        }
    }

    /* Update auto request sense feature */
    DeviceExtension->SupportsAutoSense = ConfigInfo->AutoRequestSense;

    /* Update Srb extension size */
    if (DeviceExtension->SrbExtensionSize != ConfigInfo->SrbExtensionSize)
        DeviceExtension->SrbExtensionSize = ConfigInfo->SrbExtensionSize;

    /* Update Srb extension alloc flag */
    if (ConfigInfo->AutoRequestSense || DeviceExtension->SrbExtensionSize)
        DeviceExtension->NeedSrbExtensionAlloc = TRUE;

    /* Allocate a common DMA buffer */
    Status = SpiAllocateCommonBuffer(DeviceExtension, NumberOfBytes);

    if (!NT_SUCCESS(Status))
    {
        DPRINT1("SpiAllocateCommonBuffer() failed with Status = 0x%08X!\n", Status);
        return NULL;
    }

    return DeviceExtension->NonCachedExtension;
}

static NTSTATUS
SpiAllocateCommonBuffer(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ULONG NonCachedSize)
{
    PVOID *SrbExtension, CommonBuffer;
    ULONG CommonBufferLength, BufSize;

    /* If size is 0, set it to 16 */
    if (!DeviceExtension->SrbExtensionSize)
        DeviceExtension->SrbExtensionSize = 16;

    /* Calculate size */
    BufSize = DeviceExtension->SrbExtensionSize;

    /* Add autosense data size if needed */
    if (DeviceExtension->SupportsAutoSense)
        BufSize += sizeof(SENSE_DATA);


    /* Round it */
    BufSize = (BufSize + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1);

    /* Sum up into the total common buffer length, and round it to page size */
    CommonBufferLength =
        ROUND_TO_PAGES(NonCachedSize + BufSize * DeviceExtension->RequestsNumber);

    /* Allocate it */
    if (!DeviceExtension->AdapterObject)
    {
        /* From nonpaged pool if there is no DMA */
        CommonBuffer = ExAllocatePoolWithTag(NonPagedPool, CommonBufferLength, TAG_SCSIPORT);
    }
    else
    {
        /* Perform a full request since we have a DMA adapter*/
        CommonBuffer = HalAllocateCommonBuffer(DeviceExtension->AdapterObject,
            CommonBufferLength,
            &DeviceExtension->PhysicalAddress,
            FALSE );
    }

    /* Fail in case of error */
    if (!CommonBuffer)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Zero it */
    RtlZeroMemory(CommonBuffer, CommonBufferLength);

    /* Store its size in Device Extension */
    DeviceExtension->CommonBufferLength = CommonBufferLength;

    /* SrbExtension buffer is located at the beginning of the buffer */
    DeviceExtension->SrbExtensionBuffer = CommonBuffer;

    /* Non-cached extension buffer is located at the end of
       the common buffer */
    if (NonCachedSize)
    {
        CommonBufferLength -=  NonCachedSize;
        DeviceExtension->NonCachedExtension = (PUCHAR)CommonBuffer + CommonBufferLength;
    }
    else
    {
        DeviceExtension->NonCachedExtension = NULL;
    }

    if (DeviceExtension->NeedSrbExtensionAlloc)
    {
        /* Look up how many SRB data structures we need */
        DeviceExtension->SrbDataCount = CommonBufferLength / BufSize;

        /* Initialize the free SRB extensions list */
        SrbExtension = (PVOID *)CommonBuffer;
        DeviceExtension->FreeSrbExtensions = SrbExtension;

        /* Fill the remainding pointers (if we have more than 1 SRB) */
        while (CommonBufferLength >= 2 * BufSize)
        {
            *SrbExtension = (PVOID*)((PCHAR)SrbExtension + BufSize);
            SrbExtension = *SrbExtension;

            CommonBufferLength -= BufSize;
        }
    }

    return STATUS_SUCCESS;
}



/*
 * @implemented
 */
PVOID NTAPI
ScsiPortGetVirtualAddress(IN PVOID HwDeviceExtension,
                          IN SCSI_PHYSICAL_ADDRESS PhysicalAddress)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    ULONG Offset;

    DPRINT("ScsiPortGetVirtualAddress(%p %I64x)\n",
        HwDeviceExtension, PhysicalAddress.QuadPart);

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
                                        SCSI_PORT_DEVICE_EXTENSION,
                                        MiniPortDeviceExtension);

    if (DeviceExtension->PhysicalAddress.QuadPart > PhysicalAddress.QuadPart)
        return NULL;

    Offset = (ULONG)(PhysicalAddress.QuadPart - DeviceExtension->PhysicalAddress.QuadPart);

    if (Offset >= DeviceExtension->CommonBufferLength)
        return NULL;

    return (PVOID)((ULONG_PTR)DeviceExtension->SrbExtensionBuffer + Offset);
}

static VOID
SpiInitOpenKeys(PCONFIGURATION_INFO ConfigInfo, PUNICODE_STRING RegistryPath)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING KeyName;
    NTSTATUS Status;

    /* Open the service key */
    InitializeObjectAttributes(&ObjectAttributes,
                               RegistryPath,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

    Status = ZwOpenKey(&ConfigInfo->ServiceKey,
                       KEY_READ,
                       &ObjectAttributes);

    if (!NT_SUCCESS(Status))
    {
        DPRINT("Unable to open driver's registry key %wZ, status 0x%08x\n", RegistryPath, Status);
        ConfigInfo->ServiceKey = NULL;
    }

    /* If we could open driver's service key, then proceed to the Parameters key */
    if (ConfigInfo->ServiceKey != NULL)
    {
        RtlInitUnicodeString(&KeyName, L"Parameters");
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyName,
                                   OBJ_CASE_INSENSITIVE,
                                   ConfigInfo->ServiceKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        /* Try to open it */
        Status = ZwOpenKey(&ConfigInfo->DeviceKey,
                           KEY_READ,
                           &ObjectAttributes);

        if (NT_SUCCESS(Status))
        {
            /* Yes, Parameters key exist, and it must be used instead of
               the Service key */
            ZwClose(ConfigInfo->ServiceKey);
            ConfigInfo->ServiceKey = ConfigInfo->DeviceKey;
            ConfigInfo->DeviceKey = NULL;
        }
    }

    if (ConfigInfo->ServiceKey != NULL)
    {
        /* Open the Device key */
        RtlInitUnicodeString(&KeyName, L"Device");
        InitializeObjectAttributes(&ObjectAttributes,
                                   &KeyName,
                                   OBJ_CASE_INSENSITIVE,
                                   ConfigInfo->ServiceKey,
                                   NULL);

        /* We don't check for failure here - not needed */
        ZwOpenKey(&ConfigInfo->DeviceKey,
                  KEY_READ,
                  &ObjectAttributes);
    }
}


/**********************************************************************
 * NAME							EXPORTED
 *	ScsiPortInitialize
 *
 * DESCRIPTION
 *	Initializes SCSI port driver specific data.
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	Argument1
 *		Pointer to the miniport driver's driver object.
 *
 *	Argument2
 *		Pointer to the miniport driver's registry path.
 *
 *	HwInitializationData
 *		Pointer to port driver specific configuration data.
 *
 *	HwContext
  		Miniport driver specific context.
 *
 * RETURN VALUE
 * 	Status.
 *
 * @implemented
 */

ULONG NTAPI
ScsiPortInitialize(IN PVOID Argument1,
		   IN PVOID Argument2,
		   IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
		   IN PVOID HwContext)
{
    PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Argument1;
    PUNICODE_STRING RegistryPath = (PUNICODE_STRING)Argument2;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = NULL;
    PCONFIGURATION_INFORMATION SystemConfig;
    PPORT_CONFIGURATION_INFORMATION PortConfig;
    PORT_CONFIGURATION_INFORMATION InitialPortConfig;
    CONFIGURATION_INFO ConfigInfo;
    ULONG DeviceExtensionSize;
    ULONG PortConfigSize;
    BOOLEAN Again;
    BOOLEAN DeviceFound = FALSE;
    BOOLEAN FirstConfigCall = TRUE;
    ULONG Result;
    NTSTATUS Status;
    ULONG MaxBus;
    PCI_SLOT_NUMBER SlotNumber;

    PDEVICE_OBJECT PortDeviceObject;
    WCHAR NameBuffer[80];
    UNICODE_STRING DeviceName;
    WCHAR DosNameBuffer[80];
    UNICODE_STRING DosDeviceName;
    PIO_SCSI_CAPABILITIES PortCapabilities;

    KIRQL OldIrql;
    PCM_RESOURCE_LIST ResourceList;
    BOOLEAN Conflict;
    SIZE_T BusConfigSize;


    DPRINT ("ScsiPortInitialize() called!\n");

    /* Check params for validity */
    if ((HwInitializationData->HwInitialize == NULL) ||
        (HwInitializationData->HwStartIo == NULL) ||
        (HwInitializationData->HwInterrupt == NULL) ||
        (HwInitializationData->HwFindAdapter == NULL) ||
        (HwInitializationData->HwResetBus == NULL))
    {
        return STATUS_INVALID_PARAMETER;
    }

    /* Set handlers */
    DriverObject->DriverStartIo = ScsiPortStartIo;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortDispatchScsi;

    /* Obtain configuration information */
    SystemConfig = IoGetConfigurationInformation();

    /* Zero the internal configuration info structure */
    RtlZeroMemory(&ConfigInfo, sizeof(CONFIGURATION_INFO));

    /* Zero starting slot number */
    SlotNumber.u.AsULONG = 0;

    /* Allocate space for access ranges */
    if (HwInitializationData->NumberOfAccessRanges)
    {
        ConfigInfo.AccessRanges =
            ExAllocatePoolWithTag(PagedPool,
            HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE), TAG_SCSIPORT);

        /* Fail if failed */
        if (ConfigInfo.AccessRanges == NULL)
            return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* Open registry keys */
    SpiInitOpenKeys(&ConfigInfo, (PUNICODE_STRING)Argument2);

    /* Last adapter number = not known */
    ConfigInfo.LastAdapterNumber = SP_UNINITIALIZED_VALUE;

    /* Calculate sizes of DeviceExtension and PortConfig */
    DeviceExtensionSize = sizeof(SCSI_PORT_DEVICE_EXTENSION) +
        HwInitializationData->DeviceExtensionSize;

    MaxBus = (HwInitializationData->AdapterInterfaceType == PCIBus) ? 8 : 1;
    DPRINT("MaxBus: %lu\n", MaxBus);

    while (TRUE)
    {
        /* Create a unicode device name */
        swprintf(NameBuffer,
                 L"\\Device\\ScsiPort%lu",
                 SystemConfig->ScsiPortCount);
        RtlInitUnicodeString(&DeviceName, NameBuffer);

        DPRINT("Creating device: %wZ\n", &DeviceName);

        /* Create the port device */
        Status = IoCreateDevice(DriverObject,
                                DeviceExtensionSize,
                                &DeviceName,
                                FILE_DEVICE_CONTROLLER,
                                0,
                                FALSE,
                                &PortDeviceObject);

        if (!NT_SUCCESS(Status))
        {
            DPRINT1("IoCreateDevice call failed! (Status 0x%lX)\n", Status);
            PortDeviceObject = NULL;
            break;
        }

        DPRINT ("Created device: %wZ (%p)\n", &DeviceName, PortDeviceObject);

        /* Set the buffering strategy here... */
        PortDeviceObject->Flags |= DO_DIRECT_IO;
        PortDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; /* FIXME: Is this really needed? */

        /* Fill Device Extension */
        DeviceExtension = PortDeviceObject->DeviceExtension;
        RtlZeroMemory(DeviceExtension, DeviceExtensionSize);
        DeviceExtension->Length = DeviceExtensionSize;
        DeviceExtension->DeviceObject = PortDeviceObject;
        DeviceExtension->PortNumber = SystemConfig->ScsiPortCount;

        /* Driver's routines... */
        DeviceExtension->HwInitialize = HwInitializationData->HwInitialize;
        DeviceExtension->HwStartIo = HwInitializationData->HwStartIo;
        DeviceExtension->HwInterrupt = HwInitializationData->HwInterrupt;
        DeviceExtension->HwResetBus = HwInitializationData->HwResetBus;
        DeviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted;

        /* Extensions sizes */
        DeviceExtension->MiniPortExtensionSize = HwInitializationData->DeviceExtensionSize;
        DeviceExtension->LunExtensionSize = HwInitializationData->SpecificLuExtensionSize;
        DeviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize;

        /* Round Srb extension size to the quadword */
        DeviceExtension->SrbExtensionSize =
            ~(sizeof(LONGLONG) - 1) & (DeviceExtension->SrbExtensionSize +
            sizeof(LONGLONG) - 1);

        /* Fill some numbers (bus count, lun count, etc) */
        DeviceExtension->MaxLunCount = SCSI_MAXIMUM_LOGICAL_UNITS;
        DeviceExtension->RequestsNumber = 16;

        /* Initialize the spin lock in the controller extension */
        KeInitializeSpinLock(&DeviceExtension->IrqLock);
        KeInitializeSpinLock(&DeviceExtension->SpinLock);

        /* Initialize the DPC object */
        IoInitializeDpcRequest(PortDeviceObject,
                               ScsiPortDpcForIsr);

        /* Initialize the device timer */
        DeviceExtension->TimerCount = -1;
        IoInitializeTimer(PortDeviceObject,
                          ScsiPortIoTimer,
                          DeviceExtension);

        /* Initialize miniport timer */
        KeInitializeTimer(&DeviceExtension->MiniportTimer);
        KeInitializeDpc(&DeviceExtension->MiniportTimerDpc,
                        SpiMiniportTimerDpc,
                        PortDeviceObject);

CreatePortConfig:

        Status = SpiCreatePortConfig(DeviceExtension,
                                     HwInitializationData,
                                     &ConfigInfo,
                                     &InitialPortConfig,
                                     FirstConfigCall);

        if (!NT_SUCCESS(Status))
        {
            DPRINT("SpiCreatePortConfig() failed with Status 0x%08X\n", Status);
            break;
        }

        /* Allocate and initialize port configuration info */
        PortConfigSize = (sizeof(PORT_CONFIGURATION_INFORMATION) +
                          HwInitializationData->NumberOfAccessRanges *
                          sizeof(ACCESS_RANGE) + 7) & ~7;
        DeviceExtension->PortConfig = ExAllocatePoolWithTag(NonPagedPool, PortConfigSize, TAG_SCSIPORT);

        /* Fail if failed */
        if (DeviceExtension->PortConfig == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        PortConfig = DeviceExtension->PortConfig;

        /* Copy information here */
        RtlCopyMemory(PortConfig,
                      &InitialPortConfig,
                      sizeof(PORT_CONFIGURATION_INFORMATION));


        /* Copy extension sizes into the PortConfig */
        PortConfig->SpecificLuExtensionSize = DeviceExtension->LunExtensionSize;
        PortConfig->SrbExtensionSize = DeviceExtension->SrbExtensionSize;

        /* Initialize Access ranges */
        if (HwInitializationData->NumberOfAccessRanges != 0)
        {
            PortConfig->AccessRanges = (PVOID)(PortConfig+1);

            /* Align to LONGLONG */
            PortConfig->AccessRanges = (PVOID)((ULONG_PTR)(PortConfig->AccessRanges) + 7);
            PortConfig->AccessRanges = (PVOID)((ULONG_PTR)(PortConfig->AccessRanges) & ~7);

            /* Copy the data */
            RtlCopyMemory(PortConfig->AccessRanges,
                          ConfigInfo.AccessRanges,
                          HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE));
        }

      /* Search for matching PCI device */
      if ((HwInitializationData->AdapterInterfaceType == PCIBus) &&
          (HwInitializationData->VendorIdLength > 0) &&
          (HwInitializationData->VendorId != NULL) &&
          (HwInitializationData->DeviceIdLength > 0) &&
          (HwInitializationData->DeviceId != NULL))
      {
          PortConfig->BusInterruptLevel = 0;

          /* Get PCI device data */
          DPRINT("VendorId '%.*s'  DeviceId '%.*s'\n",
                 HwInitializationData->VendorIdLength,
                 HwInitializationData->VendorId,
                 HwInitializationData->DeviceIdLength,
                 HwInitializationData->DeviceId);

          if (!SpiGetPciConfigData(DriverObject,
                                   PortDeviceObject,
                                   HwInitializationData,
                                   PortConfig,
                                   RegistryPath,
                                   ConfigInfo.BusNumber,
                                   &SlotNumber))
          {
              /* Continue to the next bus, nothing here */
              ConfigInfo.BusNumber++;
              DeviceExtension->PortConfig = NULL;
              ExFreePool(PortConfig);
              Again = FALSE;
              goto CreatePortConfig;
          }

          if (!PortConfig->BusInterruptLevel)
          {
              /* Bypass this slot, because no interrupt was assigned */
              DeviceExtension->PortConfig = NULL;
              ExFreePool(PortConfig);
              goto CreatePortConfig;
          }
      }
      else
      {
          DPRINT("Non-pci bus\n");
      }

      /* Note: HwFindAdapter is called once for each bus */
      Again = FALSE;
      DPRINT("Calling HwFindAdapter() for Bus %lu\n", PortConfig->SystemIoBusNumber);
      Result = (HwInitializationData->HwFindAdapter)(&DeviceExtension->MiniPortDeviceExtension,
                                                     HwContext,
                                                     0,  /* BusInformation */
                                                     ConfigInfo.Parameter, /* ArgumentString */
                                                     PortConfig,
                                                     &Again);

      DPRINT("HwFindAdapter() Result: %lu  Again: %s\n",
             Result, (Again) ? "True" : "False");

      /* Free MapRegisterBase, it's not needed anymore */
      if (DeviceExtension->MapRegisterBase != NULL)
      {
          ExFreePool(DeviceExtension->MapRegisterBase);
          DeviceExtension->MapRegisterBase = NULL;
      }

      /* If result is nothing good... */
      if (Result != SP_RETURN_FOUND)
      {
          DPRINT("HwFindAdapter() Result: %lu\n", Result);

          if (Result == SP_RETURN_NOT_FOUND)
          {
              /* We can continue on the next bus */
              ConfigInfo.BusNumber++;
              Again = FALSE;

              DeviceExtension->PortConfig = NULL;
              ExFreePool(PortConfig);
              goto CreatePortConfig;
          }

          /* Otherwise, break */
          Status = STATUS_INTERNAL_ERROR;
          break;
      }

      DPRINT("ScsiPortInitialize(): Found HBA! (%x), adapter Id %d\n",
          PortConfig->BusInterruptVector, PortConfig->InitiatorBusId[0]);

      /* If the SRB extension size was updated */
      if (!DeviceExtension->NonCachedExtension &&
          (PortConfig->SrbExtensionSize != DeviceExtension->SrbExtensionSize))
      {
          /* Set it (rounding to LONGLONG again) */
          DeviceExtension->SrbExtensionSize =
              (PortConfig->SrbExtensionSize +
               sizeof(LONGLONG)) & ~(sizeof(LONGLONG) - 1);
      }

      /* The same with LUN extension size */
      if (PortConfig->SpecificLuExtensionSize != DeviceExtension->LunExtensionSize)
          DeviceExtension->LunExtensionSize = PortConfig->SpecificLuExtensionSize;


      if (!((HwInitializationData->AdapterInterfaceType == PCIBus) &&
          (HwInitializationData->VendorIdLength > 0) &&
          (HwInitializationData->VendorId != NULL) &&
          (HwInitializationData->DeviceIdLength > 0) &&
          (HwInitializationData->DeviceId != NULL)))
      {
          /* Construct a resource list */
          ResourceList = SpiConfigToResource(DeviceExtension,
                                             PortConfig);

          if (ResourceList)
          {
              UNICODE_STRING UnicodeString;
              RtlInitUnicodeString(&UnicodeString, L"ScsiAdapter");
              DPRINT("Reporting resources\n");
              Status = IoReportResourceUsage(&UnicodeString,
                                             DriverObject,
                                             NULL,
                                             0,
                                             PortDeviceObject,
                                             ResourceList,
                                             FIELD_OFFSET(CM_RESOURCE_LIST,
                                                 List[0].PartialResourceList.PartialDescriptors) +
                                                 ResourceList->List[0].PartialResourceList.Count
                                                 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR),
                                             FALSE,
                                             &Conflict);
              ExFreePool(ResourceList);

              /* In case of a failure or a conflict, break */
              if (Conflict || (!NT_SUCCESS(Status)))
              {
                  if (Conflict)
                      Status = STATUS_CONFLICTING_ADDRESSES;
                  break;
              }
            }
      }

      /* Reset the Conflict var */
      Conflict = FALSE;

      /* Copy all stuff which we ever need from PortConfig to the DeviceExtension */
      if (PortConfig->MaximumNumberOfTargets > SCSI_MAXIMUM_TARGETS_PER_BUS)
          DeviceExtension->MaxTargedIds = SCSI_MAXIMUM_TARGETS_PER_BUS;
      else
          DeviceExtension->MaxTargedIds = PortConfig->MaximumNumberOfTargets;

      DeviceExtension->BusNum = PortConfig->NumberOfBuses;
      DeviceExtension->CachesData = PortConfig->CachesData;
      DeviceExtension->ReceiveEvent = PortConfig->ReceiveEvent;
      DeviceExtension->SupportsTaggedQueuing = PortConfig->TaggedQueuing;
      DeviceExtension->MultipleReqsPerLun = PortConfig->MultipleRequestPerLu;

      /* If something was disabled via registry - apply it */
      if (ConfigInfo.DisableMultipleLun)
          DeviceExtension->MultipleReqsPerLun = PortConfig->MultipleRequestPerLu = FALSE;

      if (ConfigInfo.DisableTaggedQueueing)
          DeviceExtension->SupportsTaggedQueuing = PortConfig->MultipleRequestPerLu = FALSE;

      /* Check if we need to alloc SRB data */
      if (DeviceExtension->SupportsTaggedQueuing ||
          DeviceExtension->MultipleReqsPerLun)
      {
          DeviceExtension->NeedSrbDataAlloc = TRUE;
      }
      else
      {
          DeviceExtension->NeedSrbDataAlloc = FALSE;
      }

      /* Get a pointer to the port capabilities */
      PortCapabilities = &DeviceExtension->PortCapabilities;

      /* Copy one field there */
      DeviceExtension->MapBuffers = PortConfig->MapBuffers;
      PortCapabilities->AdapterUsesPio = PortConfig->MapBuffers;

      if (DeviceExtension->AdapterObject == NULL &&
          (PortConfig->DmaChannel != SP_UNINITIALIZED_VALUE || PortConfig->Master))
      {
          DPRINT1("DMA is not supported yet\n");
          ASSERT(FALSE);
      }

      if (DeviceExtension->SrbExtensionBuffer == NULL &&
          (DeviceExtension->SrbExtensionSize != 0  ||
          PortConfig->AutoRequestSense))
      {
          DeviceExtension->SupportsAutoSense = PortConfig->AutoRequestSense;
          DeviceExtension->NeedSrbExtensionAlloc = TRUE;

          /* Allocate common buffer */
          Status = SpiAllocateCommonBuffer(DeviceExtension, 0);

          /* Check for failure */
          if (!NT_SUCCESS(Status))
              break;
      }

      /* Allocate SrbData, if needed */
      if (DeviceExtension->NeedSrbDataAlloc)
      {
          ULONG Count;
          PSCSI_REQUEST_BLOCK_INFO SrbData;

          if (DeviceExtension->SrbDataCount != 0)
              Count = DeviceExtension->SrbDataCount;
          else
              Count = DeviceExtension->RequestsNumber * 2;

          /* Allocate the data */
          SrbData = ExAllocatePoolWithTag(NonPagedPool, Count * sizeof(SCSI_REQUEST_BLOCK_INFO), TAG_SCSIPORT);
          if (SrbData == NULL)
              return STATUS_INSUFFICIENT_RESOURCES;

          RtlZeroMemory(SrbData, Count * sizeof(SCSI_REQUEST_BLOCK_INFO));

          DeviceExtension->SrbInfo = SrbData;
          DeviceExtension->FreeSrbInfo = SrbData;
          DeviceExtension->SrbDataCount = Count;

          /* Link it to the list */
          while (Count > 0)
          {
              SrbData->Requests.Flink = (PLIST_ENTRY)(SrbData + 1);
              SrbData++;
              Count--;
          }

          /* Mark the last entry of the list */
          SrbData--;
          SrbData->Requests.Flink = NULL;
      }

      /* Initialize port capabilities */
      PortCapabilities = &DeviceExtension->PortCapabilities;
      PortCapabilities->Length = sizeof(IO_SCSI_CAPABILITIES);
      PortCapabilities->MaximumTransferLength = PortConfig->MaximumTransferLength;

      if (PortConfig->ReceiveEvent)
          PortCapabilities->SupportedAsynchronousEvents |= SRBEV_SCSI_ASYNC_NOTIFICATION;

      PortCapabilities->TaggedQueuing = DeviceExtension->SupportsTaggedQueuing;
      PortCapabilities->AdapterScansDown = PortConfig->AdapterScansDown;

      if (PortConfig->AlignmentMask > PortDeviceObject->AlignmentRequirement)
          PortDeviceObject->AlignmentRequirement = PortConfig->AlignmentMask;

      PortCapabilities->AlignmentMask = PortDeviceObject->AlignmentRequirement;

      if (PortCapabilities->MaximumPhysicalPages == 0)
      {
          PortCapabilities->MaximumPhysicalPages =
              BYTES_TO_PAGES(PortCapabilities->MaximumTransferLength);

          /* Apply miniport's limits */
          if (PortConfig->NumberOfPhysicalBreaks < PortCapabilities->MaximumPhysicalPages)
          {
              PortCapabilities->MaximumPhysicalPages = PortConfig->NumberOfPhysicalBreaks;
          }
      }

      /* Deal with interrupts */
      if (DeviceExtension->HwInterrupt == NULL ||
          (PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0))
      {
          /* No interrupts */
          DeviceExtension->InterruptCount = 0;

          DPRINT1("Interrupt Count: 0\n");

          UNIMPLEMENTED;

          /* This code path will ALWAYS crash so stop it now */
          while(TRUE);
      }
      else
      {
          BOOLEAN InterruptShareable;
          KINTERRUPT_MODE InterruptMode[2];
          ULONG InterruptVector[2], i, MappedIrq[2];
          KIRQL Dirql[2], MaxDirql;
          KAFFINITY Affinity[2];

          DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel;
          DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2;

          InterruptVector[0] = PortConfig->BusInterruptVector;
          InterruptVector[1] = PortConfig->BusInterruptVector2;

          InterruptMode[0] = PortConfig->InterruptMode;
          InterruptMode[1] = PortConfig->InterruptMode2;

          DeviceExtension->InterruptCount = (PortConfig->BusInterruptLevel2 != 0 || PortConfig->BusInterruptVector2 != 0) ? 2 : 1;

          for (i = 0; i < DeviceExtension->InterruptCount; i++)
          {
              /* Register an interrupt handler for this device */
              MappedIrq[i] = HalGetInterruptVector(PortConfig->AdapterInterfaceType,
                                                   PortConfig->SystemIoBusNumber,
                                                   DeviceExtension->InterruptLevel[i],
                                                   InterruptVector[i],
                                                   &Dirql[i],
                                                   &Affinity[i]);
          }

          if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1])
              MaxDirql = Dirql[0];
          else
              MaxDirql = Dirql[1];

          for (i = 0; i < DeviceExtension->InterruptCount; i++)
          {
              /* Determing IRQ sharability as usual */
              if (PortConfig->AdapterInterfaceType == MicroChannel ||
                  InterruptMode[i] == LevelSensitive)
              {
                  InterruptShareable = TRUE;
              }
              else
              {
                  InterruptShareable = FALSE;
              }

              Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i],
                                          (PKSERVICE_ROUTINE)ScsiPortIsr,
                                          DeviceExtension,
                                          &DeviceExtension->IrqLock,
                                          MappedIrq[i],
                                          Dirql[i],
                                          MaxDirql,
                                          InterruptMode[i],
                                          InterruptShareable,
                                          Affinity[i],
                                          FALSE);

              if (!(NT_SUCCESS(Status)))
              {
                  DPRINT1("Could not connect interrupt %d\n",
                          InterruptVector[i]);
                  DeviceExtension->Interrupt[i] = NULL;
                  break;
              }
          }

          if (!NT_SUCCESS(Status))
              break;
      }

      /* Save IoAddress (from access ranges) */
      if (HwInitializationData->NumberOfAccessRanges != 0)
      {
          DeviceExtension->IoAddress =
              ((*(PortConfig->AccessRanges))[0]).RangeStart.LowPart;

          DPRINT("Io Address %x\n", DeviceExtension->IoAddress);
      }

      /* Set flag that it's allowed to disconnect during this command */
      DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;

      /* Initialize counter of active requests (-1 means there are none) */
      DeviceExtension->ActiveRequestCounter = -1;

      /* Analyze what we have about DMA */
      if (DeviceExtension->AdapterObject != NULL &&
          PortConfig->Master &&
          PortConfig->NeedPhysicalAddresses)
      {
          DeviceExtension->MapRegisters = TRUE;
      }
      else
      {
          DeviceExtension->MapRegisters = FALSE;
      }

      /* Call HwInitialize at DISPATCH_LEVEL */
      KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);

      if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                                  DeviceExtension->HwInitialize,
                                  DeviceExtension->MiniPortDeviceExtension))
      {
          DPRINT1("HwInitialize() failed!\n");
          KeLowerIrql(OldIrql);
          Status = STATUS_ADAPTER_HARDWARE_ERROR;
          break;
      }

      /* Check if a notification is needed */
      if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
      {
          /* Call DPC right away, because we're already at DISPATCH_LEVEL */
          ScsiPortDpcForIsr(NULL,
                            DeviceExtension->DeviceObject,
                            NULL,
                            NULL);
      }

      /* Lower irql back to what it was */
      KeLowerIrql(OldIrql);

      /* Start our timer */
      IoStartTimer(PortDeviceObject);

      /* Initialize bus scanning information */
      BusConfigSize = FIELD_OFFSET(BUSES_CONFIGURATION_INFORMATION,
                                   BusScanInfo[DeviceExtension->PortConfig->NumberOfBuses]);
      DeviceExtension->BusesConfig = ExAllocatePoolWithTag(PagedPool,
                                                           BusConfigSize,
                                                           TAG_SCSIPORT);
      if (!DeviceExtension->BusesConfig)
      {
          DPRINT1("Out of resources!\n");
          Status = STATUS_INSUFFICIENT_RESOURCES;
          break;
      }

      /* Zero it */
      RtlZeroMemory(DeviceExtension->BusesConfig, BusConfigSize);

      /* Store number of buses there */
      DeviceExtension->BusesConfig->NumberOfBuses = (UCHAR)DeviceExtension->BusNum;

      /* Scan the adapter for devices */
      SpiScanAdapter(DeviceExtension);

      /* Build the registry device map */
      SpiBuildDeviceMap(DeviceExtension,
                       (PUNICODE_STRING)Argument2);

      /* Create the dos device link */
      swprintf(DosNameBuffer,
               L"\\??\\Scsi%lu:",
              SystemConfig->ScsiPortCount);
      RtlInitUnicodeString(&DosDeviceName, DosNameBuffer);
      IoCreateSymbolicLink(&DosDeviceName, &DeviceName);

      /* Increase the port count */
      SystemConfig->ScsiPortCount++;
      FirstConfigCall = FALSE;

      /* Increase adapter number and bus number respectively */
      ConfigInfo.AdapterNumber++;

      if (!Again)
          ConfigInfo.BusNumber++;

      DPRINT("Bus: %lu  MaxBus: %lu\n", ConfigInfo.BusNumber, MaxBus);

      DeviceFound = TRUE;
    }

    /* Clean up the mess */
    SpiCleanupAfterInit(DeviceExtension);

    /* Close registry keys */
    if (ConfigInfo.ServiceKey != NULL)
        ZwClose(ConfigInfo.ServiceKey);

    if (ConfigInfo.DeviceKey != NULL)
        ZwClose(ConfigInfo.DeviceKey);

    if (ConfigInfo.BusKey != NULL)
        ZwClose(ConfigInfo.BusKey);

    if (ConfigInfo.AccessRanges != NULL)
        ExFreePool(ConfigInfo.AccessRanges);

    if (ConfigInfo.Parameter != NULL)
        ExFreePool(ConfigInfo.Parameter);

    DPRINT("ScsiPortInitialize() done, Status = 0x%08X, DeviceFound = %d!\n",
        Status, DeviceFound);

    return (DeviceFound == FALSE) ? Status : STATUS_SUCCESS;
}

static VOID
SpiCleanupAfterInit(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
{
    PSCSI_LUN_INFO LunInfo;
    PVOID Ptr;
    ULONG Bus, Lun;

    /* Check if we have something to clean up */
    if (DeviceExtension == NULL)
        return;

    /* Stop the timer */
    IoStopTimer(DeviceExtension->DeviceObject);

    /* Disconnect the interrupts */
    while (DeviceExtension->InterruptCount)
    {
        if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount])
            IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]);
    }

    /* Delete ConfigInfo */
    if (DeviceExtension->BusesConfig)
    {
        for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++)
        {
            if (!DeviceExtension->BusesConfig->BusScanInfo[Bus])
                continue;

            LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo;

            while (LunInfo)
            {
                /* Free current, but save pointer to the next one */
                Ptr = LunInfo->Next;
                ExFreePool(LunInfo);
                LunInfo = Ptr;
            }

            ExFreePool(DeviceExtension->BusesConfig->BusScanInfo[Bus]);
        }

        ExFreePool(DeviceExtension->BusesConfig);
    }

    /* Free PortConfig */
    if (DeviceExtension->PortConfig)
        ExFreePool(DeviceExtension->PortConfig);

    /* Free LUNs*/
    for(Lun = 0; Lun < LUS_NUMBER; Lun++)
    {
        while (DeviceExtension->LunExtensionList[Lun])
        {
            Ptr = DeviceExtension->LunExtensionList[Lun];
            DeviceExtension->LunExtensionList[Lun] = DeviceExtension->LunExtensionList[Lun]->Next;

            ExFreePool(Ptr);
        }
    }

    /* Free common buffer (if it exists) */
    if (DeviceExtension->SrbExtensionBuffer != NULL &&
        DeviceExtension->CommonBufferLength != 0)
    {
            if (!DeviceExtension->AdapterObject)
            {
                ExFreePool(DeviceExtension->SrbExtensionBuffer);
            }
            else
            {
                HalFreeCommonBuffer(DeviceExtension->AdapterObject,
                                    DeviceExtension->CommonBufferLength,
                                    DeviceExtension->PhysicalAddress,
                                    DeviceExtension->SrbExtensionBuffer,
                                    FALSE);
            }
    }

    /* Free SRB info */
    if (DeviceExtension->SrbInfo != NULL)
        ExFreePool(DeviceExtension->SrbInfo);

    /* Unmap mapped addresses */
    while (DeviceExtension->MappedAddressList != NULL)
    {
        MmUnmapIoSpace(DeviceExtension->MappedAddressList->MappedAddress,
                       DeviceExtension->MappedAddressList->NumberOfBytes);

        Ptr = DeviceExtension->MappedAddressList;
        DeviceExtension->MappedAddressList = DeviceExtension->MappedAddressList->NextMappedAddress;

        ExFreePool(Ptr);
    }

    /* Finally delete the device object */
    DPRINT("Deleting device %p\n", DeviceExtension->DeviceObject);
    IoDeleteDevice(DeviceExtension->DeviceObject);
}

/*
 * @unimplemented
 */
VOID NTAPI
ScsiPortIoMapTransfer(IN PVOID HwDeviceExtension,
		      IN PSCSI_REQUEST_BLOCK Srb,
		      IN PVOID LogicalAddress,
		      IN ULONG Length)
{
  DPRINT1("ScsiPortIoMapTransfer()\n");
  UNIMPLEMENTED;
}

/*
 * @unimplemented
 */
VOID NTAPI
ScsiPortLogError(IN PVOID HwDeviceExtension,
		 IN PSCSI_REQUEST_BLOCK Srb OPTIONAL,
		 IN UCHAR PathId,
		 IN UCHAR TargetId,
		 IN UCHAR Lun,
		 IN ULONG ErrorCode,
		 IN ULONG UniqueId)
{
  //PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;

  DPRINT1("ScsiPortLogError() called\n");
  DPRINT1("PathId: 0x%02x  TargetId: 0x%02x  Lun: 0x%02x  ErrorCode: 0x%08lx  UniqueId: 0x%08lx\n",
          PathId, TargetId, Lun, ErrorCode, UniqueId);

  //DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension);


  DPRINT("ScsiPortLogError() done\n");
}

/*
 * @implemented
 */
VOID NTAPI
ScsiPortMoveMemory(OUT PVOID Destination,
		   IN PVOID Source,
		   IN ULONG Length)
{
  RtlMoveMemory(Destination,
		Source,
		Length);
}


/*
 * @implemented
 */
VOID
ScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType,
		     IN PVOID HwDeviceExtension,
		     ...)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    va_list ap;

    DPRINT("ScsiPortNotification() called\n");

    DeviceExtension = CONTAINING_RECORD(HwDeviceExtension,
        SCSI_PORT_DEVICE_EXTENSION,
        MiniPortDeviceExtension);

    DPRINT("DeviceExtension %p\n", DeviceExtension);

    va_start(ap, HwDeviceExtension);

    switch (NotificationType)
    {
    case RequestComplete:
        {
            PSCSI_REQUEST_BLOCK Srb;
            PSCSI_REQUEST_BLOCK_INFO SrbData;

            Srb = (PSCSI_REQUEST_BLOCK) va_arg (ap, PSCSI_REQUEST_BLOCK);

            DPRINT("Notify: RequestComplete (Srb %p)\n", Srb);

            /* Make sure Srb is allright */
            ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING);
            ASSERT(Srb->Function != SRB_FUNCTION_EXECUTE_SCSI || Srb->SrbStatus != SRB_STATUS_SUCCESS || Srb->ScsiStatus == SCSISTAT_GOOD);

            if (!(Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE))
            {
                /* It's been already completed */
                va_end(ap);
                return;
            }

            /* It's not active anymore */
            Srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE;

            if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
            {
                /* TODO: Treat it specially */
                ASSERT(FALSE);
            }
            else
            {
                /* Get the SRB data */
                SrbData = SpiGetSrbData(DeviceExtension,
                                        Srb->PathId,
                                        Srb->TargetId,
                                        Srb->Lun,
                                        Srb->QueueTag);

                /* Make sure there are no CompletedRequests and there is a Srb */
                ASSERT(SrbData->CompletedRequests == NULL && SrbData->Srb != NULL);

                /* If it's a read/write request, make sure it has data inside it */
                if ((Srb->SrbStatus == SRB_STATUS_SUCCESS) &&
                    ((Srb->Cdb[0] == SCSIOP_READ) || (Srb->Cdb[0] == SCSIOP_WRITE)))
                {
                        ASSERT(Srb->DataTransferLength);
                }

                SrbData->CompletedRequests = DeviceExtension->InterruptData.CompletedRequests;
                DeviceExtension->InterruptData.CompletedRequests = SrbData;
            }
        }
        break;

    case NextRequest:
        DPRINT("Notify: NextRequest\n");
        DeviceExtension->InterruptData.Flags |= SCSI_PORT_NEXT_REQUEST_READY;
        break;

    case NextLuRequest:
        {
            UCHAR PathId;
            UCHAR TargetId;
            UCHAR Lun;
            PSCSI_PORT_LUN_EXTENSION LunExtension;

            PathId = (UCHAR) va_arg (ap, int);
            TargetId = (UCHAR) va_arg (ap, int);
            Lun = (UCHAR) va_arg (ap, int);

            DPRINT("Notify: NextLuRequest(PathId %u  TargetId %u  Lun %u)\n",
                PathId, TargetId, Lun);

            /* Mark it in the flags field */
            DeviceExtension->InterruptData.Flags |= SCSI_PORT_NEXT_REQUEST_READY;

            /* Get the LUN extension */
            LunExtension = SpiGetLunExtension(DeviceExtension,
                                              PathId,
                                              TargetId,
                                              Lun);

            /* If returned LunExtension is NULL, break out */
            if (!LunExtension) break;

            /* This request should not be processed if */
            if ((LunExtension->ReadyLun) ||
                (LunExtension->SrbInfo.Srb))
            {
                /* Nothing to do here */
                break;
            }

            /* Add this LUN to the list */
            LunExtension->ReadyLun = DeviceExtension->InterruptData.ReadyLun;
            DeviceExtension->InterruptData.ReadyLun = LunExtension;
          }
          break;

      case ResetDetected:
          DPRINT("Notify: ResetDetected\n");
          /* Add RESET flags */
          DeviceExtension->InterruptData.Flags |=
                SCSI_PORT_RESET | SCSI_PORT_RESET_REPORTED;
          break;

      case CallDisableInterrupts:
          DPRINT1("UNIMPLEMENTED SCSI Notification called: CallDisableInterrupts!\n");
          break;

      case CallEnableInterrupts:
          DPRINT1("UNIMPLEMENTED SCSI Notification called: CallEnableInterrupts!\n");
          break;

      case RequestTimerCall:
          DPRINT("Notify: RequestTimerCall\n");
          DeviceExtension->InterruptData.Flags |= SCSI_PORT_TIMER_NEEDED;
          DeviceExtension->InterruptData.HwScsiTimer = (PHW_TIMER)va_arg(ap, PHW_TIMER);
          DeviceExtension->InterruptData.MiniportTimerValue = (ULONG)va_arg(ap, ULONG);
          break;

      case BusChangeDetected:
          DPRINT1("UNIMPLEMENTED SCSI Notification called: BusChangeDetected!\n");
          break;

      default:
	DPRINT1 ("Unsupported notification from WMI: %lu\n", NotificationType);
	break;
    }

    va_end(ap);

    /* Request a DPC after we're done with the interrupt */
    DeviceExtension->InterruptData.Flags |= SCSI_PORT_NOTIFICATION_NEEDED;
}

/*
 * @implemented
 */
BOOLEAN NTAPI
ScsiPortValidateRange(IN PVOID HwDeviceExtension,
		      IN INTERFACE_TYPE BusType,
		      IN ULONG SystemIoBusNumber,
		      IN SCSI_PHYSICAL_ADDRESS IoAddress,
		      IN ULONG NumberOfBytes,
		      IN BOOLEAN InIoSpace)
{
  DPRINT("ScsiPortValidateRange()\n");
  return(TRUE);
}


/* INTERNAL FUNCTIONS ********************************************************/

static VOID
SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData,
                    IN PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor,
                    IN PPORT_CONFIGURATION_INFORMATION PortConfig)
{
    PACCESS_RANGE AccessRange;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialData;
    ULONG RangeNumber;
    ULONG Index;
    ULONG Interrupt = 0;
    ULONG Dma = 0;

    RangeNumber = 0;

    /* Loop through all entries */
    for (Index = 0; Index < ResourceDescriptor->PartialResourceList.Count; Index++)
    {
        PartialData = &ResourceDescriptor->PartialResourceList.PartialDescriptors[Index];

        switch (PartialData->Type)
        {
        case CmResourceTypePort:
            /* Copy access ranges */
            if (RangeNumber < HwInitializationData->NumberOfAccessRanges)
            {
                AccessRange = &((*(PortConfig->AccessRanges))[RangeNumber]);

                AccessRange->RangeStart = PartialData->u.Port.Start;
                AccessRange->RangeLength = PartialData->u.Port.Length;

                AccessRange->RangeInMemory = FALSE;
                RangeNumber++;
            }
            break;

        case CmResourceTypeMemory:
            /* Copy access ranges */
            if (RangeNumber < HwInitializationData->NumberOfAccessRanges)
            {
                AccessRange = &((*(PortConfig->AccessRanges))[RangeNumber]);

                AccessRange->RangeStart = PartialData->u.Memory.Start;
                AccessRange->RangeLength = PartialData->u.Memory.Length;

                AccessRange->RangeInMemory = TRUE;
                RangeNumber++;
            }
            break;

        case CmResourceTypeInterrupt:

            if (Interrupt == 0)
            {
                /* Copy interrupt data */
                PortConfig->BusInterruptLevel = PartialData->u.Interrupt.Level;
                PortConfig->BusInterruptVector = PartialData->u.Interrupt.Vector;

                /* Set interrupt mode accordingly to the resource */
                if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED)
                {
                    PortConfig->InterruptMode = Latched;
                }
                else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE)
                {
                    PortConfig->InterruptMode = LevelSensitive;
                }
            }
            else if (Interrupt == 1)
            {
                /* Copy interrupt data */
                PortConfig->BusInterruptLevel2 = PartialData->u.Interrupt.Level;
                PortConfig->BusInterruptVector2 = PartialData->u.Interrupt.Vector;

                /* Set interrupt mode accordingly to the resource */
                if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED)
                {
                    PortConfig->InterruptMode2 = Latched;
                }
                else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE)
                {
                    PortConfig->InterruptMode2 = LevelSensitive;
                }
            }

            Interrupt++;
            break;

        case CmResourceTypeDma:

            if (Dma == 0)
            {
                PortConfig->DmaChannel = PartialData->u.Dma.Channel;
                PortConfig->DmaPort = PartialData->u.Dma.Port;

                if (PartialData->Flags & CM_RESOURCE_DMA_8)
                    PortConfig->DmaWidth = Width8Bits;
                else if ((PartialData->Flags & CM_RESOURCE_DMA_16) ||
                         (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //???
                    PortConfig->DmaWidth = Width16Bits;
                else if (PartialData->Flags & CM_RESOURCE_DMA_32)
                    PortConfig->DmaWidth = Width32Bits;
            }
            else if (Dma == 1)
            {
                PortConfig->DmaChannel2 = PartialData->u.Dma.Channel;
                PortConfig->DmaPort2 = PartialData->u.Dma.Port;

                if (PartialData->Flags & CM_RESOURCE_DMA_8)
                    PortConfig->DmaWidth2 = Width8Bits;
                else if ((PartialData->Flags & CM_RESOURCE_DMA_16) ||
                         (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //???
                    PortConfig->DmaWidth2 = Width16Bits;
                else if (PartialData->Flags & CM_RESOURCE_DMA_32)
                    PortConfig->DmaWidth2 = Width32Bits;
            }
            break;
        }
    }
}

static PCM_RESOURCE_LIST
SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    PPORT_CONFIGURATION_INFORMATION PortConfig)
{
    PCONFIGURATION_INFORMATION ConfigInfo;
    PCM_RESOURCE_LIST ResourceList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
    PACCESS_RANGE AccessRange;
    ULONG ListLength = 0, i, FullSize;
    ULONG Interrupt, Dma;

    /* Get current Atdisk usage from the system */
    ConfigInfo = IoGetConfigurationInformation();

    if (PortConfig->AtdiskPrimaryClaimed)
        ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE;

    if (PortConfig->AtdiskSecondaryClaimed)
        ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE;

    /* Do we use DMA? */
    if (PortConfig->DmaChannel != SP_UNINITIALIZED_VALUE ||
        PortConfig->DmaPort != SP_UNINITIALIZED_VALUE)
    {
        Dma = 1;

        if (PortConfig->DmaChannel2 != SP_UNINITIALIZED_VALUE ||
            PortConfig->DmaPort2 != SP_UNINITIALIZED_VALUE)
            Dma++;
    }
    else
    {
        Dma = 0;
    }
    ListLength += Dma;

    /* How many interrupts to we have? */
    Interrupt = DeviceExtension->InterruptCount;
    ListLength += Interrupt;

    /* How many access ranges do we use? */
    AccessRange = &((*(PortConfig->AccessRanges))[0]);
    for (i = 0; i < PortConfig->NumberOfAccessRanges; i++)
    {
        if (AccessRange->RangeLength != 0)
            ListLength++;

        AccessRange++;
    }

    /* Allocate the resource list, since we know its size now */
    FullSize = sizeof(CM_RESOURCE_LIST) + (ListLength - 1) *
        sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

    ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool, FullSize, TAG_SCSIPORT);

    if (!ResourceList)
        return NULL;

    /* Zero it */
    RtlZeroMemory(ResourceList, FullSize);

    /* Initialize it */
    ResourceList->Count = 1;
    ResourceList->List[0].InterfaceType = PortConfig->AdapterInterfaceType;
    ResourceList->List[0].BusNumber = PortConfig->SystemIoBusNumber;
    ResourceList->List[0].PartialResourceList.Count = ListLength;
    ResourceDescriptor = ResourceList->List[0].PartialResourceList.PartialDescriptors;

    /* Copy access ranges array over */
    for (i = 0; i < PortConfig->NumberOfAccessRanges; i++)
    {
        AccessRange = &((*(PortConfig->AccessRanges))[i]);

        /* If the range is empty - skip it */
        if (AccessRange->RangeLength == 0)
            continue;

        if (AccessRange->RangeInMemory)
        {
            ResourceDescriptor->Type = CmResourceTypeMemory;
            ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
        }
        else
        {
            ResourceDescriptor->Type = CmResourceTypePort;
            ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
        }

        ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;

        ResourceDescriptor->u.Memory.Start = AccessRange->RangeStart;
        ResourceDescriptor->u.Memory.Length = AccessRange->RangeLength;

        ResourceDescriptor++;
    }

    /* If we use interrupt(s), copy them */
    while (Interrupt)
    {
        ResourceDescriptor->Type = CmResourceTypeInterrupt;

        if (PortConfig->AdapterInterfaceType == MicroChannel ||
            ((Interrupt == 2) ? PortConfig->InterruptMode2 : PortConfig->InterruptMode) == LevelSensitive)
        {
            ResourceDescriptor->ShareDisposition = CmResourceShareShared;
            ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
        }
        else
        {
            ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
            ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
        }

        ResourceDescriptor->u.Interrupt.Level = (Interrupt == 2) ? PortConfig->BusInterruptLevel2 : PortConfig->BusInterruptLevel;
        ResourceDescriptor->u.Interrupt.Vector = (Interrupt == 2) ? PortConfig->BusInterruptVector2 : PortConfig->BusInterruptVector;
        ResourceDescriptor->u.Interrupt.Affinity = 0;

        ResourceDescriptor++;
        Interrupt--;
    }

    /* Copy DMA data */
    while (Dma)
    {
        ResourceDescriptor->Type = CmResourceTypeDma;
        ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
        ResourceDescriptor->u.Dma.Channel = (Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel;
        ResourceDescriptor->u.Dma.Port = (Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort;
        ResourceDescriptor->Flags = 0;

        if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width8Bits)
            ResourceDescriptor->Flags |= CM_RESOURCE_DMA_8;
        else if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width16Bits)
            ResourceDescriptor->Flags |= CM_RESOURCE_DMA_16;
        else
            ResourceDescriptor->Flags |= CM_RESOURCE_DMA_32;

        if (((Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel) == SP_UNINITIALIZED_VALUE)
            ResourceDescriptor->u.Dma.Channel = 0;

        if (((Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort) == SP_UNINITIALIZED_VALUE)
            ResourceDescriptor->u.Dma.Port = 0;

        ResourceDescriptor++;
        Dma--;
    }

    return ResourceList;
}


static BOOLEAN
SpiGetPciConfigData(IN PDRIVER_OBJECT DriverObject,
                    IN PDEVICE_OBJECT DeviceObject,
                    IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
                    IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig,
                    IN PUNICODE_STRING RegistryPath,
                    IN ULONG BusNumber,
                    IN OUT PPCI_SLOT_NUMBER NextSlotNumber)
{
    PCI_COMMON_CONFIG PciConfig;
    PCI_SLOT_NUMBER SlotNumber;
    ULONG DataSize;
    ULONG DeviceNumber;
    ULONG FunctionNumber;
    CHAR VendorIdString[8];
    CHAR DeviceIdString[8];
    UNICODE_STRING UnicodeStr;
    PCM_RESOURCE_LIST ResourceList = NULL;
    NTSTATUS Status;

    DPRINT ("SpiGetPciConfiguration() called\n");

    SlotNumber.u.AsULONG = 0;

    /* Loop through all devices */
    for (DeviceNumber = NextSlotNumber->u.bits.DeviceNumber; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++)
    {
        SlotNumber.u.bits.DeviceNumber = DeviceNumber;

        /* Loop through all functions */
        for (FunctionNumber = NextSlotNumber->u.bits.FunctionNumber; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++)
        {
            SlotNumber.u.bits.FunctionNumber = FunctionNumber;

            /* Get PCI config bytes */
            DataSize = HalGetBusData(PCIConfiguration,
                                     BusNumber,
                                     SlotNumber.u.AsULONG,
                                     &PciConfig,
                                     sizeof(ULONG));

            /* If result of HalGetBusData is 0, then the bus is wrong */
            if (DataSize == 0)
                return FALSE;

            /* Check if result is PCI_INVALID_VENDORID or too small */
            if ((DataSize < sizeof(ULONG)) ||
                (PciConfig.VendorID == PCI_INVALID_VENDORID))
            {
                /* Continue to try the next function */
                continue;
            }

            sprintf (VendorIdString, "%04hx", PciConfig.VendorID);
            sprintf (DeviceIdString, "%04hx", PciConfig.DeviceID);

            if (_strnicmp(VendorIdString, HwInitializationData->VendorId, HwInitializationData->VendorIdLength) ||
                _strnicmp(DeviceIdString, HwInitializationData->DeviceId, HwInitializationData->DeviceIdLength))
            {
                /* It is not our device */
                continue;
            }

            DPRINT("Found device 0x%04hx 0x%04hx at %1lu %2lu %1lu\n",
                   PciConfig.VendorID,
                   PciConfig.DeviceID,
                   BusNumber,
                   SlotNumber.u.bits.DeviceNumber,
                   SlotNumber.u.bits.FunctionNumber);


            RtlInitUnicodeString(&UnicodeStr, L"ScsiAdapter");
            Status = HalAssignSlotResources(RegistryPath,
                                            &UnicodeStr,
                                            DriverObject,
                                            DeviceObject,
                                            PCIBus,
                                            BusNumber,
                                            SlotNumber.u.AsULONG,
                                            &ResourceList);

            if (!NT_SUCCESS(Status))
                break;

            /* Create configuration information */
            SpiResourceToConfig(HwInitializationData,
                                ResourceList->List,
                                PortConfig);

            /* Free the resource list */
            ExFreePool(ResourceList);

            /* Set dev & fn numbers */
            NextSlotNumber->u.bits.DeviceNumber = DeviceNumber;
            NextSlotNumber->u.bits.FunctionNumber = FunctionNumber + 1;

            /* Save the slot number */
            PortConfig->SlotNumber = SlotNumber.u.AsULONG;

            return TRUE;
        }
       NextSlotNumber->u.bits.FunctionNumber = 0;
    }

    NextSlotNumber->u.bits.DeviceNumber = 0;
    DPRINT ("No device found\n");

    return FALSE;
}



/**********************************************************************
 * NAME							INTERNAL
 *	ScsiPortCreateClose
 *
 * DESCRIPTION
 *	Answer requests for Create/Close calls: a null operation.
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	DeviceObject
 *		Pointer to a device object.
 *
 *	Irp
 *		Pointer to an IRP.
 *
 * RETURN VALUE
 * 	Status.
 */

static NTSTATUS NTAPI
ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject,
                   IN PIRP Irp)
{
    DPRINT("ScsiPortCreateClose()\n");

    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

static NTSTATUS
SpiHandleAttachRelease(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                       PIRP Irp)
{
    PSCSI_LUN_INFO LunInfo;
    PIO_STACK_LOCATION IrpStack;
    PDEVICE_OBJECT DeviceObject;
    PSCSI_REQUEST_BLOCK Srb;
    KIRQL Irql;

    /* Get pointer to the SRB */
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
    Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1;

    /* Check if PathId matches number of buses */
    if (DeviceExtension->BusesConfig == NULL ||
        DeviceExtension->BusesConfig->NumberOfBuses <= Srb->PathId)
    {
        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        return STATUS_DEVICE_DOES_NOT_EXIST;
    }

    /* Get pointer to LunInfo */
    LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Srb->PathId]->LunInfo;

    /* Find matching LunInfo */
    while (LunInfo)
    {
        if (LunInfo->PathId == Srb->PathId &&
            LunInfo->TargetId == Srb->TargetId &&
            LunInfo->Lun == Srb->Lun)
        {
            break;
        }

        LunInfo = LunInfo->Next;
    }

    /* If we couldn't find it - exit */
    if (LunInfo == NULL)
        return STATUS_DEVICE_DOES_NOT_EXIST;


    /* Get spinlock */
    KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);

    /* Release, if asked */
    if (Srb->Function == SRB_FUNCTION_RELEASE_DEVICE)
    {
        LunInfo->DeviceClaimed = FALSE;
        KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
        Srb->SrbStatus = SRB_STATUS_SUCCESS;

        return STATUS_SUCCESS;
    }

    /* Attach, if not already claimed */
    if (LunInfo->DeviceClaimed)
    {
        KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
        Srb->SrbStatus = SRB_STATUS_BUSY;

        return STATUS_DEVICE_BUSY;
    }

    /* Save the device object */
    DeviceObject = LunInfo->DeviceObject;

    if (Srb->Function == SRB_FUNCTION_CLAIM_DEVICE)
        LunInfo->DeviceClaimed = TRUE;

    if (Srb->Function == SRB_FUNCTION_ATTACH_DEVICE)
        LunInfo->DeviceObject = Srb->DataBuffer;

    Srb->DataBuffer = DeviceObject;

    KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
    Srb->SrbStatus = SRB_STATUS_SUCCESS;

    return STATUS_SUCCESS;
}


/**********************************************************************
 * NAME							INTERNAL
 *	ScsiPortDispatchScsi
 *
 * DESCRIPTION
 *	Answer requests for SCSI calls
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	Standard dispatch arguments
 *
 * RETURNS
 *	NTSTATUS
 */

static NTSTATUS NTAPI
ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject,
		     IN PIRP Irp)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PIO_STACK_LOCATION Stack;
    PSCSI_REQUEST_BLOCK Srb;
    KIRQL Irql;
    NTSTATUS Status = STATUS_SUCCESS;
    PIRP NextIrp, IrpList;
    PKDEVICE_QUEUE_ENTRY Entry;

    DPRINT("ScsiPortDispatchScsi(DeviceObject %p  Irp %p)\n",
        DeviceObject, Irp);

    DeviceExtension = DeviceObject->DeviceExtension;
    Stack = IoGetCurrentIrpStackLocation(Irp);

    Srb = Stack->Parameters.Scsi.Srb;
    if (Srb == NULL)
    {
        DPRINT1("ScsiPortDispatchScsi() called with Srb = NULL!\n");
        Status = STATUS_UNSUCCESSFUL;

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

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return(Status);
    }

    DPRINT("Srb: %p\n", Srb);
    DPRINT("Srb->Function: %lu\n", Srb->Function);
    DPRINT("PathId: %lu  TargetId: %lu  Lun: %lu\n", Srb->PathId, Srb->TargetId, Srb->Lun);

    LunExtension = SpiGetLunExtension(DeviceExtension,
                                      Srb->PathId,
                                      Srb->TargetId,
                                      Srb->Lun);
    if (LunExtension == NULL)
    {
        DPRINT("ScsiPortDispatchScsi() called with an invalid LUN\n");
        Status = STATUS_NO_SUCH_DEVICE;

        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        Irp->IoStatus.Status = Status;
        Irp->IoStatus.Information = 0;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return(Status);
    }

    switch (Srb->Function)
    {
    case SRB_FUNCTION_SHUTDOWN:
    case SRB_FUNCTION_FLUSH:
        DPRINT ("  SRB_FUNCTION_SHUTDOWN or FLUSH\n");
        if (DeviceExtension->CachesData == FALSE)
        {
            /* All success here */
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            return STATUS_SUCCESS;
        }
        /* Fall through to a usual execute operation */

    case SRB_FUNCTION_EXECUTE_SCSI:
    case SRB_FUNCTION_IO_CONTROL:
        DPRINT("  SRB_FUNCTION_EXECUTE_SCSI or SRB_FUNCTION_IO_CONTROL\n");
        /* Mark IRP as pending in all cases */
        IoMarkIrpPending(Irp);

        if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
        {
            /* Start IO directly */
            IoStartPacket(DeviceObject, Irp, NULL, NULL);
        }
        else
        {
            KIRQL oldIrql;

            /* We need to be at DISPATCH_LEVEL */
            KeRaiseIrql (DISPATCH_LEVEL, &oldIrql);

            /* Insert IRP into the queue */
            if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue,
                &Irp->Tail.Overlay.DeviceQueueEntry,
                Srb->QueueSortKey))
            {
                /* It means the queue is empty, and we just start this request */
                IoStartPacket(DeviceObject, Irp, NULL, NULL);
            }

            /* Back to the old IRQL */
            KeLowerIrql (oldIrql);
        }
        return STATUS_PENDING;

    case SRB_FUNCTION_CLAIM_DEVICE:
    case SRB_FUNCTION_ATTACH_DEVICE:
        DPRINT ("  SRB_FUNCTION_CLAIM_DEVICE or ATTACH\n");

        /* Reference device object and keep the device object */
        Status = SpiHandleAttachRelease(DeviceExtension, Irp);
        break;

    case SRB_FUNCTION_RELEASE_DEVICE:
        DPRINT ("  SRB_FUNCTION_RELEASE_DEVICE\n");

        /* Dereference device object and clear the device object */
        Status = SpiHandleAttachRelease(DeviceExtension, Irp);
        break;

    case SRB_FUNCTION_RELEASE_QUEUE:
        DPRINT("  SRB_FUNCTION_RELEASE_QUEUE\n");

        /* Guard with the spinlock */
        KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);

        if (!(LunExtension->Flags & LUNEX_FROZEN_QUEUE))
        {
            DPRINT("Queue is not frozen really\n");

            KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            Status = STATUS_SUCCESS;
            break;

        }

        /* Unfreeze the queue */
        LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;

        if (LunExtension->SrbInfo.Srb == NULL)
        {
            /* Get next logical unit request */
            SpiGetNextRequestFromLun(DeviceExtension, LunExtension);

            /* SpiGetNextRequestFromLun() releases the spinlock */
            KeLowerIrql(Irql);
        }
        else
        {
            DPRINT("The queue has active request\n");
            KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
        }


        Srb->SrbStatus = SRB_STATUS_SUCCESS;
        Status = STATUS_SUCCESS;
        break;

    case SRB_FUNCTION_FLUSH_QUEUE:
        DPRINT("  SRB_FUNCTION_FLUSH_QUEUE\n");

        /* Guard with the spinlock */
        KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);

        if (!(LunExtension->Flags & LUNEX_FROZEN_QUEUE))
        {
            DPRINT("Queue is not frozen really\n");

            KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);
            Status = STATUS_INVALID_DEVICE_REQUEST;
            break;
        }

        /* Make sure there is no active request */
        ASSERT(LunExtension->SrbInfo.Srb == NULL);

        /* Compile a list from the device queue */
        IrpList = NULL;
        while ((Entry = KeRemoveDeviceQueue(&LunExtension->DeviceQueue)) != NULL)
        {
                NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);

                /* Get the Srb */
                Stack = IoGetCurrentIrpStackLocation(NextIrp);
                Srb = Stack->Parameters.Scsi.Srb;

                /* Set statuse */
                Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
                NextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;

                /* Add then to the list */
                NextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)IrpList;
                IrpList = NextIrp;
        }

        /* Unfreeze the queue */
        LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;

        /* Release the spinlock */
        KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);

        /* Complete those requests */
        while (IrpList)
        {
            NextIrp = IrpList;
            IrpList = (PIRP)NextIrp->Tail.Overlay.ListEntry.Flink;

            IoCompleteRequest(NextIrp, 0);
        }

        Status = STATUS_SUCCESS;
        break;

    default:
        DPRINT1("SRB function not implemented (Function %lu)\n", Srb->Function);
        Status = STATUS_NOT_IMPLEMENTED;
        break;
    }

    Irp->IoStatus.Status = Status;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return(Status);
}


/**********************************************************************
 * NAME							INTERNAL
 *	ScsiPortDeviceControl
 *
 * DESCRIPTION
 *	Answer requests for device control calls
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	Standard dispatch arguments
 *
 * RETURNS
 *	NTSTATUS
 */

static NTSTATUS NTAPI
ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject,
		      IN PIRP Irp)
{
    PIO_STACK_LOCATION Stack;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PDUMP_POINTERS DumpPointers;
    NTSTATUS Status;

    DPRINT("ScsiPortDeviceControl()\n");

    Irp->IoStatus.Information = 0;

    Stack = IoGetCurrentIrpStackLocation(Irp);
    DeviceExtension = DeviceObject->DeviceExtension;

    switch (Stack->Parameters.DeviceIoControl.IoControlCode)
    {
      case IOCTL_SCSI_GET_DUMP_POINTERS:
        DPRINT("  IOCTL_SCSI_GET_DUMP_POINTERS\n");

        if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DUMP_POINTERS))
        {
          Status = STATUS_BUFFER_OVERFLOW;
          Irp->IoStatus.Information = sizeof(DUMP_POINTERS);
          break;
        }

        DumpPointers = Irp->AssociatedIrp.SystemBuffer;
        DumpPointers->DeviceObject = DeviceObject;
        /* More data.. ? */

        Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = sizeof(DUMP_POINTERS);
        break;

      case IOCTL_SCSI_GET_CAPABILITIES:
        DPRINT("  IOCTL_SCSI_GET_CAPABILITIES\n");
        if (Stack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(PVOID))
        {
            *((PVOID *)Irp->AssociatedIrp.SystemBuffer) = &DeviceExtension->PortCapabilities;

            Irp->IoStatus.Information = sizeof(PVOID);
            Status = STATUS_SUCCESS;
            break;
        }

        if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(IO_SCSI_CAPABILITIES))
        {
            Status = STATUS_BUFFER_TOO_SMALL;
            break;
        }

        RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
                      &DeviceExtension->PortCapabilities,
                      sizeof(IO_SCSI_CAPABILITIES));

        Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES);
        Status = STATUS_SUCCESS;
        break;

      case IOCTL_SCSI_GET_INQUIRY_DATA:
          DPRINT("  IOCTL_SCSI_GET_INQUIRY_DATA\n");

          /* Copy inquiry data to the port device extension */
          Status = SpiGetInquiryData(DeviceExtension, Irp);
          break;

      case IOCTL_SCSI_MINIPORT:
          DPRINT1("IOCTL_SCSI_MINIPORT unimplemented!\n");
          Status = STATUS_NOT_IMPLEMENTED;
          break;

      case IOCTL_SCSI_PASS_THROUGH:
          DPRINT1("IOCTL_SCSI_PASS_THROUGH unimplemented!\n");
          Status = STATUS_NOT_IMPLEMENTED;
          break;

      default:
          if (DEVICE_TYPE_FROM_CTL_CODE(Stack->Parameters.DeviceIoControl.IoControlCode) == MOUNTDEVCONTROLTYPE)
          {
            switch (Stack->Parameters.DeviceIoControl.IoControlCode)
            {
            case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
                DPRINT1("Got unexpected IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
                break;
            case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
                DPRINT1("Got unexpected IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n");
                break;
            default:
                DPRINT1("  got ioctl intended for the mount manager: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode);
                break;
            }
          } else {
            DPRINT1("  unknown ioctl code: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode);
          }
          Status = STATUS_NOT_IMPLEMENTED;
          break;
    }

    /* Complete the request with the given status */
    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
}


static VOID NTAPI
ScsiPortStartIo(IN PDEVICE_OBJECT DeviceObject,
		IN PIRP Irp)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PIO_STACK_LOCATION IrpStack;
    PSCSI_REQUEST_BLOCK Srb;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;
    LONG CounterResult;
    NTSTATUS Status;

    DPRINT("ScsiPortStartIo() called!\n");

    DeviceExtension = DeviceObject->DeviceExtension;
    IrpStack = IoGetCurrentIrpStackLocation(Irp);

    DPRINT("DeviceExtension %p\n", DeviceExtension);

    Srb = IrpStack->Parameters.Scsi.Srb;

    /* Apply "default" flags */
    Srb->SrbFlags |= DeviceExtension->SrbFlags;

    /* Get LUN extension */
    LunExtension = SpiGetLunExtension(DeviceExtension,
                                      Srb->PathId,
                                      Srb->TargetId,
                                      Srb->Lun);

    if (DeviceExtension->NeedSrbDataAlloc ||
        DeviceExtension->NeedSrbExtensionAlloc)
    {
        /* Allocate them */
        SrbInfo = SpiAllocateSrbStructures(DeviceExtension,
                                           LunExtension,
                                           Srb);

        /* Couldn't alloc one or both data structures, return */
        if (SrbInfo == NULL)
        {
            /* We have to call IoStartNextPacket, because this request
               was not started */
            if (LunExtension->Flags & LUNEX_REQUEST_PENDING)
                IoStartNextPacket(DeviceObject, FALSE);

            return;
        }
    }
    else
    {
        /* No allocations are needed */
        SrbInfo = &LunExtension->SrbInfo;
        Srb->SrbExtension = NULL;
        Srb->QueueTag = SP_UNTAGGED;
    }

    /* Increase sequence number of SRB */
    if (!SrbInfo->SequenceNumber)
    {
        /* Increase global sequence number */
        DeviceExtension->SequenceNumber++;

        /* Assign it */
        SrbInfo->SequenceNumber = DeviceExtension->SequenceNumber;
    }

    /* Check some special SRBs */
    if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
    {
        /* Some special handling */
        DPRINT1("Abort command! Unimplemented now\n");
    }
    else
    {
        SrbInfo->Srb = Srb;
    }

    if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION)
    {
        // Store the MDL virtual address in SrbInfo structure
        SrbInfo->DataOffset = MmGetMdlVirtualAddress(Irp->MdlAddress);

        if (DeviceExtension->MapBuffers)
        {
            /* Calculate offset within DataBuffer */
            SrbInfo->DataOffset = MmGetSystemAddressForMdl(Irp->MdlAddress);
            Srb->DataBuffer = SrbInfo->DataOffset +
                (ULONG)((PUCHAR)Srb->DataBuffer -
                (PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress));
        }

        if (DeviceExtension->AdapterObject)
        {
            /* Flush buffers */
            KeFlushIoBuffers(Irp->MdlAddress,
                             Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE,
                             TRUE);
        }

        if (DeviceExtension->MapRegisters)
        {
            /* Calculate number of needed map registers */
            SrbInfo->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
                    Srb->DataBuffer,
                    Srb->DataTransferLength);

            /* Allocate adapter channel */
            Status = IoAllocateAdapterChannel(DeviceExtension->AdapterObject,
                                              DeviceExtension->DeviceObject,
                                              SrbInfo->NumberOfMapRegisters,
                                              SpiAdapterControl,
                                              SrbInfo);

            if (!NT_SUCCESS(Status))
            {
                DPRINT1("IoAllocateAdapterChannel() failed!\n");

                Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
                ScsiPortNotification(RequestComplete,
                                     DeviceExtension + 1,
                                     Srb);

                ScsiPortNotification(NextRequest,
                                     DeviceExtension + 1);

                /* Request DPC for that work */
                IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL);
            }

            /* Control goes to SpiAdapterControl */
            return;
        }
    }

    /* Increase active request counter */
    CounterResult = InterlockedIncrement(&DeviceExtension->ActiveRequestCounter);

    if (CounterResult == 0 &&
        DeviceExtension->AdapterObject != NULL &&
        !DeviceExtension->MapRegisters)
    {
        IoAllocateAdapterChannel(
            DeviceExtension->AdapterObject,
            DeviceObject,
            DeviceExtension->PortCapabilities.MaximumPhysicalPages,
            ScsiPortAllocateAdapterChannel,
            LunExtension
            );

        return;
    }

    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

    if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                                ScsiPortStartPacket,
                                DeviceObject))
    {
        DPRINT("Synchronization failed!\n");

        Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
        Irp->IoStatus.Information = 0;
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }
    else
    {
        /* Release the spinlock only */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }


    DPRINT("ScsiPortStartIo() done\n");
}


static BOOLEAN NTAPI
ScsiPortStartPacket(IN OUT PVOID Context)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PIO_STACK_LOCATION IrpStack;
    PSCSI_REQUEST_BLOCK Srb;
    PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;
    BOOLEAN Result;
    BOOLEAN StartTimer;

    DPRINT("ScsiPortStartPacket() called\n");

    DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

    IrpStack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp);
    Srb = IrpStack->Parameters.Scsi.Srb;

    /* Get LUN extension */
    LunExtension = SpiGetLunExtension(DeviceExtension,
                                      Srb->PathId,
                                      Srb->TargetId,
                                      Srb->Lun);

    /* Check if we are in a reset state */
    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET)
    {
        /* Mark the we've got requests while being in the reset state */
        DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET_REQUEST;
        return TRUE;
    }

    /* Set the time out value */
    DeviceExtension->TimerCount = Srb->TimeOutValue;

    /* We are busy */
    DeviceExtension->Flags |= SCSI_PORT_DEVICE_BUSY;

    if (LunExtension->RequestTimeout != -1)
    {
        /* Timer already active */
        StartTimer = FALSE;
    }
    else
    {
        /* It hasn't been initialized yet */
        LunExtension->RequestTimeout = Srb->TimeOutValue;
        StartTimer = TRUE;
    }

    if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
    {
        /* Handle bypass-requests */

        /* Is this an abort request? */
        if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
        {
            /* Get pointer to SRB info structure */
            SrbInfo = SpiGetSrbData(DeviceExtension,
                                    Srb->PathId,
                                    Srb->TargetId,
                                    Srb->Lun,
                                    Srb->QueueTag);

            /* Check if the request is still "active" */
            if (SrbInfo == NULL ||
                SrbInfo->Srb == NULL ||
                !(SrbInfo->Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE))
            {
                /* It's not, mark it as active then */
                Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;

                if (StartTimer)
                    LunExtension->RequestTimeout = -1;

                DPRINT("Request has been already completed, but abort request came\n");
                Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;

                /* Notify about request complete */
                ScsiPortNotification(RequestComplete,
                                     DeviceExtension->MiniPortDeviceExtension,
                                     Srb);

                /* and about readiness for the next request */
                ScsiPortNotification(NextRequest,
                                     DeviceExtension->MiniPortDeviceExtension);

                /* They might ask for some work, so queue the DPC for them */
                IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL);

                /* We're done in this branch */
                return TRUE;
            }
        }
        else
        {
            /* Add number of queued requests */
            LunExtension->QueueCount++;
        }

        /* Bypass requests don't need request sense */
        LunExtension->Flags &= ~LUNEX_NEED_REQUEST_SENSE;

        /* Is disconnect disabled for this request? */
        if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
        {
            /* Set the corresponding flag */
            DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED;
        }

        /* Transfer timeout value from Srb to Lun */
        LunExtension->RequestTimeout = Srb->TimeOutValue;
    }
    else
    {
        if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
        {
            /* It's a disconnect, so no more requests can go */
            DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED;
        }

        LunExtension->Flags |= SCSI_PORT_LU_ACTIVE;

        /* Increment queue count */
        LunExtension->QueueCount++;

        /* If it's tagged - special thing */
        if (Srb->QueueTag != SP_UNTAGGED)
        {
            SrbInfo = &DeviceExtension->SrbInfo[Srb->QueueTag - 1];

            /* Chek for consistency */
            ASSERT(SrbInfo->Requests.Blink == NULL);

            /* Insert it into the list of requests */
            InsertTailList(&LunExtension->SrbInfo.Requests, &SrbInfo->Requests);
        }
    }

    /* Mark this Srb active */
    Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;

    /* Call HwStartIo routine */
    Result = DeviceExtension->HwStartIo(&DeviceExtension->MiniPortDeviceExtension,
                                        Srb);

    /* If notification is needed, then request a DPC */
    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
        IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL);

    return Result;
}

IO_ALLOCATION_ACTION
NTAPI
SpiAdapterControl(PDEVICE_OBJECT DeviceObject,
                  PIRP Irp,
                  PVOID MapRegisterBase,
                  PVOID Context)
{
    PSCSI_REQUEST_BLOCK Srb;
    PSCSI_SG_ADDRESS ScatterGatherList;
    KIRQL CurrentIrql;
    PIO_STACK_LOCATION IrpStack;
    ULONG TotalLength = 0;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PUCHAR DataVA;
    BOOLEAN WriteToDevice;

    /* Get pointers to SrbInfo and DeviceExtension */
    SrbInfo = (PSCSI_REQUEST_BLOCK_INFO)Context;
    DeviceExtension = DeviceObject->DeviceExtension;

    /* Get pointer to SRB */
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
    Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1;

    /* Depending on the map registers number, we allocate
       either from NonPagedPool, or from our static list */
    if (SrbInfo->NumberOfMapRegisters > MAX_SG_LIST)
    {
        SrbInfo->ScatterGather = ExAllocatePoolWithTag(
            NonPagedPool, SrbInfo->NumberOfMapRegisters * sizeof(SCSI_SG_ADDRESS), TAG_SCSIPORT);

        if (SrbInfo->ScatterGather == NULL)
            ASSERT(FALSE);

        Srb->SrbFlags |= SRB_FLAGS_SGLIST_FROM_POOL;
    }
    else
    {
        SrbInfo->ScatterGather = SrbInfo->ScatterGatherList;
    }

    /* Use chosen SG list source */
    ScatterGatherList = SrbInfo->ScatterGather;

    /* Save map registers base */
    SrbInfo->BaseOfMapRegister = MapRegisterBase;

    /* Determine WriteToDevice flag */
    WriteToDevice = Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE;

    /* Get virtual address of the data buffer */
    DataVA = (PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress) +
                ((PCHAR)Srb->DataBuffer - SrbInfo->DataOffset);

    /* Build the actual SG list */
    while (TotalLength < Srb->DataTransferLength)
    {
        if (!ScatterGatherList)
            break;

        ScatterGatherList->Length = Srb->DataTransferLength - TotalLength;
        ScatterGatherList->PhysicalAddress = IoMapTransfer(DeviceExtension->AdapterObject,
                                                           Irp->MdlAddress,
                                                           MapRegisterBase,
                                                           DataVA + TotalLength,
                                                           &ScatterGatherList->Length,
                                                           WriteToDevice);

        TotalLength += ScatterGatherList->Length;
        ScatterGatherList++;
    }

    /* Schedule an active request */
    InterlockedIncrement(&DeviceExtension->ActiveRequestCounter );
    KeAcquireSpinLock(&DeviceExtension->SpinLock, &CurrentIrql);
    KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                           ScsiPortStartPacket,
                           DeviceObject);
    KeReleaseSpinLock(&DeviceExtension->SpinLock, CurrentIrql);

    return DeallocateObjectKeepRegisters;
}

static PSCSI_PORT_LUN_EXTENSION
SpiAllocateLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
{
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    ULONG LunExtensionSize;

    DPRINT("SpiAllocateLunExtension (%p)\n",
        DeviceExtension);

    /* Round LunExtensionSize first to the sizeof LONGLONG */
    LunExtensionSize = (DeviceExtension->LunExtensionSize +
        sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1);

    LunExtensionSize += sizeof(SCSI_PORT_LUN_EXTENSION);
    DPRINT("LunExtensionSize %lu\n", LunExtensionSize);

    LunExtension = ExAllocatePoolWithTag(NonPagedPool, LunExtensionSize, TAG_SCSIPORT);
    if (LunExtension == NULL)
    {
        DPRINT1("Out of resources!\n");
        return NULL;
    }

    /* Zero everything */
    RtlZeroMemory(LunExtension, LunExtensionSize);

    /* Initialize a list of requests */
    InitializeListHead(&LunExtension->SrbInfo.Requests);

    /* Initialize timeout counter */
    LunExtension->RequestTimeout = -1;

    /* Set maximum queue size */
    LunExtension->MaxQueueCount = 256;

    /* Initialize request queue */
    KeInitializeDeviceQueue (&LunExtension->DeviceQueue);

    return LunExtension;
}

static PSCSI_PORT_LUN_EXTENSION
SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		    IN UCHAR PathId,
		    IN UCHAR TargetId,
		    IN UCHAR Lun)
{
    PSCSI_PORT_LUN_EXTENSION LunExtension;

    DPRINT("SpiGetLunExtension(%p %u %u %u) called\n",
        DeviceExtension, PathId, TargetId, Lun);

    /* Get appropriate list */
    LunExtension = DeviceExtension->LunExtensionList[(TargetId + Lun) % LUS_NUMBER];

    /* Iterate it until we find what we need */
    while (LunExtension)
    {
        if (LunExtension->TargetId == TargetId &&
            LunExtension->Lun == Lun &&
            LunExtension->PathId == PathId)
        {
            /* All matches, return */
            return LunExtension;
        }

        /* Advance to the next item */
        LunExtension = LunExtension->Next;
    }

    /* We did not find anything */
    DPRINT("Nothing found\n");
    return NULL;
}

static PSCSI_REQUEST_BLOCK_INFO
SpiAllocateSrbStructures(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                         PSCSI_PORT_LUN_EXTENSION LunExtension,
                         PSCSI_REQUEST_BLOCK Srb)
{
    PCHAR SrbExtension;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;

    /* Spinlock must be held while this function executes */
    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

    /* Allocate SRB data structure */
    if (DeviceExtension->NeedSrbDataAlloc)
    {
        /* Treat the abort request in a special way */
        if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)
        {
            SrbInfo = SpiGetSrbData(DeviceExtension,
                                    Srb->PathId,
                                    Srb->TargetId,
                                    Srb->Lun,
                                    Srb->QueueTag);
        }
        else if (Srb->SrbFlags &
                 (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE) &&
                 !(Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
                 )
        {
            /* Do not process tagged commands if need request sense is set */
            if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE)
            {
                ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING));

                LunExtension->PendingRequest = Srb->OriginalRequest;
                LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE;

                /* Relese the spinlock and return */
                KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
                return NULL;
            }

            ASSERT(LunExtension->SrbInfo.Srb == NULL);
            SrbInfo = DeviceExtension->FreeSrbInfo;

            if (SrbInfo == NULL)
            {
                /* No SRB structures left in the list. We have to leave
                   and wait while we are called again */

                DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING;
                KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
                return NULL;
            }

            DeviceExtension->FreeSrbInfo = (PSCSI_REQUEST_BLOCK_INFO)SrbInfo->Requests.Flink;

            /* QueueTag must never be 0, so +1 to it */
            Srb->QueueTag = (UCHAR)(SrbInfo - DeviceExtension->SrbInfo) + 1;
        }
        else
        {
            /* Usual untagged command */
            if (
                (!IsListEmpty(&LunExtension->SrbInfo.Requests) ||
                LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE) &&
                !(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)
                )
            {
                /* Mark it as pending and leave */
                ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING));
                LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE;
                LunExtension->PendingRequest = Srb->OriginalRequest;

                KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
                return(NULL);
            }

            Srb->QueueTag = SP_UNTAGGED;
            SrbInfo = &LunExtension->SrbInfo;
        }
    }
    else
    {
        Srb->QueueTag = SP_UNTAGGED;
        SrbInfo = &LunExtension->SrbInfo;
    }

    /* Allocate SRB extension structure */
    if (DeviceExtension->NeedSrbExtensionAlloc)
    {
        /* Check the list of free extensions */
        SrbExtension = DeviceExtension->FreeSrbExtensions;

        /* If no free extensions... */
        if (SrbExtension == NULL)
        {
            /* Free SRB data */
            if (Srb->Function != SRB_FUNCTION_ABORT_COMMAND &&
                Srb->QueueTag != SP_UNTAGGED)
            {
                SrbInfo->Requests.Blink = NULL;
                SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo;
                DeviceExtension->FreeSrbInfo = SrbInfo;
            }

            /* Return, in order to be called again later */
            DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING;
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
            return NULL;
        }

        /* Remove that free SRB extension from the list (since
           we're going to use it) */
        DeviceExtension->FreeSrbExtensions = *((PVOID *)SrbExtension);

        /* Spinlock can be released now */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        Srb->SrbExtension = SrbExtension;

        if (Srb->SenseInfoBuffer != NULL &&
            DeviceExtension->SupportsAutoSense)
        {
            /* Store pointer to the SenseInfo buffer */
            SrbInfo->SaveSenseRequest = Srb->SenseInfoBuffer;

            /* Does data fit the buffer? */
            if (Srb->SenseInfoBufferLength > sizeof(SENSE_DATA))
            {
                /* No, disabling autosense at all */
                Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;
            }
            else
            {
                /* Yes, update the buffer pointer */
                Srb->SenseInfoBuffer = SrbExtension + DeviceExtension->SrbExtensionSize;
            }
        }
    }
    else
    {
        /* Cleanup... */
        Srb->SrbExtension = NULL;
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }

    return SrbInfo;
}


static NTSTATUS
SpiSendInquiry (IN PDEVICE_OBJECT DeviceObject,
		IN PSCSI_LUN_INFO LunInfo)
{
    IO_STATUS_BLOCK IoStatusBlock;
    PIO_STACK_LOCATION IrpStack;
    KEVENT Event;
    KIRQL Irql;
    PIRP Irp;
    NTSTATUS Status;
    PINQUIRYDATA InquiryBuffer;
    PSENSE_DATA SenseBuffer;
    BOOLEAN KeepTrying = TRUE;
    ULONG RetryCount = 0;
    SCSI_REQUEST_BLOCK Srb;
    PCDB Cdb;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;

    DPRINT ("SpiSendInquiry() called\n");

    DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

    InquiryBuffer = ExAllocatePoolWithTag (NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_SCSIPORT);
    if (InquiryBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    SenseBuffer = ExAllocatePoolWithTag (NonPagedPool, SENSE_BUFFER_SIZE, TAG_SCSIPORT);
    if (SenseBuffer == NULL)
    {
        ExFreePool(InquiryBuffer);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    while (KeepTrying)
    {
        /* Initialize event for waiting */
        KeInitializeEvent(&Event,
                          NotificationEvent,
                          FALSE);

        /* Create an IRP */
        Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN,
            DeviceObject,
            NULL,
            0,
            InquiryBuffer,
            INQUIRYDATABUFFERSIZE,
            TRUE,
            &Event,
            &IoStatusBlock);
        if (Irp == NULL)
        {
            DPRINT("IoBuildDeviceIoControlRequest() failed\n");
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        /* Prepare SRB */
        RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));

        Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
        Srb.OriginalRequest = Irp;
        Srb.PathId = LunInfo->PathId;
        Srb.TargetId = LunInfo->TargetId;
        Srb.Lun = LunInfo->Lun;
        Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
        Srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
        Srb.TimeOutValue = 4;
        Srb.CdbLength = 6;

        Srb.SenseInfoBuffer = SenseBuffer;
        Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;

        Srb.DataBuffer = InquiryBuffer;
        Srb.DataTransferLength = INQUIRYDATABUFFERSIZE;

        /* Attach Srb to the Irp */
        IrpStack = IoGetNextIrpStackLocation (Irp);
        IrpStack->Parameters.Scsi.Srb = &Srb;

        /* Fill in CDB */
        Cdb = (PCDB)Srb.Cdb;
        Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
        Cdb->CDB6INQUIRY.LogicalUnitNumber = LunInfo->Lun;
        Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;

        /* Call the driver */
        Status = IoCallDriver(DeviceObject, Irp);

        /* Wait for it to complete */
        if (Status == STATUS_PENDING)
        {
            DPRINT("SpiSendInquiry(): Waiting for the driver to process request...\n");
            KeWaitForSingleObject(&Event,
                Executive,
                KernelMode,
                FALSE,
                NULL);
            Status = IoStatusBlock.Status;
        }

        DPRINT("SpiSendInquiry(): Request processed by driver, status = 0x%08X\n", Status);

        if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS)
        {
            /* All fine, copy data over */
            RtlCopyMemory(LunInfo->InquiryData,
                          InquiryBuffer,
                          INQUIRYDATABUFFERSIZE);

            Status = STATUS_SUCCESS;
            KeepTrying = FALSE;
        }
        else
        {
            DPRINT("Inquiry SRB failed with SrbStatus 0x%08X\n", Srb.SrbStatus);
            /* Check if the queue is frozen */
            if (Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
            {
                /* Something weird happened, deal with it (unfreeze the queue) */
                KeepTrying = FALSE;

                DPRINT("SpiSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId);

                LunExtension = SpiGetLunExtension(DeviceExtension,
                                                  LunInfo->PathId,
                                                  LunInfo->TargetId,
                                                  LunInfo->Lun);

                /* Clear frozen flag */
                LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;

                /* Acquire the spinlock */
                KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);

                /* Process the request */
                SpiGetNextRequestFromLun(DeviceObject->DeviceExtension, LunExtension);

                /* SpiGetNextRequestFromLun() releases the spinlock,
                   so we just lower irql back to what it was before */
                KeLowerIrql(Irql);
            }

            /* Check if data overrun happened */
            if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
            {
                DPRINT("Data overrun at TargetId %d\n", LunInfo->TargetId);
                /* Nothing dramatic, just copy data, but limiting the size */
                RtlCopyMemory(LunInfo->InquiryData,
                              InquiryBuffer,
                              (Srb.DataTransferLength > INQUIRYDATABUFFERSIZE) ?
                              INQUIRYDATABUFFERSIZE : Srb.DataTransferLength);

                Status = STATUS_SUCCESS;
                KeepTrying = FALSE;
            }
            else if ((Srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
                SenseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)
            {
                /* LUN is not valid, but some device responds there.
                   Mark it as invalid anyway */

                Status = STATUS_INVALID_DEVICE_REQUEST;
                KeepTrying = FALSE;
            }
            else
            {
                /* Retry a couple of times if no timeout happened */
                if ((RetryCount < 2) &&
                    (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_NO_DEVICE) &&
                    (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT))
                {
                    RetryCount++;
                    KeepTrying = TRUE;
                }
                else
                {
                    /* That's all, go to exit */
                    KeepTrying = FALSE;

                    /* Set status according to SRB status */
                    if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_FUNCTION ||
                        SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
                    {
                        Status = STATUS_INVALID_DEVICE_REQUEST;
                    }
                    else
                    {
                        Status = STATUS_IO_DEVICE_ERROR;
                    }
                }
            }
        }
    }

    /* Free buffers */
    ExFreePool(InquiryBuffer);
    ExFreePool(SenseBuffer);

    DPRINT("SpiSendInquiry() done with Status 0x%08X\n", Status);

    return Status;
}


/* Scans all SCSI buses */
static VOID
SpiScanAdapter(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension)
{
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    ULONG Bus;
    ULONG Target;
    ULONG Lun;
    PSCSI_BUS_SCAN_INFO BusScanInfo;
    PSCSI_LUN_INFO LastLunInfo, LunInfo, LunInfoExists;
    BOOLEAN DeviceExists;
    ULONG Hint;
    NTSTATUS Status;
    ULONG DevicesFound;

    DPRINT("SpiScanAdapter() called\n");

    /* Scan all buses */
    for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++)
    {
        DPRINT("    Scanning bus %d\n", Bus);
        DevicesFound = 0;

        /* Get pointer to the scan information */
        BusScanInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus];

        if (BusScanInfo)
        {
            /* Find the last LUN info in the list */
            LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo;
            LastLunInfo = LunInfo;

            while (LunInfo != NULL)
            {
                LastLunInfo = LunInfo;
                LunInfo = LunInfo->Next;
            }
        }
        else
        {
            /* We need to allocate this buffer */
            BusScanInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_BUS_SCAN_INFO), TAG_SCSIPORT);

            if (!BusScanInfo)
            {
                DPRINT1("Out of resources!\n");
                return;
            }

            /* Store the pointer in the BusScanInfo array */
            DeviceExtension->BusesConfig->BusScanInfo[Bus] = BusScanInfo;

            /* Fill this struct (length and bus ids for now) */
            BusScanInfo->Length = sizeof(SCSI_BUS_SCAN_INFO);
            BusScanInfo->LogicalUnitsCount = 0;
            BusScanInfo->BusIdentifier = DeviceExtension->PortConfig->InitiatorBusId[Bus];
            BusScanInfo->LunInfo = NULL;

            /* Set pointer to the last LUN info to NULL */
            LastLunInfo = NULL;
        }

        /* Create LUN information structure */
        LunInfo = ExAllocatePoolWithTag(PagedPool, sizeof(SCSI_LUN_INFO), TAG_SCSIPORT);

        if (LunInfo == NULL)
        {
            DPRINT1("Out of resources!\n");
            return;
        }

        RtlZeroMemory(LunInfo, sizeof(SCSI_LUN_INFO));

        /* Create LunExtension */
        LunExtension = SpiAllocateLunExtension (DeviceExtension);

        /* And send INQUIRY to every target */
        for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++)
        {
            /* TODO: Support scan bottom-up */

            /* Skip if it's the same address */
            if (Target == BusScanInfo->BusIdentifier)
                continue;

            /* Try to find an existing device here */
            DeviceExists = FALSE;
            LunInfoExists = BusScanInfo->LunInfo;

            /* Find matching address on this bus */
            while (LunInfoExists)
            {
                if (LunInfoExists->TargetId == Target)
                {
                    DeviceExists = TRUE;
                    break;
                }

                /* Advance to the next one */
                LunInfoExists = LunInfoExists->Next;
            }

            /* No need to bother rescanning, since we already did that before */
            if (DeviceExists)
                continue;

            /* Scan all logical units */
            for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++)
            {
                if ((!LunExtension) || (!LunInfo))
                    break;

                /* Add extension to the list */
                Hint = (Target + Lun) % LUS_NUMBER;
                LunExtension->Next = DeviceExtension->LunExtensionList[Hint];
                DeviceExtension->LunExtensionList[Hint] = LunExtension;

                /* Fill Path, Target, Lun fields */
                LunExtension->PathId = LunInfo->PathId = (UCHAR)Bus;
                LunExtension->TargetId = LunInfo->TargetId = (UCHAR) Target;
                LunExtension->Lun = LunInfo->Lun = (UCHAR)Lun;

                /* Set flag to prevent race conditions */
                LunExtension->Flags |= SCSI_PORT_SCAN_IN_PROGRESS;

                /* Zero LU extension contents */
                if (DeviceExtension->LunExtensionSize)
                {
                    RtlZeroMemory(LunExtension + 1,
                                  DeviceExtension->LunExtensionSize);
                }

                /* Finally send the inquiry command */
                Status = SpiSendInquiry(DeviceExtension->DeviceObject, LunInfo);

                if (NT_SUCCESS(Status))
                {
                    /* Let's see if we really found a device */
                    PINQUIRYDATA InquiryData = (PINQUIRYDATA)LunInfo->InquiryData;

                    /* Check if this device is unsupported */
                    if (InquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED)
                    {
                        DeviceExtension->LunExtensionList[Hint] =
                            DeviceExtension->LunExtensionList[Hint]->Next;

                        continue;
                    }

                    /* Clear the "in scan" flag */
                    LunExtension->Flags &= ~SCSI_PORT_SCAN_IN_PROGRESS;

                    DPRINT("SpiScanAdapter(): Found device of type %d at bus %d tid %d lun %d\n",
                        InquiryData->DeviceType, Bus, Target, Lun);

                    /* Add this info to the linked list */
                    LunInfo->Next = NULL;
                    if (LastLunInfo)
                        LastLunInfo->Next = LunInfo;
                    else
                        BusScanInfo->LunInfo = LunInfo;

                    /* Store the last LUN info */
                    LastLunInfo = LunInfo;

                    /* Store DeviceObject */
                    LunInfo->DeviceObject = DeviceExtension->DeviceObject;

                    /* Allocate another buffer */
                    LunInfo = ExAllocatePoolWithTag(PagedPool, sizeof(SCSI_LUN_INFO), TAG_SCSIPORT);

                    if (!LunInfo)
                    {
                        DPRINT1("Out of resources!\n");
                        break;
                    }

                    RtlZeroMemory(LunInfo, sizeof(SCSI_LUN_INFO));

                    /* Create a new LU extension */
                    LunExtension = SpiAllocateLunExtension(DeviceExtension);

                    DevicesFound++;
                }
                else
                {
                    /* Remove this LUN from the list */
                    DeviceExtension->LunExtensionList[Hint] =
                        DeviceExtension->LunExtensionList[Hint]->Next;

                    /* Decide whether we are continuing or not */
                    if (Status == STATUS_INVALID_DEVICE_REQUEST)
                        continue;
                    else
                        break;
                }
            }
        }

        /* Free allocated buffers */
        if (LunExtension)
            ExFreePool(LunExtension);

        if (LunInfo)
            ExFreePool(LunInfo);

        /* Sum what we found */
        BusScanInfo->LogicalUnitsCount += (UCHAR) DevicesFound;
        DPRINT("    Found %d devices on bus %d\n", DevicesFound, Bus);
    }

    DPRINT ("SpiScanAdapter() done\n");
}


static NTSTATUS
SpiGetInquiryData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		  PIRP Irp)
{
    ULONG InquiryDataSize;
    PSCSI_LUN_INFO LunInfo;
    ULONG BusCount, LunCount, Length;
    PIO_STACK_LOCATION IrpStack;
    PSCSI_ADAPTER_BUS_INFO AdapterBusInfo;
    PSCSI_INQUIRY_DATA InquiryData;
    PSCSI_BUS_DATA BusData;
    ULONG Bus;
    PUCHAR Buffer;

    DPRINT("SpiGetInquiryData() called\n");

    /* Get pointer to the buffer */
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
    Buffer = Irp->AssociatedIrp.SystemBuffer;

    /* Initialize bus and LUN counters */
    BusCount = DeviceExtension->BusesConfig->NumberOfBuses;
    LunCount = 0;

    /* Calculate total number of LUNs */
    for (Bus = 0; Bus < BusCount; Bus++)
        LunCount += DeviceExtension->BusesConfig->BusScanInfo[Bus]->LogicalUnitsCount;

    /* Calculate size of inquiry data, rounding up to sizeof(ULONG) */
    InquiryDataSize =
        ((sizeof(SCSI_INQUIRY_DATA) - 1 + INQUIRYDATABUFFERSIZE +
        sizeof(ULONG) - 1) & ~(sizeof(ULONG) - 1));

    /* Calculate data size */
    Length = sizeof(SCSI_ADAPTER_BUS_INFO) + (BusCount - 1) *
        sizeof(SCSI_BUS_DATA);

    Length += InquiryDataSize * LunCount;

    /* Check, if all data is going to fit into provided buffer */
    if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < Length)
    {
        Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
        return STATUS_BUFFER_TOO_SMALL;
    }

    /* Store data size in the IRP */
    Irp->IoStatus.Information = Length;

    DPRINT("Data size: %lu\n", Length);

    AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer;

    AdapterBusInfo->NumberOfBuses = (UCHAR)BusCount;

    /* Point InquiryData to the corresponding place inside Buffer */
    InquiryData = (PSCSI_INQUIRY_DATA)(Buffer + sizeof(SCSI_ADAPTER_BUS_INFO) +
        (BusCount - 1) * sizeof(SCSI_BUS_DATA));

    /* Loop each bus */
    for (Bus = 0; Bus < BusCount; Bus++)
    {
        BusData = &AdapterBusInfo->BusData[Bus];

        /* Calculate and save an offset of the inquiry data */
        BusData->InquiryDataOffset = (ULONG)((PUCHAR)InquiryData - Buffer);

        /* Get a pointer to the LUN information structure */
        LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo;

        /* Store Initiator Bus Id */
        BusData->InitiatorBusId =
            DeviceExtension->BusesConfig->BusScanInfo[Bus]->BusIdentifier;

        /* Store LUN count */
        BusData->NumberOfLogicalUnits =
            DeviceExtension->BusesConfig->BusScanInfo[Bus]->LogicalUnitsCount;

        /* Loop all LUNs */
        while (LunInfo != NULL)
        {
            DPRINT("(Bus %lu Target %lu Lun %lu)\n",
                Bus, LunInfo->TargetId, LunInfo->Lun);

            /* Fill InquiryData with values */
            InquiryData->PathId = LunInfo->PathId;
            InquiryData->TargetId = LunInfo->TargetId;
            InquiryData->Lun = LunInfo->Lun;
            InquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE;
            InquiryData->DeviceClaimed = LunInfo->DeviceClaimed;
            InquiryData->NextInquiryDataOffset =
                (ULONG)((PUCHAR)InquiryData + InquiryDataSize - Buffer);

            /* Copy data in it */
            RtlCopyMemory(InquiryData->InquiryData,
                          LunInfo->InquiryData,
                          INQUIRYDATABUFFERSIZE);

            /* Move to the next LUN */
            LunInfo = LunInfo->Next;
            InquiryData = (PSCSI_INQUIRY_DATA) ((PCHAR)InquiryData + InquiryDataSize);
        }

        /* Either mark the end, or set offset to 0 */
        if (BusData->NumberOfLogicalUnits != 0)
            ((PSCSI_INQUIRY_DATA) ((PCHAR) InquiryData - InquiryDataSize))->NextInquiryDataOffset = 0;
        else
            BusData->InquiryDataOffset = 0;
    }

    /* Finish with success */
    Irp->IoStatus.Status = STATUS_SUCCESS;
    return STATUS_SUCCESS;
}

static PSCSI_REQUEST_BLOCK_INFO
SpiGetSrbData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
              IN UCHAR PathId,
              IN UCHAR TargetId,
              IN UCHAR Lun,
              IN UCHAR QueueTag)
{
    PSCSI_PORT_LUN_EXTENSION LunExtension;

    if (QueueTag == SP_UNTAGGED)
    {
        /* Untagged request, get LU and return pointer to SrbInfo */
        LunExtension = SpiGetLunExtension(DeviceExtension,
                                          PathId,
                                          TargetId,
                                          Lun);

        /* Return NULL in case of error */
        if (!LunExtension)
            return(NULL);

        /* Return the pointer to SrbInfo */
        return &LunExtension->SrbInfo;
    }
    else
    {
        /* Make sure the tag is valid, if it is - return the data */
        if (QueueTag > DeviceExtension->SrbDataCount || QueueTag < 1)
            return NULL;
        else
            return &DeviceExtension->SrbInfo[QueueTag -1];
    }
}

static VOID
SpiSendRequestSense(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    IN PSCSI_REQUEST_BLOCK InitialSrb)
{
    PSCSI_REQUEST_BLOCK Srb;
    PCDB Cdb;
    PIRP Irp;
    PIO_STACK_LOCATION IrpStack;
    LARGE_INTEGER LargeInt;
    PVOID *Ptr;

    DPRINT("SpiSendRequestSense() entered, InitialSrb %p\n", InitialSrb);

    /* Allocate Srb */
    Srb = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(PVOID), TAG_SCSIPORT);
    RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));

    /* Allocate IRP */
    LargeInt.QuadPart = (LONGLONG) 1;
    Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
                                        DeviceExtension->DeviceObject,
                                        InitialSrb->SenseInfoBuffer,
                                        InitialSrb->SenseInfoBufferLength,
                                        &LargeInt,
                                        NULL);

    IoSetCompletionRoutine(Irp,
                           (PIO_COMPLETION_ROUTINE)SpiCompletionRoutine,
                           Srb,
                           TRUE,
                           TRUE,
                           TRUE);

    if (!Srb)
    {
        DPRINT("SpiSendRequestSense() failed, Srb %p\n", Srb);
        return;
    }

    IrpStack = IoGetNextIrpStackLocation(Irp);
    IrpStack->MajorFunction = IRP_MJ_SCSI;

    /* Put Srb address into Irp... */
    IrpStack->Parameters.Others.Argument1 = (PVOID)Srb;

    /* ...and vice versa */
    Srb->OriginalRequest = Irp;

    /* Save Srb */
    Ptr = (PVOID *)(Srb+1);
    *Ptr = InitialSrb;

    /* Build CDB for REQUEST SENSE */
    Srb->CdbLength = 6;
    Cdb = (PCDB)Srb->Cdb;

    Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
    Cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
    Cdb->CDB6INQUIRY.Reserved1 = 0;
    Cdb->CDB6INQUIRY.PageCode = 0;
    Cdb->CDB6INQUIRY.IReserved = 0;
    Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)InitialSrb->SenseInfoBufferLength;
    Cdb->CDB6INQUIRY.Control = 0;

    /* Set address */
    Srb->TargetId = InitialSrb->TargetId;
    Srb->Lun = InitialSrb->Lun;
    Srb->PathId = InitialSrb->PathId;

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

    /* Timeout will be 2 seconds */
    Srb->TimeOutValue = 2;

    /* No auto request sense */
    Srb->SenseInfoBufferLength = 0;
    Srb->SenseInfoBuffer = NULL;

    /* Set necessary flags */
    Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE |
                    SRB_FLAGS_DISABLE_DISCONNECT;

    /* Transfer disable synch transfer flag */
    if (InitialSrb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER)
        Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;

    Srb->DataBuffer = InitialSrb->SenseInfoBuffer;

    /* Fill the transfer length */
    Srb->DataTransferLength = InitialSrb->SenseInfoBufferLength;

    /* Clear statuses */
    Srb->ScsiStatus = Srb->SrbStatus = 0;
    Srb->NextSrb = 0;

    /* Call the driver */
    (VOID)IoCallDriver(DeviceExtension->DeviceObject, Irp);

    DPRINT("SpiSendRequestSense() done\n");
}


static
VOID
NTAPI
SpiProcessCompletedRequest(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                           IN PSCSI_REQUEST_BLOCK_INFO SrbInfo,
                           OUT PBOOLEAN NeedToCallStartIo)
{
    PSCSI_REQUEST_BLOCK Srb;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    LONG Result;
    PIRP Irp;
    //ULONG SequenceNumber;

    Srb = SrbInfo->Srb;
    Irp = Srb->OriginalRequest;

    /* Get Lun extension */
    LunExtension = SpiGetLunExtension(DeviceExtension,
                                     Srb->PathId,
                                     Srb->TargetId,
                                     Srb->Lun);

    if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION &&
        DeviceExtension->MapBuffers &&
        Irp->MdlAddress)
    {
        /* MDL is shared if transfer is broken into smaller parts */
        Srb->DataBuffer = (PCCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress) +
            ((PCCHAR)Srb->DataBuffer - SrbInfo->DataOffset);

        /* In case of data going in, flush the buffers */
        if (Srb->SrbFlags & SRB_FLAGS_DATA_IN)
        {
            KeFlushIoBuffers(Irp->MdlAddress,
                             TRUE,
                             FALSE);
        }
    }

    /* Flush adapter if needed */
    if (SrbInfo->BaseOfMapRegister)
    {
        /* TODO: Implement */
        ASSERT(FALSE);
    }

    /* Clear the request */
    SrbInfo->Srb = NULL;

    /* If disconnect is disabled... */
    if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
    {
        /* Acquire the spinlock since we mess with flags */
        KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

        /* Set corresponding flag */
        DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED;

        /* Clear the timer if needed */
        if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET))
            DeviceExtension->TimerCount = -1;

        /* Spinlock is not needed anymore */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        if (!(DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING) &&
            !(DeviceExtension->Flags & SCSI_PORT_DEVICE_BUSY) &&
            !(*NeedToCallStartIo))
        {
            /* We're not busy, but we have a request pending */
            IoStartNextPacket(DeviceExtension->DeviceObject, FALSE);
        }
    }

    /* Scatter/gather */
    if (Srb->SrbFlags & SRB_FLAGS_SGLIST_FROM_POOL)
    {
        /* TODO: Implement */
        ASSERT(FALSE);
    }

    /* Acquire spinlock (we're freeing SrbExtension) */
    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

    /* Free it (if needed) */
    if (Srb->SrbExtension)
    {
        if (Srb->SenseInfoBuffer != NULL && DeviceExtension->SupportsAutoSense)
        {
            ASSERT(Srb->SenseInfoBuffer == NULL || SrbInfo->SaveSenseRequest != NULL);

            if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)
            {
                /* Copy sense data to the buffer */
                RtlCopyMemory(SrbInfo->SaveSenseRequest,
                              Srb->SenseInfoBuffer,
                              Srb->SenseInfoBufferLength);
            }

            /* And restore the pointer */
            Srb->SenseInfoBuffer = SrbInfo->SaveSenseRequest;
        }

        /* Put it into the free srb extensions list */
        *((PVOID *)Srb->SrbExtension) = DeviceExtension->FreeSrbExtensions;
        DeviceExtension->FreeSrbExtensions = Srb->SrbExtension;
    }

    /* Save transfer length in the IRP */
    Irp->IoStatus.Information = Srb->DataTransferLength;

    //SequenceNumber = SrbInfo->SequenceNumber;
    SrbInfo->SequenceNumber = 0;

    /* Decrement the queue count */
    LunExtension->QueueCount--;

    /* Free Srb, if needed*/
    if (Srb->QueueTag != SP_UNTAGGED)
    {
        /* Put it into the free list */
        SrbInfo->Requests.Blink = NULL;
        SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo;
        DeviceExtension->FreeSrbInfo = SrbInfo;
    }

    /* SrbInfo is not used anymore */
    SrbInfo = NULL;

    if (DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING)
    {
        /* Clear the flag */
        DeviceExtension->Flags &= ~SCSI_PORT_REQUEST_PENDING;

        /* Note the caller about StartIo */
        *NeedToCallStartIo = TRUE;
    }

    if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
    {
        /* Start the packet */
        Irp->IoStatus.Status = STATUS_SUCCESS;

        if (!(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) &&
            LunExtension->RequestTimeout == -1)
        {
            /* Start the next packet */
            SpiGetNextRequestFromLun(DeviceExtension, LunExtension);
        }
        else
        {
            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
        }

        DPRINT("IoCompleting request IRP 0x%p\n", Irp);

        IoCompleteRequest(Irp, IO_DISK_INCREMENT);

        /* Decrement number of active requests, and analyze the result */
        Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter);

        if (Result < 0 &&
            !DeviceExtension->MapRegisters &&
            DeviceExtension->AdapterObject != NULL)
        {
            /* Nullify map registers */
            DeviceExtension->MapRegisterBase = NULL;
            IoFreeAdapterChannel(DeviceExtension->AdapterObject);
        }

         /* Exit, we're done */
        return;
    }

    /* Decrement number of active requests, and analyze the result */
    Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter);

    if (Result < 0 &&
        !DeviceExtension->MapRegisters &&
        DeviceExtension->AdapterObject != NULL)
    {
        /* Result is negative, so this is a slave, free map registers */
        DeviceExtension->MapRegisterBase = NULL;
        IoFreeAdapterChannel(DeviceExtension->AdapterObject);
    }

    /* Convert status */
    Irp->IoStatus.Status = SpiStatusSrbToNt(Srb->SrbStatus);

    /* It's not a bypass, it's busy or the queue is full? */
    if ((Srb->ScsiStatus == SCSISTAT_BUSY ||
         Srb->SrbStatus == SRB_STATUS_BUSY ||
         Srb->ScsiStatus == SCSISTAT_QUEUE_FULL) &&
         !(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE))
    {

        DPRINT("Busy SRB status %x\n", Srb->SrbStatus);

        /* Requeu, if needed */
        if (LunExtension->Flags & (LUNEX_FROZEN_QUEUE | LUNEX_BUSY))
        {
            DPRINT("it's being requeued\n");

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

            if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue,
                                          &Irp->Tail.Overlay.DeviceQueueEntry,
                                          Srb->QueueSortKey))
            {
                /* It's a big f.ck up if we got here */
                Srb->SrbStatus = SRB_STATUS_ERROR;
                Srb->ScsiStatus = SCSISTAT_BUSY;

                ASSERT(FALSE);
                goto Error;
            }

            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        }
        else if (LunExtension->AttemptCount++ < 20)
        {
            /* LUN is still busy */
            Srb->ScsiStatus = 0;
            Srb->SrbStatus = SRB_STATUS_PENDING;

            LunExtension->BusyRequest = Irp;
            LunExtension->Flags |= LUNEX_BUSY;

            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
        }
        else
        {
Error:
            /* Freeze the queue*/
            Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
            LunExtension->Flags |= LUNEX_FROZEN_QUEUE;

            /* "Unfull" the queue */
            LunExtension->Flags &= ~LUNEX_FULL_QUEUE;

            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

            /* Return status that the device is not ready */
            Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

        return;
    }

    /* Start the next request, if LUN is idle, and this is sense request */
    if (((Srb->ScsiStatus != SCSISTAT_CHECK_CONDITION) ||
        (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) ||
        !Srb->SenseInfoBuffer || !Srb->SenseInfoBufferLength)
        && (Srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE))
    {
        if (LunExtension->RequestTimeout == -1)
            SpiGetNextRequestFromLun(DeviceExtension, LunExtension);
        else
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }
    else
    {
        /* Freeze the queue */
        Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
        LunExtension->Flags |= LUNEX_FROZEN_QUEUE;

        /* Do we need a request sense? */
        if (Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION &&
            !(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
            Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength)
        {
            /* If LUN is busy, we have to requeue it in order to allow request sense */
            if (LunExtension->Flags & LUNEX_BUSY)
            {
                DPRINT("Requeueing busy request to allow request sense\n");

                if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue,
                    &LunExtension->BusyRequest->Tail.Overlay.DeviceQueueEntry,
                    Srb->QueueSortKey))
                {
                    /* We should never get here */
                    ASSERT(FALSE);

                    KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
                    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
                    return;

                }

                /* Clear busy flags */
                LunExtension->Flags &= ~(LUNEX_FULL_QUEUE | LUNEX_BUSY);
            }

            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

            /* Send RequestSense */
            SpiSendRequestSense(DeviceExtension, Srb);

            /* Exit */
            return;
        }

        /* Release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }

    /* Complete the request */
    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}

NTSTATUS
NTAPI
SpiCompletionRoutine(PDEVICE_OBJECT DeviceObject,
                     PIRP Irp,
                     PVOID Context)
{
    PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)Context;
    PSCSI_REQUEST_BLOCK InitialSrb;
    PIRP InitialIrp;

    DPRINT("SpiCompletionRoutine() entered, IRP %p \n", Irp);

    if ((Srb->Function == SRB_FUNCTION_RESET_BUS) ||
        (Srb->Function == SRB_FUNCTION_ABORT_COMMAND))
    {
        /* Deallocate SRB and IRP and exit */
        ExFreePool(Srb);
        IoFreeIrp(Irp);

        return STATUS_MORE_PROCESSING_REQUIRED;
    }

    /* Get a pointer to the SRB and IRP which were initially sent */
    InitialSrb = *((PVOID *)(Srb+1));
    InitialIrp = InitialSrb->OriginalRequest;

    if ((SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) ||
        (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN))
    {
        /* Sense data is OK */
        InitialSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;

        /* Set length to be the same */
        InitialSrb->SenseInfoBufferLength = (UCHAR)Srb->DataTransferLength;
    }

    /* Make sure initial SRB's queue is frozen */
    ASSERT(InitialSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN);

    /* Complete this request */
    IoCompleteRequest(InitialIrp, IO_DISK_INCREMENT);

    /* Deallocate everything (internal) */
    ExFreePool(Srb);

    if (Irp->MdlAddress != NULL)
    {
		MmUnlockPages(Irp->MdlAddress);
        IoFreeMdl(Irp->MdlAddress);
        Irp->MdlAddress = NULL;
    }

    IoFreeIrp(Irp);
    return STATUS_MORE_PROCESSING_REQUIRED;
}

static BOOLEAN NTAPI
ScsiPortIsr(IN PKINTERRUPT Interrupt,
            IN PVOID ServiceContext)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;

    DPRINT("ScsiPortIsr() called!\n");

    DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)ServiceContext;

    /* If interrupts are disabled - we don't expect any */
    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_DISABLE_INTERRUPTS)
        return FALSE;

    /* Call miniport's HwInterrupt routine */
    if (DeviceExtension->HwInterrupt(&DeviceExtension->MiniPortDeviceExtension) == FALSE)
    {
        /* This interrupt doesn't belong to us */
        return FALSE;
    }

    /* If flag of notification is set - queue a DPC */
    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
    {
        IoRequestDpc(DeviceExtension->DeviceObject,
                     DeviceExtension->CurrentIrp,
                     DeviceExtension);
    }

    return TRUE;
}

BOOLEAN
NTAPI
SpiSaveInterruptData(IN PVOID Context)
{
    PSCSI_PORT_SAVE_INTERRUPT InterruptContext = Context;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    PSCSI_REQUEST_BLOCK Srb;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo, NextSrbInfo;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    BOOLEAN IsTimed;

    /* Get pointer to the device extension */
    DeviceExtension = InterruptContext->DeviceExtension;

    /* If we don't have anything pending - return */
    if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED))
        return FALSE;

    /* Actually save the interrupt data */
    *InterruptContext->InterruptData = DeviceExtension->InterruptData;

    /* Clear the data stored in the device extension */
    DeviceExtension->InterruptData.Flags &=
        (SCSI_PORT_RESET | SCSI_PORT_RESET_REQUEST | SCSI_PORT_DISABLE_INTERRUPTS);
    DeviceExtension->InterruptData.CompletedAbort = NULL;
    DeviceExtension->InterruptData.ReadyLun = NULL;
    DeviceExtension->InterruptData.CompletedRequests = NULL;

    /* Loop through the list of completed requests */
    SrbInfo = InterruptContext->InterruptData->CompletedRequests;

    while (SrbInfo)
    {
        /* Make sure we have SRV */
        ASSERT(SrbInfo->Srb);

        /* Get SRB and LunExtension */
        Srb = SrbInfo->Srb;

        LunExtension = SpiGetLunExtension(DeviceExtension,
                                          Srb->PathId,
                                          Srb->TargetId,
                                          Srb->Lun);

        /* We have to check special cases if request is unsuccessfull*/
        if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
        {
            /* Check if we need request sense by a few conditions */
            if (Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength &&
                Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION &&
                !(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID))
            {
                if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE)
                {
                    /* It means: we tried to send REQUEST SENSE, but failed */

                    Srb->ScsiStatus = 0;
                    Srb->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
                }
                else
                {
                    /* Set the corresponding flag, so that REQUEST SENSE
                       will be sent */
                    LunExtension->Flags |= LUNEX_NEED_REQUEST_SENSE;
                }

            }

            /* Check for a full queue */
            if (Srb->ScsiStatus == SCSISTAT_QUEUE_FULL)
            {
                /* TODO: Implement when it's encountered */
                ASSERT(FALSE);
            }
        }

        /* Let's decide if we need to watch timeout or not */
        if (Srb->QueueTag == SP_UNTAGGED)
        {
            IsTimed = TRUE;
        }
        else
        {
            if (LunExtension->SrbInfo.Requests.Flink == &SrbInfo->Requests)
                IsTimed = TRUE;
            else
                IsTimed = FALSE;

            /* Remove it from the queue */
            RemoveEntryList(&SrbInfo->Requests);
        }

        if (IsTimed)
        {
            /* We have to maintain timeout counter */
            if (IsListEmpty(&LunExtension->SrbInfo.Requests))
            {
                LunExtension->RequestTimeout = -1;
            }
            else
            {
                NextSrbInfo = CONTAINING_RECORD(LunExtension->SrbInfo.Requests.Flink,
                                                SCSI_REQUEST_BLOCK_INFO,
                                                Requests);

                Srb = NextSrbInfo->Srb;

                /* Update timeout counter */
                LunExtension->RequestTimeout = Srb->TimeOutValue;
            }
        }

        SrbInfo = SrbInfo->CompletedRequests;
    }

    return TRUE;
}

VOID
NTAPI
SpiGetNextRequestFromLun(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                         IN PSCSI_PORT_LUN_EXTENSION LunExtension)
{
    PIO_STACK_LOCATION IrpStack;
    PIRP NextIrp;
    PKDEVICE_QUEUE_ENTRY Entry;
    PSCSI_REQUEST_BLOCK Srb;


    /* If LUN is not active or queue is more than maximum allowed  */
    if (LunExtension->QueueCount >= LunExtension->MaxQueueCount ||
        !(LunExtension->Flags & SCSI_PORT_LU_ACTIVE))
    {
        /* Release the spinlock and exit */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
        return;
    }

    /* Check if we can get a next request */
    if (LunExtension->Flags &
        (LUNEX_NEED_REQUEST_SENSE | LUNEX_BUSY |
         LUNEX_FULL_QUEUE | LUNEX_FROZEN_QUEUE | LUNEX_REQUEST_PENDING))
    {
        /* Pending requests can only be started if the queue is empty */
        if (IsListEmpty(&LunExtension->SrbInfo.Requests) &&
            !(LunExtension->Flags &
              (LUNEX_BUSY | LUNEX_FROZEN_QUEUE | LUNEX_FULL_QUEUE | LUNEX_NEED_REQUEST_SENSE)))
        {
            /* Make sure we have SRB */
            ASSERT(LunExtension->SrbInfo.Srb == NULL);

            /* Clear active and pending flags */
            LunExtension->Flags &= ~(LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE);

            /* Get next Irp, and clear pending requests list */
            NextIrp = LunExtension->PendingRequest;
            LunExtension->PendingRequest = NULL;

            /* Set attempt counter to zero */
            LunExtension->AttemptCount = 0;

            /* Release the spinlock */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

            /* Start the next pending request */
            IoStartPacket(DeviceExtension->DeviceObject, NextIrp, (PULONG)NULL, NULL);

            return;
        }
        else
        {
            /* Release the spinlock, without clearing any flags and exit */
            KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

            return;
        }
    }

    /* Reset active flag */
    LunExtension->Flags &= ~SCSI_PORT_LU_ACTIVE;

    /* Set attempt counter to zero */
    LunExtension->AttemptCount = 0;

    /* Remove packet from the device queue */
    Entry = KeRemoveByKeyDeviceQueue(&LunExtension->DeviceQueue, LunExtension->SortKey);

    if (Entry != NULL)
    {
        /* Get pointer to the next irp */
        NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);

        /* Get point to the SRB */
        IrpStack = IoGetCurrentIrpStackLocation(NextIrp);
        Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1;

        /* Set new key*/
        LunExtension->SortKey = Srb->QueueSortKey;
        LunExtension->SortKey++;

        /* Release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        /* Start the next pending request */
        IoStartPacket(DeviceExtension->DeviceObject, NextIrp, (PULONG)NULL, NULL);
    }
    else
    {
        /* Release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }
}



//    ScsiPortDpcForIsr
//  DESCRIPTION:
//
//  RUN LEVEL:
//
//  ARGUMENTS:
//    IN PKDPC          Dpc
//    IN PDEVICE_OBJECT DpcDeviceObject
//    IN PIRP           DpcIrp
//    IN PVOID          DpcContext
//
static VOID NTAPI
ScsiPortDpcForIsr(IN PKDPC Dpc,
		  IN PDEVICE_OBJECT DpcDeviceObject,
		  IN PIRP DpcIrp,
		  IN PVOID DpcContext)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DpcDeviceObject->DeviceExtension;
    SCSI_PORT_INTERRUPT_DATA InterruptData;
    SCSI_PORT_SAVE_INTERRUPT Context;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    BOOLEAN NeedToStartIo;
    PSCSI_REQUEST_BLOCK_INFO SrbInfo;
    LARGE_INTEGER TimerValue;

    DPRINT("ScsiPortDpcForIsr(Dpc %p  DpcDeviceObject %p  DpcIrp %p  DpcContext %p)\n",
           Dpc, DpcDeviceObject, DpcIrp, DpcContext);

    /* We need to acquire spinlock */
    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

	RtlZeroMemory(&InterruptData, sizeof(SCSI_PORT_INTERRUPT_DATA));

TryAgain:

    /* Interrupt structure must be snapshotted, and only then analyzed */
    Context.InterruptData = &InterruptData;
    Context.DeviceExtension = DeviceExtension;

    if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                                SpiSaveInterruptData,
                                &Context))
    {
        /* Nothing - just return (don't forget to release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
        DPRINT("ScsiPortDpcForIsr() done\n");
        return;
    }

    /* If flush of adapters is needed - do it */
    if (InterruptData.Flags & SCSI_PORT_FLUSH_ADAPTERS)
    {
        /* TODO: Implement */
        ASSERT(FALSE);
    }

    /* Check for IoMapTransfer */
    if (InterruptData.Flags & SCSI_PORT_MAP_TRANSFER)
    {
        /* TODO: Implement */
        ASSERT(FALSE);
    }

    /* Check if timer is needed */
    if (InterruptData.Flags & SCSI_PORT_TIMER_NEEDED)
    {
        /* Save the timer routine */
        DeviceExtension->HwScsiTimer = InterruptData.HwScsiTimer;

        if (InterruptData.MiniportTimerValue == 0)
        {
            /* Cancel the timer */
            KeCancelTimer(&DeviceExtension->MiniportTimer);
        }
        else
        {
            /* Convert timer value */
            TimerValue.QuadPart = Int32x32To64(InterruptData.MiniportTimerValue, -10);

            /* Set the timer */
            KeSetTimer(&DeviceExtension->MiniportTimer,
                       TimerValue,
                       &DeviceExtension->MiniportTimerDpc);
        }
    }

    /* If it's ready for the next request */
    if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY)
    {
        /* Check for a duplicate request (NextRequest+NextLuRequest) */
        if ((DeviceExtension->Flags &
            (SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED)) ==
            (SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED))
        {
            /* Clear busy flag set by ScsiPortStartPacket() */
            DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY;

            if (!(InterruptData.Flags & SCSI_PORT_RESET))
            {
                /* Ready for next, and no reset is happening */
                DeviceExtension->TimerCount = -1;
            }
        }
        else
        {
            /* Not busy, but not ready for the next request */
            DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY;
            InterruptData.Flags &= ~SCSI_PORT_NEXT_REQUEST_READY;
        }
    }

    /* Any resets? */
    if (InterruptData.Flags & SCSI_PORT_RESET_REPORTED)
    {
        /* Hold for a bit */
        DeviceExtension->TimerCount = 4;
    }

    /* Any ready LUN? */
    if (InterruptData.ReadyLun != NULL)
    {

        /* Process all LUNs from the list*/
        while (TRUE)
        {
            /* Remove it from the list first (as processed) */
            LunExtension = InterruptData.ReadyLun;
            InterruptData.ReadyLun = LunExtension->ReadyLun;
            LunExtension->ReadyLun = NULL;

            /* Get next request for this LUN */
            SpiGetNextRequestFromLun(DeviceExtension, LunExtension);

            /* Still ready requests exist?
               If yes - get spinlock, if no - stop here */
            if (InterruptData.ReadyLun != NULL)
                KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
            else
                break;
        }
    }
    else
    {
        /* Release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }

    /* If we ready for next packet, start it */
    if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY)
        IoStartNextPacket(DeviceExtension->DeviceObject, FALSE);

    NeedToStartIo = FALSE;

    /* Loop the completed request list */
    while (InterruptData.CompletedRequests)
    {
        /* Remove the request */
        SrbInfo = InterruptData.CompletedRequests;
        InterruptData.CompletedRequests = SrbInfo->CompletedRequests;
        SrbInfo->CompletedRequests = NULL;

        /* Process it */
        SpiProcessCompletedRequest(DeviceExtension,
                                  SrbInfo,
                                  &NeedToStartIo);
    }

    /* Loop abort request list */
    while (InterruptData.CompletedAbort)
    {
        LunExtension = InterruptData.CompletedAbort;

        /* Remove the request */
        InterruptData.CompletedAbort = LunExtension->CompletedAbortRequests;

        /* Get spinlock since we're going to change flags */
        KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

        /* TODO: Put SrbExtension to the list of free extensions */
        ASSERT(FALSE);
    }

    /* If we need - call StartIo routine */
    if (NeedToStartIo)
    {
        /* Make sure CurrentIrp is not null! */
        ASSERT(DpcDeviceObject->CurrentIrp != NULL);
        ScsiPortStartIo(DpcDeviceObject, DpcDeviceObject->CurrentIrp);
    }

    /* Everything has been done, check */
    if (InterruptData.Flags & SCSI_PORT_ENABLE_INT_REQUEST)
    {
        /* Synchronize using spinlock */
        KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

        /* Request an interrupt */
        DeviceExtension->HwInterrupt(DeviceExtension->MiniPortDeviceExtension);

        ASSERT(DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET);

        /* Should interrupts be enabled again? */
        if (DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET)
        {
            /* Clear this flag */
            DeviceExtension->Flags &= ~SCSI_PORT_DISABLE_INT_REQUESET;

            /* Call a special routine to do this */
            ASSERT(FALSE);
#if 0
            KeSynchronizeExecution(DeviceExtension->Interrupt,
                                   SpiEnableInterrupts,
                                   DeviceExtension);
#endif
        }

        /* If we need a notification again - loop */
        if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
            goto TryAgain;

        /* Release the spinlock */
        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
    }

    DPRINT("ScsiPortDpcForIsr() done\n");
}

BOOLEAN
NTAPI
SpiProcessTimeout(PVOID ServiceContext)
{
    PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
    ULONG Bus;

    DPRINT("SpiProcessTimeout() entered\n");

    DeviceExtension->TimerCount = -1;

    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET)
    {
        DeviceExtension->InterruptData.Flags &= ~SCSI_PORT_RESET;

        if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET_REQUEST)
        {
            DeviceExtension->InterruptData.Flags &=  ~SCSI_PORT_RESET_REQUEST;
            ScsiPortStartPacket(ServiceContext);
        }

        return FALSE;
    }
    else
    {
        DPRINT("Resetting the bus\n");

        for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++)
        {
            DeviceExtension->HwResetBus(DeviceExtension->MiniPortDeviceExtension, Bus);

            /* Reset flags and set reset timeout to 4 seconds */
            DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET;
            DeviceExtension->TimerCount = 4;
        }

        /* If miniport requested - request a dpc for it */
        if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
            IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL);
    }

    return TRUE;
}


BOOLEAN
NTAPI
SpiResetBus(PVOID ServiceContext)
{
    PRESETBUS_PARAMS ResetParams = (PRESETBUS_PARAMS)ServiceContext;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;

    /* Perform the bus reset */
    DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)ResetParams->DeviceExtension;
    DeviceExtension->HwResetBus(DeviceExtension->MiniPortDeviceExtension,
                                ResetParams->PathId);

    /* Set flags and start the timer */
    DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET;
    DeviceExtension->TimerCount = 4;

    /* If miniport requested - give him a DPC */
    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
        IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL);

    return TRUE;
}

//    ScsiPortIoTimer
//  DESCRIPTION:
//    This function handles timeouts and other time delayed processing
//
//  RUN LEVEL:
//
//  ARGUMENTS:
//    IN  PDEVICE_OBJECT  DeviceObject  Device object registered with timer
//    IN  PVOID           Context       the Controller extension for the
//                                      controller the device is on
//
static VOID NTAPI
ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject,
                PVOID Context)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;
    PSCSI_PORT_LUN_EXTENSION LunExtension;
    ULONG Lun;
    PIRP Irp;

    DPRINT("ScsiPortIoTimer()\n");

    DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

    /* Protect with the spinlock */
    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

    /* Check timeouts */
    if (DeviceExtension->TimerCount > 0)
    {
        /* Decrease the timeout counter */
        DeviceExtension->TimerCount--;

        if (DeviceExtension->TimerCount == 0)
        {
            /* Timeout, process it */
            if (KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                                       SpiProcessTimeout,
                                       DeviceExtension->DeviceObject))
            {
                DPRINT("Error happened during processing timeout, but nothing critical\n");
            }
        }

        KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

        /* We should exit now, since timeout is processed */
        return;
    }

    /* Per-Lun scanning of timeouts is needed... */
    for (Lun = 0; Lun < LUS_NUMBER; Lun++)
    {
        LunExtension = DeviceExtension->LunExtensionList[Lun];

        while (LunExtension)
        {
            if (LunExtension->Flags & LUNEX_BUSY)
            {
                if (!(LunExtension->Flags &
                    (LUNEX_NEED_REQUEST_SENSE | LUNEX_FROZEN_QUEUE)))
                {
                    DPRINT("Retrying busy request\n");

                    /* Clear flags, and retry busy request */
                    LunExtension->Flags &= ~(LUNEX_BUSY | LUNEX_FULL_QUEUE);
                    Irp = LunExtension->BusyRequest;

                    /* Clearing busy request */
                    LunExtension->BusyRequest = NULL;

                    KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

                    IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL);

                    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
                }
            }
            else if (LunExtension->RequestTimeout == 0)
            {
                RESETBUS_PARAMS ResetParams;

                LunExtension->RequestTimeout = -1;

                DPRINT("Request timed out, resetting bus\n");

                /* Pass params to the bus reset routine */
                ResetParams.PathId = LunExtension->PathId;
                ResetParams.DeviceExtension = DeviceExtension;

                if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0],
                                            SpiResetBus,
                                            &ResetParams))
                {
                    DPRINT1("Reset failed\n");
                }
            }
            else if (LunExtension->RequestTimeout > 0)
            {
                /* Decrement the timeout counter */
                LunExtension->RequestTimeout--;
            }

            LunExtension = LunExtension->Next;
        }
    }

    /* Release the spinlock */
    KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
}

/**********************************************************************
 * NAME							INTERNAL
 *	SpiBuildDeviceMap
 *
 * DESCRIPTION
 *	Builds the registry device map of all device which are attached
 *	to the given SCSI HBA port. The device map is located at:
 *	  \Registry\Machine\DeviceMap\Scsi
 *
 * RUN LEVEL
 *	PASSIVE_LEVEL
 *
 * ARGUMENTS
 *	DeviceExtension
 *		...
 *
 *	RegistryPath
 *		Name of registry driver service key.
 *
 * RETURNS
 *	NTSTATUS
 */

static NTSTATUS
SpiBuildDeviceMap (PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
		   PUNICODE_STRING RegistryPath)
{
  PSCSI_PORT_LUN_EXTENSION LunExtension;
  OBJECT_ATTRIBUTES ObjectAttributes;
  UNICODE_STRING KeyName;
  UNICODE_STRING ValueName;
  WCHAR NameBuffer[64];
  ULONG Disposition;
  HANDLE ScsiKey;
  HANDLE ScsiPortKey = NULL;
  HANDLE ScsiBusKey = NULL;
  HANDLE ScsiInitiatorKey = NULL;
  HANDLE ScsiTargetKey = NULL;
  HANDLE ScsiLunKey = NULL;
  ULONG BusNumber;
  ULONG Target;
  ULONG CurrentTarget;
  ULONG Lun;
  PWCHAR DriverName;
  ULONG UlongData;
  PWCHAR TypeName;
  NTSTATUS Status;

  DPRINT("SpiBuildDeviceMap() called\n");

  if (DeviceExtension == NULL || RegistryPath == NULL)
    {
      DPRINT1("Invalid parameter\n");
      return(STATUS_INVALID_PARAMETER);
    }

  /* Open or create the 'Scsi' subkey */
  RtlInitUnicodeString(&KeyName,
			  L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi");
  InitializeObjectAttributes(&ObjectAttributes,
			     &KeyName,
			     OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_KERNEL_HANDLE,
			     0,
			     NULL);
  Status = ZwCreateKey(&ScsiKey,
		       KEY_ALL_ACCESS,
		       &ObjectAttributes,
		       0,
		       NULL,
		       REG_OPTION_VOLATILE,
		       &Disposition);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
      return(Status);
    }

  /* Create new 'Scsi Port X' subkey */
  DPRINT("Scsi Port %lu\n",
	 DeviceExtension->PortNumber);

  swprintf(NameBuffer,
	   L"Scsi Port %lu",
	   DeviceExtension->PortNumber);
  RtlInitUnicodeString(&KeyName,
		       NameBuffer);
  InitializeObjectAttributes(&ObjectAttributes,
			     &KeyName,
			     OBJ_KERNEL_HANDLE,
			     ScsiKey,
			     NULL);
  Status = ZwCreateKey(&ScsiPortKey,
		       KEY_ALL_ACCESS,
		       &ObjectAttributes,
		       0,
		       NULL,
		       REG_OPTION_VOLATILE,
		       &Disposition);
  ZwClose(ScsiKey);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
      return(Status);
    }

  /*
   * Create port-specific values
   */

  /* Set 'DMA Enabled' (REG_DWORD) value */
  UlongData = (ULONG)!DeviceExtension->PortCapabilities.AdapterUsesPio;
  DPRINT("  DMA Enabled = %s\n", (UlongData) ? "TRUE" : "FALSE");
  RtlInitUnicodeString(&ValueName,
		       L"DMA Enabled");
  Status = ZwSetValueKey(ScsiPortKey,
			 &ValueName,
			 0,
			 REG_DWORD,
			 &UlongData,
			 sizeof(ULONG));
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwSetValueKey('DMA Enabled') failed (Status %lx)\n", Status);
      ZwClose(ScsiPortKey);
      return(Status);
    }

  /* Set 'Driver' (REG_SZ) value */
  DriverName = wcsrchr(RegistryPath->Buffer, L'\\') + 1;
  RtlInitUnicodeString(&ValueName,
		       L"Driver");
  Status = ZwSetValueKey(ScsiPortKey,
			 &ValueName,
			 0,
			 REG_SZ,
			 DriverName,
			 (ULONG)((wcslen(DriverName) + 1) * sizeof(WCHAR)));
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwSetValueKey('Driver') failed (Status %lx)\n", Status);
      ZwClose(ScsiPortKey);
      return(Status);
    }

  /* Set 'Interrupt' (REG_DWORD) value (NT4 only) */
  UlongData = (ULONG)DeviceExtension->PortConfig->BusInterruptLevel;
  DPRINT("  Interrupt = %lu\n", UlongData);
  RtlInitUnicodeString(&ValueName,
		       L"Interrupt");
  Status = ZwSetValueKey(ScsiPortKey,
			 &ValueName,
			 0,
			 REG_DWORD,
			 &UlongData,
			 sizeof(ULONG));
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwSetValueKey('Interrupt') failed (Status %lx)\n", Status);
      ZwClose(ScsiPortKey);
      return(Status);
    }

  /* Set 'IOAddress' (REG_DWORD) value (NT4 only) */
  UlongData = ScsiPortConvertPhysicalAddressToUlong((*DeviceExtension->PortConfig->AccessRanges)[0].RangeStart);
  DPRINT("  IOAddress = %lx\n", UlongData);
  RtlInitUnicodeString(&ValueName,
		       L"IOAddress");
  Status = ZwSetValueKey(ScsiPortKey,
			 &ValueName,
			 0,
			 REG_DWORD,
			 &UlongData,
			 sizeof(ULONG));
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ZwSetValueKey('IOAddress') failed (Status %lx)\n", Status);
      ZwClose(ScsiPortKey);
      return(Status);
    }

  /* Enumerate buses */
  for (BusNumber = 0; BusNumber < DeviceExtension->PortConfig->NumberOfBuses; BusNumber++)
    {
      /* Create 'Scsi Bus X' key */
      DPRINT("    Scsi Bus %lu\n", BusNumber);
      swprintf(NameBuffer,
	       L"Scsi Bus %lu",
	       BusNumber);
      RtlInitUnicodeString(&KeyName,
			   NameBuffer);
      InitializeObjectAttributes(&ObjectAttributes,
				 &KeyName,
				 0,
				 ScsiPortKey,
				 NULL);
      Status = ZwCreateKey(&ScsiBusKey,
			   KEY_ALL_ACCESS,
			   &ObjectAttributes,
			   0,
			   NULL,
			   REG_OPTION_VOLATILE,
			   &Disposition);
      if (!NT_SUCCESS(Status))
	{
	  DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
	  goto ByeBye;
	}

      /* Create 'Initiator Id X' key */
      DPRINT("      Initiator Id %u\n",
	      DeviceExtension->PortConfig->InitiatorBusId[BusNumber]);
      swprintf(NameBuffer,
	       L"Initiator Id %u",
	       (unsigned int)(UCHAR)DeviceExtension->PortConfig->InitiatorBusId[BusNumber]);
      RtlInitUnicodeString(&KeyName,
			   NameBuffer);
      InitializeObjectAttributes(&ObjectAttributes,
				 &KeyName,
				 0,
				 ScsiBusKey,
				 NULL);
      Status = ZwCreateKey(&ScsiInitiatorKey,
			   KEY_ALL_ACCESS,
			   &ObjectAttributes,
			   0,
			   NULL,
			   REG_OPTION_VOLATILE,
			   &Disposition);
      if (!NT_SUCCESS(Status))
	{
	  DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
	  goto ByeBye;
	}

      /* FIXME: Are there any initiator values (??) */

      ZwClose(ScsiInitiatorKey);
      ScsiInitiatorKey = NULL;


      /* Enumerate targets */
      CurrentTarget = (ULONG)-1;
      ScsiTargetKey = NULL;
      for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++)
	{
	  for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++)
	    {
	      LunExtension = SpiGetLunExtension(DeviceExtension,
						(UCHAR)BusNumber,
						(UCHAR)Target,
						(UCHAR)Lun);
	      if (LunExtension != NULL)
		{
		  if (Target != CurrentTarget)
		    {
		      /* Close old target key */
		      if (ScsiTargetKey != NULL)
			{
			  ZwClose(ScsiTargetKey);
			  ScsiTargetKey = NULL;
			}

		      /* Create 'Target Id X' key */
		      DPRINT("      Target Id %lu\n", Target);
		      swprintf(NameBuffer,
			       L"Target Id %lu",
			       Target);
		      RtlInitUnicodeString(&KeyName,
					   NameBuffer);
		      InitializeObjectAttributes(&ObjectAttributes,
						 &KeyName,
						 0,
						 ScsiBusKey,
						 NULL);
		      Status = ZwCreateKey(&ScsiTargetKey,
					   KEY_ALL_ACCESS,
					   &ObjectAttributes,
					   0,
					   NULL,
					   REG_OPTION_VOLATILE,
					   &Disposition);
		      if (!NT_SUCCESS(Status))
			{
			  DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
			  goto ByeBye;
			}

		      CurrentTarget = Target;
		    }

		  /* Create 'Logical Unit Id X' key */
		  DPRINT("        Logical Unit Id %lu\n", Lun);
		  swprintf(NameBuffer,
			   L"Logical Unit Id %lu",
			   Lun);
		  RtlInitUnicodeString(&KeyName,
				       NameBuffer);
		  InitializeObjectAttributes(&ObjectAttributes,
					     &KeyName,
					     0,
					     ScsiTargetKey,
					     NULL);
		  Status = ZwCreateKey(&ScsiLunKey,
				       KEY_ALL_ACCESS,
				       &ObjectAttributes,
				       0,
				       NULL,
				       REG_OPTION_VOLATILE,
				       &Disposition);
		  if (!NT_SUCCESS(Status))
		    {
		      DPRINT("ZwCreateKey() failed (Status %lx)\n", Status);
		      goto ByeBye;
		    }

		  /* Set 'Identifier' (REG_SZ) value */
		  swprintf(NameBuffer,
			   L"%.8S%.16S%.4S",
			   LunExtension->InquiryData.VendorId,
			   LunExtension->InquiryData.ProductId,
			   LunExtension->InquiryData.ProductRevisionLevel);
		  DPRINT("          Identifier = '%S'\n", NameBuffer);
		  RtlInitUnicodeString(&ValueName,
				       L"Identifier");
		  Status = ZwSetValueKey(ScsiLunKey,
					 &ValueName,
					 0,
					 REG_SZ,
					 NameBuffer,
					 (ULONG)((wcslen(NameBuffer) + 1) * sizeof(WCHAR)));
		  if (!NT_SUCCESS(Status))
		    {
		      DPRINT("ZwSetValueKey('Identifier') failed (Status %lx)\n", Status);
		      goto ByeBye;
		    }

		  /* Set 'Type' (REG_SZ) value */
		  switch (LunExtension->InquiryData.DeviceType)
		    {
		      case 0:
			TypeName = L"DiskPeripheral";
			break;
		      case 1:
			TypeName = L"TapePeripheral";
			break;
		      case 2:
			TypeName = L"PrinterPeripheral";
			break;
		      case 4:
			TypeName = L"WormPeripheral";
			break;
		      case 5:
			TypeName = L"CdRomPeripheral";
			break;
		      case 6:
			TypeName = L"ScannerPeripheral";
			break;
		      case 7:
			TypeName = L"OpticalDiskPeripheral";
			break;
		      case 8:
			TypeName = L"MediumChangerPeripheral";
			break;
		      case 9:
			TypeName = L"CommunicationPeripheral";
			break;
		      default:
			TypeName = L"OtherPeripheral";
			break;
		    }
		  DPRINT("          Type = '%S'\n", TypeName);
		  RtlInitUnicodeString(&ValueName,
				       L"Type");
		  Status = ZwSetValueKey(ScsiLunKey,
					 &ValueName,
					 0,
					 REG_SZ,
					 TypeName,
					 (ULONG)((wcslen(TypeName) + 1) * sizeof(WCHAR)));
		  if (!NT_SUCCESS(Status))
		    {
		      DPRINT("ZwSetValueKey('Type') failed (Status %lx)\n", Status);
		      goto ByeBye;
		    }

		  ZwClose(ScsiLunKey);
		  ScsiLunKey = NULL;
		}
	    }

	  /* Close old target key */
	  if (ScsiTargetKey != NULL)
	    {
	      ZwClose(ScsiTargetKey);
	      ScsiTargetKey = NULL;
	    }
	}

      ZwClose(ScsiBusKey);
      ScsiBusKey = NULL;
    }

ByeBye:
  if (ScsiLunKey != NULL)
    ZwClose (ScsiLunKey);

  if (ScsiInitiatorKey != NULL)
    ZwClose (ScsiInitiatorKey);

  if (ScsiTargetKey != NULL)
    ZwClose (ScsiTargetKey);

  if (ScsiBusKey != NULL)
    ZwClose (ScsiBusKey);

  if (ScsiPortKey != NULL)
    ZwClose (ScsiPortKey);

  DPRINT("SpiBuildDeviceMap() done\n");

  return Status;
}

VOID
NTAPI
SpiMiniportTimerDpc(IN struct _KDPC *Dpc,
                    IN PVOID DeviceObject,
                    IN PVOID SystemArgument1,
                    IN PVOID SystemArgument2)
{
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension;

    DPRINT("Miniport timer DPC\n");

    DeviceExtension = ((PDEVICE_OBJECT)DeviceObject)->DeviceExtension;

    /* Acquire the spinlock */
    KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);

    /* Call the timer routine */
    if (DeviceExtension->HwScsiTimer != NULL)
    {
        DeviceExtension->HwScsiTimer(&DeviceExtension->MiniPortDeviceExtension);
    }

    /* Release the spinlock */
    KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);

    if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)
    {
        ScsiPortDpcForIsr(NULL,
                          DeviceExtension->DeviceObject,
                          NULL,
                          NULL);
    }
}

static NTSTATUS
SpiCreatePortConfig(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                    PHW_INITIALIZATION_DATA HwInitData,
                    PCONFIGURATION_INFO InternalConfigInfo,
                    PPORT_CONFIGURATION_INFORMATION ConfigInfo,
                    BOOLEAN ZeroStruct)
{
    UNICODE_STRING UnicodeString;
    OBJECT_ATTRIBUTES ObjectAttributes;
    PCONFIGURATION_INFORMATION DdkConfigInformation;
    HANDLE RootKey, Key;
    BOOLEAN Found;
    WCHAR DeviceBuffer[16];
    WCHAR StrBuffer[512];
    ULONG Bus;
    NTSTATUS Status;

    /* Zero out the struct if told so */
    if (ZeroStruct)
    {
        /* First zero the portconfig */
        RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION));

        /* Then access ranges */
        RtlZeroMemory(InternalConfigInfo->AccessRanges,
                      HwInitData->NumberOfAccessRanges * sizeof(ACCESS_RANGE));

        /* Initialize the struct */
        ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION);
        ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType;
        ConfigInfo->InterruptMode = Latched;
        ConfigInfo->DmaChannel = SP_UNINITIALIZED_VALUE;
        ConfigInfo->DmaPort = SP_UNINITIALIZED_VALUE;
        ConfigInfo->DmaChannel2 = SP_UNINITIALIZED_VALUE;
        ConfigInfo->DmaPort2 = SP_UNINITIALIZED_VALUE;
        ConfigInfo->MaximumTransferLength = SP_UNINITIALIZED_VALUE;
        ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges;
        ConfigInfo->MaximumNumberOfTargets = 8;

        /* Store parameters */
        ConfigInfo->NeedPhysicalAddresses = HwInitData->NeedPhysicalAddresses;
        ConfigInfo->MapBuffers = HwInitData->MapBuffers;
        ConfigInfo->AutoRequestSense = HwInitData->AutoRequestSense;
        ConfigInfo->ReceiveEvent = HwInitData->ReceiveEvent;
        ConfigInfo->TaggedQueuing = HwInitData->TaggedQueuing;
        ConfigInfo->MultipleRequestPerLu = HwInitData->MultipleRequestPerLu;

        /* Get the disk usage */
        DdkConfigInformation = IoGetConfigurationInformation();
        ConfigInfo->AtdiskPrimaryClaimed = DdkConfigInformation->AtDiskPrimaryAddressClaimed;
        ConfigInfo->AtdiskSecondaryClaimed = DdkConfigInformation->AtDiskSecondaryAddressClaimed;

        /* Initiator bus id is not set */
        for (Bus = 0; Bus < 8; Bus++)
            ConfigInfo->InitiatorBusId[Bus] = (CCHAR)SP_UNINITIALIZED_VALUE;
    }

    ConfigInfo->NumberOfPhysicalBreaks = 17;

    /* Clear this information */
    InternalConfigInfo->DisableTaggedQueueing = FALSE;
    InternalConfigInfo->DisableMultipleLun = FALSE;

    /* Store Bus Number */
    ConfigInfo->SystemIoBusNumber = InternalConfigInfo->BusNumber;

TryNextAd:

    if (ConfigInfo->AdapterInterfaceType == Internal)
    {
        /* Open registry key for HW database */
        InitializeObjectAttributes(&ObjectAttributes,
                                   DeviceExtension->DeviceObject->DriverObject->HardwareDatabase,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);

        Status = ZwOpenKey(&RootKey,
                           KEY_READ,
                           &ObjectAttributes);

        if (NT_SUCCESS(Status))
        {
            /* Create name for it */
            swprintf(StrBuffer, L"ScsiAdapter\\%lu",
                InternalConfigInfo->AdapterNumber);

            RtlInitUnicodeString(&UnicodeString, StrBuffer);

            /* Open device key */
            InitializeObjectAttributes(&ObjectAttributes,
                                       &UnicodeString,
                                       OBJ_CASE_INSENSITIVE,
                                       RootKey,
                                       NULL);

            Status = ZwOpenKey(&Key,
                               KEY_READ,
                               &ObjectAttributes);

            ZwClose(RootKey);

            if (NT_SUCCESS(Status))
            {
                if (InternalConfigInfo->LastAdapterNumber != InternalConfigInfo->AdapterNumber)
                {
                    DPRINT("Hardware info found at %S\n", StrBuffer);

                    /* Parse it */
                    SpiParseDeviceInfo(DeviceExtension,
                                       Key,
                                       ConfigInfo,
                                       InternalConfigInfo,
                                       (PUCHAR)StrBuffer);

                     InternalConfigInfo->BusNumber = 0;
                }
                else
                {
                    /* Try the next adapter */
                    InternalConfigInfo->AdapterNumber++;
                    goto TryNextAd;
                }
            }
            else
            {
                /* Info was not found, exit */
                DPRINT1("ZwOpenKey() failed with Status=0x%08X\n", Status);
                return STATUS_DEVICE_DOES_NOT_EXIST;
            }
        }
        else
        {
            DPRINT1("ZwOpenKey() failed with Status=0x%08X\n", Status);
        }
    }

    /* Look at device params */
    Key = NULL;
    if (InternalConfigInfo->Parameter)
    {
        ExFreePool(InternalConfigInfo->Parameter);
        InternalConfigInfo->Parameter = NULL;
    }

    if (InternalConfigInfo->ServiceKey != NULL)
    {
        swprintf(DeviceBuffer, L"Device%lu", InternalConfigInfo->AdapterNumber);
        RtlInitUnicodeString(&UnicodeString, DeviceBuffer);

        /* Open the service key */
        InitializeObjectAttributes(&ObjectAttributes,
                                   &UnicodeString,
                                   OBJ_CASE_INSENSITIVE,
                                   InternalConfigInfo->ServiceKey,
                                   NULL);

        Status = ZwOpenKey(&Key,
                           KEY_READ,
                           &ObjectAttributes);
    }

    /* Parse device key */
    if (InternalConfigInfo->DeviceKey != NULL)
    {
        SpiParseDeviceInfo(DeviceExtension,
                           InternalConfigInfo->DeviceKey,
                           ConfigInfo,
                           InternalConfigInfo,
                           (PUCHAR)StrBuffer);
    }

    /* Then parse hw info */
    if (Key != NULL)
    {
        if (InternalConfigInfo->LastAdapterNumber != InternalConfigInfo->AdapterNumber)
        {
            SpiParseDeviceInfo(DeviceExtension,
                               Key,
                               ConfigInfo,
                               InternalConfigInfo,
                               (PUCHAR)StrBuffer);

            /* Close the key */
            ZwClose(Key);
        }
        else
        {
            /* Adapter not found, go try the next one */
            InternalConfigInfo->AdapterNumber++;

            /* Close the key */
            ZwClose(Key);

            goto TryNextAd;
        }
    }

    /* Update the last adapter number */
    InternalConfigInfo->LastAdapterNumber = InternalConfigInfo->AdapterNumber;

    /* Do we have this kind of bus at all? */
    Found = FALSE;
    Status = IoQueryDeviceDescription(&HwInitData->AdapterInterfaceType,
                                      &InternalConfigInfo->BusNumber,
                                      NULL,
                                      NULL,
                                      NULL,
                                      NULL,
                                      SpQueryDeviceCallout,
                                      &Found);

    /* This bus was not found */
    if (!Found)
    {
        INTERFACE_TYPE InterfaceType = Eisa;

        /* Check for EISA */
        if (HwInitData->AdapterInterfaceType == Isa)
        {
            Status = IoQueryDeviceDescription(&InterfaceType,
                                              &InternalConfigInfo->BusNumber,
                                              NULL,
                                              NULL,
                                              NULL,
                                              NULL,
                                              SpQueryDeviceCallout,
                                              &Found);

            /* Return respectively */
            if (Found)
                return STATUS_SUCCESS;
            else
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }
        else
        {
            return STATUS_DEVICE_DOES_NOT_EXIST;
        }
    }
    else
    {
        return STATUS_SUCCESS;
    }
}

static VOID
SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
                   IN HANDLE Key,
                   IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
                   IN PCONFIGURATION_INFO InternalConfigInfo,
                   IN PUCHAR Buffer)
{
    PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
    PCM_FULL_RESOURCE_DESCRIPTOR FullResource;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
    PCM_SCSI_DEVICE_DATA ScsiDeviceData;
    ULONG Length, Count, Dma = 0, Interrupt = 0;
    ULONG Index = 0, RangeCount = 0;
    UNICODE_STRING UnicodeString;
    ANSI_STRING AnsiString;
    NTSTATUS Status = STATUS_SUCCESS;


    KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;

    /* Loop through all values in the device node */
    while(TRUE)
    {
        Status = ZwEnumerateValueKey(Key,
                                     Index,
                                     KeyValueFullInformation,
                                     Buffer,
                                     512,
                                     &Length);

        if (!NT_SUCCESS(Status))
            return;

        Index++;

        /* Length for DWORD is ok? */
        if (KeyValueInformation->Type == REG_DWORD &&
            KeyValueInformation->DataLength != sizeof(ULONG))
        {
            continue;
        }

        /* Get MaximumLogicalUnit */
        if (_wcsnicmp(KeyValueInformation->Name, L"MaximumLogicalUnit",
            KeyValueInformation->NameLength/2) == 0)
        {

            if (KeyValueInformation->Type != REG_DWORD)
            {
                DPRINT("Bad data type for MaximumLogicalUnit\n");
                continue;
            }

            DeviceExtension->MaxLunCount = *((PUCHAR)
                (Buffer + KeyValueInformation->DataOffset));

            /* Check / reset if needed */
            if (DeviceExtension->MaxLunCount > SCSI_MAXIMUM_LOGICAL_UNITS)
                DeviceExtension->MaxLunCount = SCSI_MAXIMUM_LOGICAL_UNITS;

            DPRINT("MaximumLogicalUnit = %d\n", DeviceExtension->MaxLunCount);
        }

        /* Get InitiatorTargetId */
        if (_wcsnicmp(KeyValueInformation->Name, L"InitiatorTargetId",
            KeyValueInformation->NameLength / 2) == 0)
        {

            if (KeyValueInformation->Type != REG_DWORD)
            {
                DPRINT("Bad data type for InitiatorTargetId\n");
                continue;
            }

            ConfigInfo->InitiatorBusId[0] = *((PUCHAR)
                (Buffer + KeyValueInformation->DataOffset));

            /* Check / reset if needed */
            if (ConfigInfo->InitiatorBusId[0] > ConfigInfo->MaximumNumberOfTargets - 1)
                ConfigInfo->InitiatorBusId[0] = (CCHAR)-1;

            DPRINT("InitiatorTargetId = %d\n", ConfigInfo->InitiatorBusId[0]);
        }

        /* Get ScsiDebug */
        if (_wcsnicmp(KeyValueInformation->Name, L"ScsiDebug",
            KeyValueInformation->NameLength/2) == 0)
        {
            DPRINT("ScsiDebug key not supported\n");
        }

        /* Check for a breakpoint */
        if (_wcsnicmp(KeyValueInformation->Name, L"BreakPointOnEntry",
            KeyValueInformation->NameLength/2) == 0)
        {
            DPRINT1("Breakpoint on entry requested!\n");
            DbgBreakPoint();
        }

        /* Get DisableSynchronousTransfers */
        if (_wcsnicmp(KeyValueInformation->Name, L"DisableSynchronousTransfers",
            KeyValueInformation->NameLength/2) == 0)
        {
            DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
            DPRINT("Synch transfers disabled\n");
        }

        /* Get DisableDisconnects */
        if (_wcsnicmp(KeyValueInformation->Name, L"DisableDisconnects",
            KeyValueInformation->NameLength/2) == 0)
        {
            DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT;
            DPRINT("Disconnects disabled\n");
        }

        /* Get DisableTaggedQueuing */
        if (_wcsnicmp(KeyValueInformation->Name, L"DisableTaggedQueuing",
            KeyValueInformation->NameLength/2) == 0)
        {
            InternalConfigInfo->DisableTaggedQueueing = TRUE;
            DPRINT("Tagged queueing disabled\n");
        }

        /* Get DisableMultipleRequests */
        if (_wcsnicmp(KeyValueInformation->Name, L"DisableMultipleRequests",
            KeyValueInformation->NameLength/2) == 0)
        {
            InternalConfigInfo->DisableMultipleLun = TRUE;
            DPRINT("Multiple requests disabled\n");
        }

        /* Get DriverParameters */
        if (_wcsnicmp(KeyValueInformation->Name, L"DriverParameters",
            KeyValueInformation->NameLength/2) == 0)
        {
            /* Skip if nothing */
            if (KeyValueInformation->DataLength == 0)
                continue;

            /* If there was something previously allocated - free it */
            if (InternalConfigInfo->Parameter != NULL)
                ExFreePool(InternalConfigInfo->Parameter);

            /* Allocate it */
            InternalConfigInfo->Parameter = ExAllocatePoolWithTag(NonPagedPool,
                                                           KeyValueInformation->DataLength, TAG_SCSIPORT);

            if (InternalConfigInfo->Parameter != NULL)
            {
                if (KeyValueInformation->Type != REG_SZ)
                {
                    /* Just copy */
                    RtlCopyMemory(
                        InternalConfigInfo->Parameter,
                        (PCCHAR)KeyValueInformation + KeyValueInformation->DataOffset,
                        KeyValueInformation->DataLength);
                }
                else
                {
                    /* If it's a unicode string, convert it to ansi */
                    UnicodeString.Length = (USHORT)KeyValueInformation->DataLength;
                    UnicodeString.MaximumLength = (USHORT)KeyValueInformation->DataLength;
                    UnicodeString.Buffer =
                        (PWSTR)((PCCHAR)KeyValueInformation + KeyValueInformation->DataOffset);

                    AnsiString.Length = 0;
                    AnsiString.MaximumLength = (USHORT)KeyValueInformation->DataLength;
                    AnsiString.Buffer = (PCHAR)InternalConfigInfo->Parameter;

                    Status = RtlUnicodeStringToAnsiString(&AnsiString,
                                                          &UnicodeString,
                                                          FALSE);

                    /* In case of error, free the allocated space */
                    if (!NT_SUCCESS(Status))
                    {
                        ExFreePool(InternalConfigInfo->Parameter);
                        InternalConfigInfo->Parameter = NULL;
                    }

                }
            }

            DPRINT("Found driver parameter\n");
        }

        /* Get MaximumSGList */
        if (_wcsnicmp(KeyValueInformation->Name, L"MaximumSGList",
            KeyValueInformation->NameLength/2) == 0)
        {
            if (KeyValueInformation->Type != REG_DWORD)
            {
                DPRINT("Bad data type for MaximumSGList\n");
                continue;
            }

            ConfigInfo->NumberOfPhysicalBreaks = *((PUCHAR)(Buffer + KeyValueInformation->DataOffset));

            /* Check / fix */
            if (ConfigInfo->NumberOfPhysicalBreaks > SCSI_MAXIMUM_PHYSICAL_BREAKS)
            {
                ConfigInfo->NumberOfPhysicalBreaks = SCSI_MAXIMUM_PHYSICAL_BREAKS;
            }
            else if (ConfigInfo->NumberOfPhysicalBreaks < SCSI_MINIMUM_PHYSICAL_BREAKS)
            {
                ConfigInfo->NumberOfPhysicalBreaks = SCSI_MINIMUM_PHYSICAL_BREAKS;
            }

            DPRINT("MaximumSGList = %d\n", ConfigInfo->NumberOfPhysicalBreaks);
        }

        /* Get NumberOfRequests */
        if (_wcsnicmp(KeyValueInformation->Name, L"NumberOfRequests",
            KeyValueInformation->NameLength/2) == 0)
        {
            if (KeyValueInformation->Type != REG_DWORD)
            {
                DPRINT("NumberOfRequests has wrong data type\n");
                continue;
            }

            DeviceExtension->RequestsNumber = *((PUCHAR)(Buffer + KeyValueInformation->DataOffset));

            /* Check / fix */
            if (DeviceExtension->RequestsNumber < 16)
            {
                DeviceExtension->RequestsNumber = 16;
            }
            else if (DeviceExtension->RequestsNumber > 512)
            {
                DeviceExtension->RequestsNumber = 512;
            }

            DPRINT("Number Of Requests = %d\n", DeviceExtension->RequestsNumber);
        }

        /* Get resource list */
        if (_wcsnicmp(KeyValueInformation->Name, L"ResourceList",
                KeyValueInformation->NameLength/2) == 0 ||
            _wcsnicmp(KeyValueInformation->Name, L"Configuration Data",
                KeyValueInformation->NameLength/2) == 0 )
        {
            if (KeyValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR ||
                KeyValueInformation->DataLength < sizeof(REG_FULL_RESOURCE_DESCRIPTOR))
            {
                DPRINT("Bad data type for ResourceList\n");
                continue;
            }
            else
            {
                DPRINT("Found ResourceList\n");
            }

            FullResource = (PCM_FULL_RESOURCE_DESCRIPTOR)(Buffer + KeyValueInformation->DataOffset);

            /* Copy some info from it */
            InternalConfigInfo->BusNumber = FullResource->BusNumber;
            ConfigInfo->SystemIoBusNumber = FullResource->BusNumber;

            /* Loop through it */
            for (Count = 0; Count < FullResource->PartialResourceList.Count; Count++)
            {
                /* Get partial descriptor */
                PartialDescriptor =
                    &FullResource->PartialResourceList.PartialDescriptors[Count];

                /* Check datalength */
                if ((ULONG)((PCHAR)(PartialDescriptor + 1) -
                    (PCHAR)FullResource) > KeyValueInformation->DataLength)
                {
                    DPRINT("Resource data is of incorrect size\n");
                    break;
                }

                switch (PartialDescriptor->Type)
                {
                case CmResourceTypePort:
                    if (RangeCount >= ConfigInfo->NumberOfAccessRanges)
                    {
                        DPRINT("Too many access ranges\n");
                        continue;
                    }

                    InternalConfigInfo->AccessRanges[RangeCount].RangeInMemory = FALSE;
                    InternalConfigInfo->AccessRanges[RangeCount].RangeStart = PartialDescriptor->u.Port.Start;
                    InternalConfigInfo->AccessRanges[RangeCount].RangeLength = PartialDescriptor->u.Port.Length;
                    RangeCount++;

                    break;

                case CmResourceTypeMemory:
                    if (RangeCount >= ConfigInfo->NumberOfAccessRanges)
                    {
                        DPRINT("Too many access ranges\n");
                        continue;
                    }

                    InternalConfigInfo->AccessRanges[RangeCount].RangeInMemory = TRUE;
                    InternalConfigInfo->AccessRanges[RangeCount].RangeStart = PartialDescriptor->u.Memory.Start;
                    InternalConfigInfo->AccessRanges[RangeCount].RangeLength = PartialDescriptor->u.Memory.Length;
                    RangeCount++;

                    break;

                case CmResourceTypeInterrupt:

                    if (Interrupt == 0)
                    {
                        ConfigInfo->BusInterruptLevel =
                            PartialDescriptor->u.Interrupt.Level;

                        ConfigInfo->BusInterruptVector =
                            PartialDescriptor->u.Interrupt.Vector;

                        ConfigInfo->InterruptMode = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
                    }
                    else if (Interrupt == 1)
                    {
                        ConfigInfo->BusInterruptLevel2 =
                        PartialDescriptor->u.Interrupt.Level;

                        ConfigInfo->BusInterruptVector2 =
                        PartialDescriptor->u.Interrupt.Vector;

                        ConfigInfo->InterruptMode2 = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
                    }

                    Interrupt++;
                    break;

                case CmResourceTypeDma:

                    if (Dma == 0)
                    {
                        ConfigInfo->DmaChannel = PartialDescriptor->u.Dma.Channel;
                        ConfigInfo->DmaPort = PartialDescriptor->u.Dma.Port;

                        if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8)
                            ConfigInfo->DmaWidth = Width8Bits;
                        else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) ||
                                 (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //???
                            ConfigInfo->DmaWidth = Width16Bits;
                        else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32)
                            ConfigInfo->DmaWidth = Width32Bits;
                    }
                    else if (Dma == 1)
                    {
                        ConfigInfo->DmaChannel2 = PartialDescriptor->u.Dma.Channel;
                        ConfigInfo->DmaPort2 = PartialDescriptor->u.Dma.Port;

                        if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8)
                            ConfigInfo->DmaWidth2 = Width8Bits;
                        else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) ||
                                 (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //???
                            ConfigInfo->DmaWidth2 = Width16Bits;
                        else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32)
                            ConfigInfo->DmaWidth2 = Width32Bits;
                    }

                    Dma++;
                    break;

                case CmResourceTypeDeviceSpecific:
                    if (PartialDescriptor->u.DeviceSpecificData.DataSize <
                        sizeof(CM_SCSI_DEVICE_DATA) ||
                        (PCHAR) (PartialDescriptor + 1) - (PCHAR)FullResource +
                        PartialDescriptor->u.DeviceSpecificData.DataSize >
                        KeyValueInformation->DataLength)
                    {
                        DPRINT("Resource data length is incorrect");
                        break;
                    }

                    /* Set only one field from it */
                    ScsiDeviceData = (PCM_SCSI_DEVICE_DATA) (PartialDescriptor+1);
                    ConfigInfo->InitiatorBusId[0] = ScsiDeviceData->HostIdentifier;
                    break;
                }
            }
        }
    }
}

NTSTATUS
NTAPI
SpQueryDeviceCallout(IN PVOID  Context,
                     IN PUNICODE_STRING  PathName,
                     IN INTERFACE_TYPE  BusType,
                     IN ULONG  BusNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *BusInformation,
                     IN CONFIGURATION_TYPE  ControllerType,
                     IN ULONG  ControllerNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *ControllerInformation,
                     IN CONFIGURATION_TYPE  PeripheralType,
                     IN ULONG  PeripheralNumber,
                     IN PKEY_VALUE_FULL_INFORMATION  *PeripheralInformation)
{
    PBOOLEAN Found = (PBOOLEAN)Context;
    /* We just set our Found variable to TRUE */

    *Found = TRUE;
    return STATUS_SUCCESS;
}

IO_ALLOCATION_ACTION
NTAPI
ScsiPortAllocateAdapterChannel(IN PDEVICE_OBJECT DeviceObject,
                               IN PIRP Irp,
                               IN PVOID MapRegisterBase,
                               IN PVOID Context)
{
    KIRQL Irql;
    PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;

    /* Guard access with the spinlock */
    KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);

    /* Save MapRegisterBase we've got here */
    DeviceExtension->MapRegisterBase = MapRegisterBase;

    /* Start pending request */
    KeSynchronizeExecution(DeviceExtension->Interrupt[0],
        ScsiPortStartPacket, DeviceObject);

    /* Release spinlock we took */
    KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql);

    return KeepObject;
}

static
NTSTATUS
SpiStatusSrbToNt(UCHAR SrbStatus)
{
    switch (SRB_STATUS(SrbStatus))
    {
    case SRB_STATUS_TIMEOUT:
    case SRB_STATUS_COMMAND_TIMEOUT:
        return STATUS_IO_TIMEOUT;

    case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
    case SRB_STATUS_BAD_FUNCTION:
        return STATUS_INVALID_DEVICE_REQUEST;

    case SRB_STATUS_NO_DEVICE:
    case SRB_STATUS_INVALID_LUN:
    case SRB_STATUS_INVALID_TARGET_ID:
    case SRB_STATUS_NO_HBA:
        return STATUS_DEVICE_DOES_NOT_EXIST;

    case SRB_STATUS_DATA_OVERRUN:
        return STATUS_BUFFER_OVERFLOW;

    case SRB_STATUS_SELECTION_TIMEOUT:
        return STATUS_DEVICE_NOT_CONNECTED;

    default:
        return STATUS_IO_DEVICE_ERROR;
    }

    return STATUS_IO_DEVICE_ERROR;
}


#undef ScsiPortConvertPhysicalAddressToUlong
/*
 * @implemented
 */
ULONG NTAPI
ScsiPortConvertPhysicalAddressToUlong(IN SCSI_PHYSICAL_ADDRESS Address)
{
  DPRINT("ScsiPortConvertPhysicalAddressToUlong()\n");
  return(Address.u.LowPart);
}

/* EOF */
