reactos/drivers/storage/scsiport/scsiport.c

6454 lines
210 KiB
C

/*
* 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 OUT 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(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
IN 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 */
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 remaining 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++)
{
/* Determine 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 alright */
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;
/* Release 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 OUT 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)
{
ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
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");
/* Quit the loop */
Status = STATUS_INSUFFICIENT_RESOURCES;
KeepTrying = FALSE;
continue;
}
/* 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);
/* Quit the loop */
Status = STATUS_SUCCESS;
KeepTrying = FALSE;
continue;
}
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);
/* Quit the loop */
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 */
/* Quit the loop */
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, quit the loop */
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 */
ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT);
ExFreePoolWithTag(SenseBuffer, TAG_SCSIPORT);
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)
{
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);
/*
* Cache the inquiry data into the LUN extension (or alternatively
* we could save a pointer to LunInfo within the LunExtension?)
*/
RtlCopyMemory(&LunExtension->InquiryData,
InquiryData,
INQUIRYDATABUFFERSIZE);
/* 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)
ExFreePoolWithTag(LunExtension, TAG_SCSIPORT);
if (LunInfo)
ExFreePoolWithTag(LunInfo, TAG_SCSIPORT);
/* 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,
IN 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);
/* Requeue, 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("Requeuing 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 unsuccessful*/
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(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension,
IN 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(UlongData));
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(UlongData));
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(UlongData));
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 %lu\n",
DeviceExtension->PortConfig->InitiatorBusId[BusNumber]);
swprintf(NameBuffer,
L"Initiator Id %lu",
(ULONG)(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)
continue;
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 */
/*
* See https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-ide-devices
* and https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-scsi-devices
* for a list of types with their human-readable forms.
*/
switch (LunExtension->InquiryData.DeviceType)
{
case 0:
TypeName = L"DiskPeripheral";
break;
case 1:
TypeName = L"TapePeripheral";
break;
case 2:
TypeName = L"PrinterPeripheral";
break;
// case 3: "ProcessorPeripheral", classified as 'other': fall back to default case.
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"CommunicationsPeripheral";
break;
/* New peripheral types (SCSI only) */
case 10: case 11:
TypeName = L"ASCPrePressGraphicsPeripheral";
break;
case 12:
TypeName = L"ArrayPeripheral";
break;
case 13:
TypeName = L"EnclosurePeripheral";
break;
case 14:
TypeName = L"RBCPeripheral";
break;
case 15:
TypeName = L"CardReaderPeripheral";
break;
case 16:
TypeName = L"BridgePeripheral";
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 */