mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4556 lines
124 KiB
4556 lines
124 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
enum.c
|
|
|
|
Abstract:
|
|
|
|
This module contains device enumeration code for the scsi port driver
|
|
|
|
Authors:
|
|
|
|
Peter Wieland
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "port.h"
|
|
|
|
#define __FILE_ID__ 'enum'
|
|
|
|
ULONG EnumDebug = 2;
|
|
|
|
#if DBG
|
|
static const char *__file__ = __FILE__;
|
|
#endif
|
|
|
|
#define MINIMUM_BUS_SCAN_INTERVAL ((ULONGLONG) (30 * SECONDS))
|
|
|
|
ULONG BreakOnTarget = (ULONG) -1;
|
|
ULONG BreakOnScan = TRUE;
|
|
|
|
ULONG BreakOnMissingLun = FALSE;
|
|
|
|
typedef struct {
|
|
UCHAR LunListLength[4]; // sizeof LunSize * 8
|
|
UCHAR Reserved[4];
|
|
UCHAR Luns[16][8];
|
|
} SP_DEFAULT_LUN_LIST;
|
|
|
|
SP_DEFAULT_LUN_LIST ScsiPortDefaultLunList = {
|
|
{0, 0, 0, sizeof(ScsiPortDefaultLunList.Luns)}, // LunListLength
|
|
{0, 0, 0, 0}, // Reserved
|
|
{{ 0, 0, 0, 0, 0, 0, 0, 0}, // Luns
|
|
{ 0, 1, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 2, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 3, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 4, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 5, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 6, 0, 0, 0, 0, 0, 0},
|
|
{ 0, 7, 0, 0, 0, 0, 0, 0}}};
|
|
|
|
NTSTATUS
|
|
SpInquireLogicalUnit(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN OPTIONAL PLOGICAL_UNIT_EXTENSION RescanLun,
|
|
OUT PLOGICAL_UNIT_EXTENSION *LogicalUnit,
|
|
OUT PBOOLEAN CheckNextLun
|
|
);
|
|
|
|
VOID
|
|
SpSignalEnumerationCompletion (
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSP_ENUMERATION_REQUEST Request,
|
|
IN NTSTATUS Status
|
|
);
|
|
|
|
BOOLEAN
|
|
SpRemoveLogicalUnitFromBinSynchronized(
|
|
IN PVOID ServiceContext // PLOGICAL_UNIT_EXTENSION
|
|
);
|
|
|
|
BOOLEAN
|
|
SpAddLogicalUnitToBinSynchronized(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnitExtension
|
|
);
|
|
|
|
ULONG
|
|
SpCountLogicalUnits(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
NTSTATUS
|
|
IssueReportLuns(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
OUT PLUN_LIST *LunList
|
|
);
|
|
|
|
PLUN_LIST
|
|
AdjustReportLuns(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PLUN_LIST RawList
|
|
);
|
|
|
|
VOID
|
|
SpScanAdapter(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
NTSTATUS
|
|
SpScanBus(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun
|
|
);
|
|
|
|
NTSTATUS
|
|
SpScanTarget(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun
|
|
);
|
|
|
|
NTSTATUS
|
|
IssueInquiry(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN BOOLEAN EnableVitalProductData,
|
|
IN UCHAR PageCode,
|
|
OUT PVOID InquiryData,
|
|
OUT PUCHAR BytesReturned
|
|
);
|
|
|
|
VOID
|
|
SpSetVerificationMarks(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId
|
|
);
|
|
|
|
VOID
|
|
SpClearVerificationMark(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
);
|
|
|
|
VOID
|
|
SpPurgeTarget(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId
|
|
);
|
|
|
|
NTSTATUS
|
|
SpCloneAndSwapLogicalUnit(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN PINQUIRYDATA InquiryData,
|
|
IN ULONG InquiryDataSize,
|
|
OUT PLOGICAL_UNIT_EXTENSION *NewLun
|
|
);
|
|
|
|
VOID
|
|
SpSetLogicalUnitAddress(
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
);
|
|
|
|
VOID
|
|
SpClearLogicalUnitAddress(
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun
|
|
);
|
|
|
|
NTSTATUS
|
|
SpPrepareLogicalUnitForReuse(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
);
|
|
|
|
NTSTATUS
|
|
SpCreateLogicalUnit(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN OPTIONAL UCHAR PathId,
|
|
IN OPTIONAL UCHAR TargetId,
|
|
IN OPTIONAL UCHAR Lun,
|
|
IN BOOLEAN Temporary,
|
|
IN BOOLEAN Scsi1,
|
|
OUT PLOGICAL_UNIT_EXTENSION *NewLun
|
|
);
|
|
|
|
NTSTATUS
|
|
SpSendSrbSynchronous(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN OPTIONAL PIRP Irp,
|
|
IN OPTIONAL PMDL Mdl,
|
|
IN PVOID DataBuffer,
|
|
IN ULONG TransferLength,
|
|
IN PVOID SenseInfoBuffer,
|
|
IN UCHAR SenseInfoBufferLength,
|
|
OUT PULONG BytesReturned
|
|
);
|
|
|
|
BOOLEAN
|
|
SpGetDeviceIdentifiers(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN BOOLEAN NewDevice
|
|
);
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
SpCompareInquiryData(
|
|
IN PUCHAR InquiryData1,
|
|
IN PUCHAR InquiryData2
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SpEnumerateAdapterSynchronous)
|
|
#pragma alloc_text(PAGE, SpEnumerateAdapterAsynchronous)
|
|
#pragma alloc_text(PAGE, SpSignalEnumerationCompletion)
|
|
#pragma alloc_text(PAGE, SpEnumerationWorker)
|
|
|
|
#pragma alloc_text(PAGE, SpScanAdapter)
|
|
#pragma alloc_text(PAGE, SpScanBus)
|
|
#pragma alloc_text(PAGE, SpScanTarget)
|
|
|
|
#pragma alloc_text(PAGE, SpCompareInquiryData)
|
|
#pragma alloc_text(PAGE, SpInquireLogicalUnit)
|
|
#pragma alloc_text(PAGE, SpExtractDeviceRelations)
|
|
|
|
#pragma alloc_text(PAGELOCK, SpCountLogicalUnits)
|
|
#pragma alloc_text(PAGELOCK, GetNextLuRequestWithoutLock)
|
|
#pragma alloc_text(PAGELOCK, IssueReportLuns)
|
|
|
|
#pragma alloc_text(PAGELOCK, SpSetVerificationMarks)
|
|
#pragma alloc_text(PAGELOCK, SpPurgeTarget)
|
|
|
|
#pragma alloc_text(PAGE, SpClearVerificationMark)
|
|
|
|
#pragma alloc_text(PAGE, SpGetInquiryData)
|
|
#pragma alloc_text(PAGE, IssueInquiry)
|
|
|
|
#pragma alloc_text(PAGE, AdjustReportLuns)
|
|
|
|
#pragma alloc_text(PAGE, SpCreateLogicalUnit)
|
|
#pragma alloc_text(PAGE, SpCloneAndSwapLogicalUnit)
|
|
#pragma alloc_text(PAGE, SpSetLogicalUnitAddress)
|
|
#pragma alloc_text(PAGE, SpClearLogicalUnitAddress)
|
|
#pragma alloc_text(PAGE, SpPrepareLogicalUnitForReuse)
|
|
|
|
#pragma alloc_text(PAGE, SpSendSrbSynchronous)
|
|
#pragma alloc_text(PAGE, SpGetDeviceIdentifiers)
|
|
|
|
LONG SpPAGELOCKLockCount = 0;
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
SpExtractDeviceRelations(
|
|
PADAPTER_EXTENSION Adapter,
|
|
DEVICE_RELATION_TYPE RelationType,
|
|
PDEVICE_RELATIONS *DeviceRelations
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will allocate a device relations structure and fill in the
|
|
count and object array with referenced object pointers
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter to extract relations from.
|
|
|
|
RelationType - what type of relationship is being retrieved
|
|
|
|
DeviceRelations - a place to store the relationships
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT fdo = Adapter->DeviceObject;
|
|
ULONG count = 0;
|
|
|
|
ULONG relationsSize;
|
|
PDEVICE_RELATIONS deviceRelations = NULL;
|
|
|
|
UCHAR bus, target, lun;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
|
|
ULONG i;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = KeWaitForMutexObject(
|
|
&(Adapter->EnumerationDeviceMutex),
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if(status == STATUS_USER_APC) {
|
|
status = STATUS_REQUEST_ABORTED;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Find out how many devices there are
|
|
//
|
|
|
|
for(bus = 0; bus < Adapter->NumberOfBuses; bus++) {
|
|
for(target = 0; target < Adapter->MaximumTargetIds; target++) {
|
|
for(lun = 0; lun < SCSI_MAXIMUM_LUNS_PER_TARGET; lun++) {
|
|
|
|
luExtension = GetLogicalUnitExtension(
|
|
Adapter,
|
|
bus,
|
|
target,
|
|
lun,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
if(luExtension == NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Temporary luns only exist while the bus scanning code is
|
|
// holding the device lock. we've got it now so we should
|
|
// never find one.
|
|
//
|
|
|
|
ASSERT(luExtension->IsTemporary == FALSE);
|
|
|
|
if(luExtension->IsMissing) {
|
|
continue;
|
|
}
|
|
|
|
if(luExtension->IsVisible == FALSE) {
|
|
continue;
|
|
}
|
|
|
|
if(luExtension->CommonExtension.IsRemoved >= REMOVE_COMPLETE) {
|
|
ASSERT(FALSE);
|
|
continue;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the structure
|
|
//
|
|
|
|
relationsSize = sizeof(DEVICE_RELATIONS) + (count * sizeof(PDEVICE_OBJECT));
|
|
|
|
deviceRelations = SpAllocatePool(PagedPool,
|
|
relationsSize,
|
|
SCSIPORT_TAG_DEVICE_RELATIONS,
|
|
fdo->DriverObject);
|
|
|
|
if(deviceRelations == NULL) {
|
|
|
|
DebugPrint((1, "SpExtractDeviceRelations: unable to allocate "
|
|
"%d bytes for device relations\n", relationsSize));
|
|
|
|
KeReleaseMutex(&(Adapter->EnumerationDeviceMutex), FALSE);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(deviceRelations, relationsSize);
|
|
|
|
i = 0;
|
|
|
|
for(bus = 0; bus < Adapter->NumberOfBuses; bus++) {
|
|
for(target = 0; target < Adapter->MaximumTargetIds; target++) {
|
|
for(lun = 0; lun < SCSI_MAXIMUM_LUNS_PER_TARGET; lun++) {
|
|
|
|
luExtension = GetLogicalUnitExtension(
|
|
Adapter,
|
|
bus,
|
|
target,
|
|
lun,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
if(luExtension == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Temporary luns only exist while the bus scanning code is
|
|
// holding the device lock. we've got it now so we should
|
|
// never find one.
|
|
//
|
|
|
|
ASSERT(luExtension->IsTemporary == FALSE);
|
|
|
|
if(luExtension->IsMissing) {
|
|
|
|
DebugPrint((1, "SpExtractDeviceRelations: logical unit "
|
|
"(%d,%d,%d) is missing and will not be "
|
|
"returned\n",
|
|
bus, target, lun));
|
|
|
|
luExtension->IsEnumerated = FALSE;
|
|
continue;
|
|
|
|
} else if(luExtension->CommonExtension.IsRemoved >= REMOVE_COMPLETE) {
|
|
|
|
ASSERT(FALSE);
|
|
luExtension->IsEnumerated = FALSE;
|
|
continue;
|
|
|
|
} else if(luExtension->IsVisible == FALSE) {
|
|
luExtension->IsEnumerated = FALSE;
|
|
continue;
|
|
}
|
|
|
|
status = ObReferenceObjectByPointer(
|
|
luExtension->CommonExtension.DeviceObject,
|
|
0,
|
|
NULL,
|
|
KernelMode);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1, "SpFdoExtractDeviceRelations: status %#08lx "
|
|
"while referenceing object %#p\n",
|
|
status,
|
|
deviceRelations->Objects[i]));
|
|
continue;
|
|
}
|
|
|
|
deviceRelations->Objects[i] =
|
|
luExtension->CommonExtension.DeviceObject;
|
|
|
|
i++;
|
|
luExtension->IsEnumerated = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
deviceRelations->Count = i;
|
|
*DeviceRelations = deviceRelations;
|
|
|
|
KeReleaseMutex(&(Adapter->EnumerationDeviceMutex), FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IssueReportLuns(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
OUT PLUN_LIST *LunList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build IRP, SRB and CDB for SCSI REPORT LUNS command.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - address of target's device object extension.
|
|
LunList - address of buffer for LUN_LIST information.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PMDL mdl;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
KEVENT event;
|
|
KIRQL currentIrql;
|
|
PLUN_LIST lunListDataBuffer;
|
|
PSENSE_DATA senseInfoBuffer = NULL;
|
|
NTSTATUS status;
|
|
ULONG retryCount = 0;
|
|
ULONG lunListSize;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if 0
|
|
if ((LogicalUnit->InquiryData.Versions & 7) < 3) {
|
|
|
|
//
|
|
// make sure the device supports scsi3 commands
|
|
// without this check, we may hang some scsi2 devices
|
|
//
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// start with the minilun of 16 byte for the lun list
|
|
//
|
|
lunListSize = 16;
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
senseInfoBuffer = LogicalUnit->AdapterExtension->InquirySenseBuffer;
|
|
irp = LogicalUnit->AdapterExtension->InquiryIrp;
|
|
mdl = NULL;
|
|
|
|
KeInitializeEvent(&event,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// This is a two pass operation - for the first pass we just try to figure
|
|
// out how large the list should be. On the second pass we'll actually
|
|
// reallocate the buffer and try to get the entire lun list.
|
|
//
|
|
// NOTE - we may want to set an arbitrary limit here so we don't soak up all
|
|
// of non-paged pool when some device hands us back a buffer filled
|
|
// with 0xff.
|
|
//
|
|
|
|
for (i=0; i<2; i++) {
|
|
|
|
//
|
|
// Allocate a cache aligned LUN_LIST structure.
|
|
//
|
|
|
|
lunListDataBuffer = SpAllocatePool(
|
|
NonPagedPoolCacheAligned,
|
|
lunListSize,
|
|
SCSIPORT_TAG_REPORT_LUNS,
|
|
LogicalUnit->DeviceObject->DriverObject);
|
|
|
|
if (lunListDataBuffer == NULL) {
|
|
|
|
DebugPrint((1,"IssueReportLuns: Can't allocate report luns data buffer\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
mdl = SpAllocateMdl(lunListDataBuffer,
|
|
lunListSize,
|
|
FALSE,
|
|
FALSE,
|
|
NULL,
|
|
LogicalUnit->DeviceObject->DriverObject);
|
|
|
|
if(mdl == NULL) {
|
|
DebugPrint((1,"IssueReportLuns: Can't allocate data buffer MDL\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool(mdl);
|
|
|
|
//
|
|
// number of retry
|
|
//
|
|
retryCount = 3;
|
|
while (retryCount--) {
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
//
|
|
|
|
IoInitializeIrp(irp,
|
|
IoSizeOfIrp(INQUIRY_STACK_LOCATIONS),
|
|
INQUIRY_STACK_LOCATIONS);
|
|
|
|
irp->MdlAddress = mdl;
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
//
|
|
// Mark the minor function to indicate that this is an internal scsiport
|
|
// request and that the start state of the device can be ignored.
|
|
//
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
irpStack->MinorFunction = 1;
|
|
|
|
irpStack->Parameters.Scsi.Srb = &srb;
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
SpSignalCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
srb.PathId = LogicalUnit->PathId;
|
|
srb.TargetId = LogicalUnit->TargetId;
|
|
srb.Lun = LogicalUnit->Lun;
|
|
|
|
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Set flags to disable synchronous negociation.
|
|
//
|
|
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
srb.SrbStatus = srb.ScsiStatus = 0;
|
|
|
|
srb.NextSrb = 0;
|
|
|
|
srb.OriginalRequest = irp;
|
|
|
|
//
|
|
// Set timeout to 2 seconds.
|
|
//
|
|
|
|
srb.TimeOutValue = 4;
|
|
|
|
srb.CdbLength = 12;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
srb.SenseInfoBuffer = senseInfoBuffer;
|
|
srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
srb.DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);
|
|
srb.DataTransferLength = lunListSize;
|
|
|
|
cdb = (PCDB)srb.Cdb;
|
|
|
|
//
|
|
// Set CDB operation code.
|
|
//
|
|
|
|
cdb->REPORT_LUNS.OperationCode = SCSIOP_REPORT_LUNS;
|
|
cdb->REPORT_LUNS.AllocationLength[0] = (UCHAR) ((lunListSize >> 24) & 0xff);
|
|
cdb->REPORT_LUNS.AllocationLength[1] = (UCHAR) ((lunListSize >> 16) & 0xff);
|
|
cdb->REPORT_LUNS.AllocationLength[2] = (UCHAR) ((lunListSize >> 8) & 0xff);
|
|
cdb->REPORT_LUNS.AllocationLength[3] = (UCHAR) ((lunListSize >> 0) & 0xff);
|
|
|
|
//
|
|
// Call port driver to handle this request.
|
|
//
|
|
|
|
status = IoCallDriver(LogicalUnit->DeviceObject, irp);
|
|
|
|
//
|
|
// Wait for request to complete.
|
|
//
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
if (SRB_STATUS(srb.SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((2,"IssueReportLuns: failed SRB status %x\n",
|
|
srb.SrbStatus));
|
|
|
|
//
|
|
// Unfreeze queue if necessary
|
|
//
|
|
|
|
if (srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
|
|
DebugPrint((3, "IssueInquiry: Unfreeze Queue TID %d\n",
|
|
srb.TargetId));
|
|
|
|
LogicalUnit->LuFlags &= ~LU_QUEUE_FROZEN;
|
|
|
|
KeAcquireSpinLock(
|
|
&(LogicalUnit->AdapterExtension->SpinLock),
|
|
¤tIrql);
|
|
|
|
GetNextLuRequest(LogicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
}
|
|
|
|
if ((srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
senseInfoBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST){
|
|
|
|
//
|
|
// A sense key of illegal request was recieved. This indicates
|
|
// that the logical unit number of not valid but there is a
|
|
// target device out there.
|
|
//
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
} else if ((SRB_STATUS(srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT) ||
|
|
(SRB_STATUS(srb.SrbStatus) == SRB_STATUS_NO_DEVICE)) {
|
|
|
|
//
|
|
// If the selection times out then give up
|
|
//
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// retry...
|
|
//
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
IoFreeMdl(mdl);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ULONG listLength;
|
|
|
|
listLength = lunListDataBuffer->LunListLength[3] << 0;
|
|
listLength |= lunListDataBuffer->LunListLength[2] << 8;
|
|
listLength |= lunListDataBuffer->LunListLength[1] << 16;
|
|
listLength |= lunListDataBuffer->LunListLength[0] << 24;
|
|
|
|
if (lunListSize < (listLength + sizeof (LUN_LIST))) {
|
|
|
|
lunListSize = listLength + sizeof (LUN_LIST);
|
|
|
|
//
|
|
// try report lun with a bigger buffer
|
|
//
|
|
|
|
ExFreePool(lunListDataBuffer);
|
|
lunListDataBuffer = NULL;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else {
|
|
|
|
//
|
|
// lun list is good
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the lun list
|
|
//
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// AdjustReportLuns returns lunListDataBuffer if it cannot allocate
|
|
// a new list.
|
|
//
|
|
|
|
*LunList = AdjustReportLuns(LogicalUnit->DeviceObject->DriverObject,
|
|
lunListDataBuffer);
|
|
|
|
//
|
|
// Only delete lunListDataBuffer if we didn't return it from
|
|
// AdjustReportLuns.
|
|
//
|
|
|
|
ASSERT(*LunList != NULL);
|
|
ASSERT(lunListDataBuffer != NULL);
|
|
if (*LunList != lunListDataBuffer) {
|
|
ExFreePool(lunListDataBuffer);
|
|
}
|
|
} else {
|
|
*LunList = NULL;
|
|
if (lunListDataBuffer) {
|
|
ExFreePool(lunListDataBuffer);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end IssueReportLuns()
|
|
|
|
|
|
|
|
VOID
|
|
GetNextLuRequestWithoutLock(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(SpPAGELOCKLockCount != 0);
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
KeAcquireSpinLockAtDpcLevel(&(LogicalUnit->AdapterExtension->SpinLock));
|
|
GetNextLuRequest(LogicalUnit);
|
|
KeLowerIrql(oldIrql);
|
|
PAGED_CODE();
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
SpCountLogicalUnits(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
ULONG numberOfLus = 0;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
KIRQL oldIrql;
|
|
|
|
ULONG bin;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
PVOID sectionHandle;
|
|
#endif
|
|
//
|
|
// Code is paged until locked down.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Lock this routine down before grabbing the spinlock.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
sectionHandle = MmLockPagableCodeSection(SpCountLogicalUnits);
|
|
#endif
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
|
|
for(bin = 0; bin < NUMBER_LOGICAL_UNIT_BINS; bin++) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
|
|
for(luExtension = Adapter->LogicalUnitList[bin].List;
|
|
luExtension != NULL;
|
|
luExtension = luExtension->NextLogicalUnit) {
|
|
|
|
if(luExtension->IsMissing == FALSE) {
|
|
numberOfLus++;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
}
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
|
|
return numberOfLus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpGetInquiryData(
|
|
IN PADAPTER_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions copies the inquiry data to the system buffer. The data
|
|
is translate from the port driver's internal format to the user mode
|
|
format.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer the SCSI adapter device extension.
|
|
|
|
Irp - Supplies a pointer to the Irp which made the original request.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR bufferStart;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
UCHAR bin;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
PSCSI_ADAPTER_BUS_INFO adapterInfo;
|
|
PSCSI_INQUIRY_DATA inquiryData;
|
|
ULONG inquiryDataSize;
|
|
ULONG length;
|
|
PLOGICAL_UNIT_INFO lunInfo;
|
|
ULONG numberOfBuses;
|
|
ULONG numberOfLus;
|
|
ULONG j;
|
|
UCHAR pathId;
|
|
UCHAR targetId;
|
|
UCHAR lun;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(DeviceExtension->CommonExtension.DeviceObject);
|
|
|
|
status = KeWaitForMutexObject(&(DeviceExtension->EnumerationDeviceMutex),
|
|
UserRequest,
|
|
UserMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if(status == STATUS_USER_APC) {
|
|
status = STATUS_REQUEST_ABORTED;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Status = status;
|
|
return status;
|
|
}
|
|
|
|
DebugPrint((3,"SpGetInquiryData: Enter routine\n"));
|
|
|
|
//
|
|
// Get a pointer to the control block.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
bufferStart = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Determine the number of SCSI buses and logical units.
|
|
//
|
|
|
|
numberOfBuses = DeviceExtension->NumberOfBuses;
|
|
numberOfLus = 0;
|
|
|
|
numberOfLus = SpCountLogicalUnits(DeviceExtension);
|
|
|
|
//
|
|
// Caculate the size of the logical unit structure and round it to a word
|
|
// alignment.
|
|
//
|
|
|
|
inquiryDataSize = ((sizeof(SCSI_INQUIRY_DATA) - 1 + INQUIRYDATABUFFERSIZE +
|
|
sizeof(ULONG) - 1) & ~(sizeof(ULONG) - 1));
|
|
|
|
// Based on the number of buses and logical unit, determine the minimum
|
|
// buffer length to hold all of the data.
|
|
//
|
|
|
|
length = sizeof(SCSI_ADAPTER_BUS_INFO) +
|
|
(numberOfBuses - 1) * sizeof(SCSI_BUS_DATA);
|
|
length += inquiryDataSize * numberOfLus;
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length) {
|
|
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
KeReleaseMutex(&(DeviceExtension->EnumerationDeviceMutex), FALSE);
|
|
return(STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Set the information field.
|
|
//
|
|
|
|
Irp->IoStatus.Information = length;
|
|
|
|
//
|
|
// Fill in the bus information.
|
|
//
|
|
|
|
adapterInfo = (PSCSI_ADAPTER_BUS_INFO) bufferStart;
|
|
|
|
adapterInfo->NumberOfBuses = (UCHAR) numberOfBuses;
|
|
inquiryData = (PSCSI_INQUIRY_DATA)(bufferStart +
|
|
sizeof(SCSI_ADAPTER_BUS_INFO) +
|
|
((numberOfBuses - 1) *
|
|
sizeof(SCSI_BUS_DATA)));
|
|
|
|
for (pathId = 0; pathId < numberOfBuses; pathId++) {
|
|
|
|
PSCSI_BUS_DATA busData;
|
|
|
|
busData = &adapterInfo->BusData[pathId];
|
|
busData->InitiatorBusId = DeviceExtension->PortConfig->InitiatorBusId[pathId];
|
|
busData->NumberOfLogicalUnits = 0;
|
|
busData->InquiryDataOffset = (ULONG)((PUCHAR) inquiryData - bufferStart);
|
|
|
|
for(targetId = 0;
|
|
targetId < DeviceExtension->MaximumTargetIds;
|
|
targetId++) {
|
|
for(lun = 0;
|
|
lun < SCSI_MAXIMUM_LUNS_PER_TARGET;
|
|
lun++) {
|
|
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
|
|
luExtension = GetLogicalUnitExtension(DeviceExtension,
|
|
pathId,
|
|
targetId,
|
|
lun,
|
|
Irp,
|
|
TRUE);
|
|
|
|
if(luExtension == NULL) {
|
|
continue;
|
|
}
|
|
|
|
|
|
if((luExtension->IsMissing) ||
|
|
(luExtension->CommonExtension.IsRemoved)) {
|
|
|
|
SpReleaseRemoveLock(
|
|
luExtension->CommonExtension.DeviceObject,
|
|
Irp);
|
|
|
|
continue;
|
|
}
|
|
|
|
busData->NumberOfLogicalUnits++;
|
|
|
|
DebugPrint((1, "InquiryData for (%d, %d, %d) - ",
|
|
pathId,
|
|
targetId,
|
|
lun));
|
|
DebugPrint((1, "%d units found\n", busData->NumberOfLogicalUnits));
|
|
|
|
inquiryData->PathId = pathId;
|
|
inquiryData->TargetId = targetId;
|
|
inquiryData->Lun = lun;
|
|
inquiryData->DeviceClaimed = luExtension->IsClaimed;
|
|
inquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE;
|
|
inquiryData->NextInquiryDataOffset = (ULONG)((PUCHAR) inquiryData + inquiryDataSize - bufferStart);
|
|
|
|
RtlCopyMemory(inquiryData->InquiryData,
|
|
&(luExtension->InquiryData),
|
|
INQUIRYDATABUFFERSIZE);
|
|
|
|
inquiryData = (PSCSI_INQUIRY_DATA) ((PUCHAR) inquiryData + inquiryDataSize);
|
|
|
|
SpReleaseRemoveLock(luExtension->CommonExtension.DeviceObject,
|
|
Irp);
|
|
}
|
|
}
|
|
|
|
if(busData->NumberOfLogicalUnits == 0) {
|
|
busData->InquiryDataOffset = 0;
|
|
} else {
|
|
((PSCSI_INQUIRY_DATA) ((PCHAR) inquiryData - inquiryDataSize))->NextInquiryDataOffset = 0;
|
|
}
|
|
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
KeReleaseMutex(&(DeviceExtension->EnumerationDeviceMutex), FALSE);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
VOID
|
|
SpAddLogicalUnitToBin (
|
|
IN PADAPTER_EXTENSION AdapterExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnitExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will synchronize with any interrupt or miniport routines and
|
|
add the specified logical unit to the appropriate logical unit list.
|
|
The logical unit must not already be in the list.
|
|
|
|
This routine acquires the bin spinlock and calls the SynchronizeExecution
|
|
routine. It cannot be called when the bin spinlock is held or from a
|
|
miniport API.
|
|
|
|
Arguments:
|
|
|
|
AdapterExtension - the adapter to add this logical unit to.
|
|
|
|
LogicalUnitExtension - the logical unit to be added.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR hash = ADDRESS_TO_HASH(LogicalUnitExtension->PathId,
|
|
LogicalUnitExtension->TargetId,
|
|
LogicalUnitExtension->Lun);
|
|
|
|
PLOGICAL_UNIT_BIN bin = &AdapterExtension->LogicalUnitList[hash];
|
|
|
|
PLOGICAL_UNIT_EXTENSION lun;
|
|
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&AdapterExtension->SpinLock, &oldIrql);
|
|
KeAcquireSpinLockAtDpcLevel(&bin->Lock);
|
|
|
|
//
|
|
// Run through the list quickly and make sure this lun isn't already there
|
|
//
|
|
|
|
lun = bin->List;
|
|
|
|
while(lun != NULL) {
|
|
|
|
if(lun == LogicalUnitExtension) {
|
|
break;
|
|
}
|
|
lun = lun->NextLogicalUnit;
|
|
}
|
|
|
|
ASSERTMSG("Logical Unit already in list: ", lun == NULL);
|
|
|
|
ASSERTMSG("Logical Unit not properly initialized: ",
|
|
(LogicalUnitExtension->AdapterExtension == AdapterExtension));
|
|
|
|
ASSERTMSG("Logical Unit is already on a list: ",
|
|
LogicalUnitExtension->NextLogicalUnit == NULL);
|
|
|
|
LogicalUnitExtension->NextLogicalUnit = bin->List;
|
|
|
|
bin->List = LogicalUnitExtension;
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&bin->Lock);
|
|
KeReleaseSpinLock(&AdapterExtension->SpinLock, oldIrql);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpRemoveLogicalUnitFromBin (
|
|
IN PADAPTER_EXTENSION AdapterExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnitExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will synchronize with any interrupt or miniport routines and
|
|
remove the specified logical unit from the appropriate logical unit list.
|
|
The logical unit MUST be in the logical unit list.
|
|
|
|
This routine acquires the bin spinlock and calls the SynchronizeExecution
|
|
routine. It cannot be called when the bin spinlock is held or from
|
|
a miniport exported routine.
|
|
|
|
Arguments:
|
|
|
|
AdapterExtension - The adapter from which to remove this logical unit
|
|
|
|
LogicalUnitExtension - the logical unit to be removed
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PLOGICAL_UNIT_BIN bin =
|
|
&AdapterExtension->LogicalUnitList[ADDRESS_TO_HASH(
|
|
LogicalUnitExtension->PathId,
|
|
LogicalUnitExtension->TargetId,
|
|
LogicalUnitExtension->Lun)];
|
|
|
|
KeAcquireSpinLock(&AdapterExtension->SpinLock, &oldIrql);
|
|
KeAcquireSpinLockAtDpcLevel(&bin->Lock);
|
|
|
|
AdapterExtension->SynchronizeExecution(
|
|
AdapterExtension->InterruptObject,
|
|
SpRemoveLogicalUnitFromBinSynchronized,
|
|
LogicalUnitExtension
|
|
);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&bin->Lock);
|
|
KeReleaseSpinLock(&AdapterExtension->SpinLock, oldIrql);
|
|
|
|
if(LogicalUnitExtension->IsMismatched) {
|
|
DebugPrint((1, "SpRemoveLogicalUnitFromBin: Signalling for rescan "
|
|
"after removal of mismatched lun %#p\n",
|
|
LogicalUnitExtension));
|
|
IoInvalidateDeviceRelations(AdapterExtension->LowerPdo,
|
|
BusRelations);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpRemoveLogicalUnitFromBinSynchronized(
|
|
IN PVOID ServiceContext
|
|
)
|
|
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnitExtension =
|
|
(PLOGICAL_UNIT_EXTENSION) ServiceContext;
|
|
PADAPTER_EXTENSION adapterExtension =
|
|
logicalUnitExtension->AdapterExtension;
|
|
|
|
UCHAR hash = ADDRESS_TO_HASH(
|
|
logicalUnitExtension->PathId,
|
|
logicalUnitExtension->TargetId,
|
|
logicalUnitExtension->Lun);
|
|
|
|
PLOGICAL_UNIT_BIN bin;
|
|
|
|
PLOGICAL_UNIT_EXTENSION *lun;
|
|
|
|
ASSERT(hash < NUMBER_LOGICAL_UNIT_BINS);
|
|
|
|
adapterExtension->CachedLogicalUnit = NULL;
|
|
|
|
bin = &adapterExtension->LogicalUnitList[hash];
|
|
|
|
lun = &bin->List;
|
|
|
|
while(*lun != NULL) {
|
|
|
|
if(*lun == logicalUnitExtension) {
|
|
|
|
//
|
|
// Found a match - unlink it from the list.
|
|
//
|
|
|
|
*lun = logicalUnitExtension->NextLogicalUnit;
|
|
logicalUnitExtension->NextLogicalUnit = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
lun = &((*lun)->NextLogicalUnit);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PLUN_LIST
|
|
AdjustReportLuns(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PLUN_LIST RawList
|
|
)
|
|
{
|
|
ULONG newLength;
|
|
ULONG numberOfEntries;
|
|
ULONG maxLun = 8;
|
|
|
|
PLUN_LIST newList;
|
|
|
|
//
|
|
// Derive the length of the list and the number of entries currently in
|
|
// the list.
|
|
//
|
|
|
|
newLength = RawList->LunListLength[3] << 0;
|
|
newLength |= RawList->LunListLength[2] << 8;
|
|
newLength |= RawList->LunListLength[1] << 16;
|
|
newLength |= RawList->LunListLength[0] << 24;
|
|
|
|
numberOfEntries = newLength / sizeof (RawList->Lun[0]);
|
|
|
|
newLength += sizeof(LUN_LIST);
|
|
newLength += maxLun * sizeof(RawList->Lun[0]);
|
|
|
|
//
|
|
// Allocate a list with "maxLun" extra entries in it. This might waste
|
|
// some space if we have duplicates but it's easy.
|
|
//
|
|
//
|
|
// ALLOCATION
|
|
//
|
|
|
|
|
|
newList = SpAllocatePool(NonPagedPool,
|
|
newLength,
|
|
SCSIPORT_TAG_REPORT_LUNS,
|
|
DriverObject);
|
|
|
|
if(newList == NULL){
|
|
|
|
newList = RawList;
|
|
} else {
|
|
|
|
UCHAR lunNumber;
|
|
ULONG entry;
|
|
ULONG newEntryCount = 0;
|
|
|
|
RtlZeroMemory(newList, newLength);
|
|
|
|
//
|
|
// First make a fake entry for each of the luns from 0 to maxLun - 1
|
|
//
|
|
|
|
for(lunNumber = 0; lunNumber < maxLun; lunNumber++) {
|
|
newList->Lun[lunNumber][1] = lunNumber;
|
|
newEntryCount++;
|
|
};
|
|
|
|
//
|
|
// Now iterate through the entries in the remaining list. For each
|
|
// one copy it over iff it's not already a lun 0 -> (maxLun - 1)
|
|
//
|
|
|
|
for(entry = 0; entry < numberOfEntries; entry++) {
|
|
USHORT l;
|
|
|
|
l = (RawList->Lun[entry][0] << 8);
|
|
l |= RawList->Lun[entry][1];
|
|
l &= 0x3fff;
|
|
|
|
if(l >= maxLun) {
|
|
RtlCopyMemory(&(newList->Lun[lunNumber]),
|
|
&(RawList->Lun[entry]),
|
|
sizeof(newList->Lun[0]));
|
|
lunNumber++;
|
|
newEntryCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy over the reserved bytes for the cases where they aren't all
|
|
// that reserved.
|
|
//
|
|
|
|
RtlCopyMemory(newList->Reserved,
|
|
RawList->Reserved,
|
|
sizeof(RawList->Reserved));
|
|
|
|
//
|
|
// Subtract out the number of duplicate entries we found.
|
|
//
|
|
|
|
newLength = newEntryCount * sizeof(RawList->Lun[0]);
|
|
|
|
newList->LunListLength[0] = (UCHAR) ((newLength >> 24) & 0xff);
|
|
newList->LunListLength[1] = (UCHAR) ((newLength >> 16) & 0xff);
|
|
newList->LunListLength[2] = (UCHAR) ((newLength >> 8) & 0xff);
|
|
newList->LunListLength[3] = (UCHAR) ((newLength >> 0) & 0xff);
|
|
}
|
|
|
|
return newList;
|
|
}
|
|
|
|
VOID
|
|
SpCompleteEnumRequest(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes our handling of an asynchronous bus scan. If the
|
|
supplied IRP has been completed successfully, we pass it down to the
|
|
driver below. If the IRP was failed, we complete the request here.
|
|
|
|
Arguments:
|
|
|
|
Adapter - The adapter we're scanning.
|
|
|
|
Irp - The IRP that prompted this asynchronous bus scan true then a
|
|
scan will be done even if one has happend within the minimum
|
|
bus scan delta time.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
ULONG tempLock;
|
|
|
|
//
|
|
// Acquire a temporary remove lock so we can release the lock acquired
|
|
// on behalf of the IRP.
|
|
//
|
|
|
|
SpAcquireRemoveLock(Adapter->DeviceObject, &tempLock);
|
|
|
|
//
|
|
// Release the IRP's remove lock because holding it across completion
|
|
// could trip up our remove tracking code since it is based on the
|
|
// IRP address which can be recycled.
|
|
//
|
|
|
|
SpReleaseRemoveLock(Adapter->DeviceObject, Irp);
|
|
|
|
//
|
|
// Call down or complete the IRP, depending on the request's completion
|
|
// status.
|
|
//
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoCallDriver(Adapter->CommonExtension.LowerDeviceObject, Irp);
|
|
|
|
} else {
|
|
|
|
SpCompleteRequest(Adapter->DeviceObject,
|
|
Irp,
|
|
NULL,
|
|
IO_NO_INCREMENT);
|
|
|
|
}
|
|
|
|
//
|
|
// Release the temporary lock.
|
|
//
|
|
|
|
SpReleaseRemoveLock(Adapter->DeviceObject, &tempLock);
|
|
}
|
|
|
|
NTSTATUS
|
|
SpEnumerateAdapterSynchronous(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN BOOLEAN Force
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will call SpEnumerateAdapterAsynchronous and wait for it to
|
|
complete.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter we're scanning.
|
|
|
|
Force - if true then a scan will be done even if one has happend within
|
|
the minimum bus scan delta time.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
SP_ENUMERATION_REQUEST request;
|
|
KEVENT event;
|
|
|
|
NTSTATUS status;
|
|
|
|
RtlZeroMemory(&request, sizeof(SP_ENUMERATION_REQUEST));
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
request.CompletionRoutine = SpSignalEnumerationCompletion;
|
|
request.Context = &event;
|
|
request.CompletionStatus = &status;
|
|
request.Synchronous = TRUE;
|
|
|
|
SpEnumerateAdapterAsynchronous(Adapter, &request, Force);
|
|
|
|
KeWaitForSingleObject(&(event),
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
SpEnumerateAdapterAsynchronous(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSP_ENUMERATION_REQUEST Request,
|
|
IN BOOLEAN Force
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine will queue a bus scan and return. When the scan completes the
|
|
worker thread will run the callback in the Request passed in by the caller.
|
|
|
|
Details:
|
|
|
|
If the force flag (or the ForceNextBusScan flag in the adapter) is set or
|
|
the minimum interval between bus scans has passed then this routine will
|
|
queue this enumeration request to the work list and, if necessary, start
|
|
a new worker thread to process them.
|
|
|
|
Otherwise it will attempt to acquire the EnumerationDeviceMutex in order to
|
|
run the completion routine. If this is not available then it will also
|
|
queue the work item and start the thread if necessary.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter to be scanned.
|
|
|
|
Request - the request to be processed when the scan is complete. The
|
|
completion routine in this request may free the request structure.
|
|
|
|
Force - hint as to whether or not we should honor the minimum bus scan
|
|
interval.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG forceNext;
|
|
LONGLONG rescanInterval;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Request->CompletionRoutine != NULL);
|
|
ASSERT(Request->NextRequest == NULL);
|
|
|
|
ExAcquireFastMutex(&(Adapter->EnumerationWorklistMutex));
|
|
|
|
//
|
|
// Swap out the ForceNextBusScan value for FALSE.
|
|
//
|
|
|
|
forceNext = InterlockedExchange(&(Adapter->ForceNextBusScan), FALSE);
|
|
|
|
//
|
|
// Force the bus scan to happen either way.
|
|
//
|
|
|
|
Force = (Force || forceNext || Adapter->EnumerationRunning) ? TRUE : FALSE;
|
|
|
|
//
|
|
// Calculate the time between bus enumerations.
|
|
//
|
|
|
|
if(Force == FALSE) {
|
|
LARGE_INTEGER currentSystemTime;
|
|
LONGLONG lastTime;
|
|
|
|
KeQuerySystemTime(¤tSystemTime);
|
|
|
|
lastTime = Adapter->LastBusScanTime.QuadPart;
|
|
|
|
rescanInterval = currentSystemTime.QuadPart - lastTime;
|
|
}
|
|
|
|
//
|
|
// If we're required to do the bus scan then queue this request and
|
|
// schedule a work item to run in (if necessary).
|
|
//
|
|
|
|
if((Force == TRUE) || (rescanInterval > MINIMUM_BUS_SCAN_INTERVAL)) {
|
|
|
|
//
|
|
// Grab the remove lock for this device so we know it (and the
|
|
// associated code) can't be removed.
|
|
//
|
|
|
|
SpAcquireRemoveLock(Adapter->DeviceObject, Request);
|
|
|
|
//
|
|
// Queue the entry to the work list.
|
|
//
|
|
|
|
Request->NextRequest = Adapter->EnumerationWorkList;
|
|
Adapter->EnumerationWorkList = Request;
|
|
|
|
if(Adapter->EnumerationRunning == FALSE) {
|
|
|
|
//
|
|
// Start a new worker thread to run the enumeration.
|
|
//
|
|
|
|
Adapter->EnumerationRunning = TRUE;
|
|
|
|
ExQueueWorkItem(&(Adapter->EnumerationWorkItem), DelayedWorkQueue);
|
|
}
|
|
|
|
ExReleaseFastMutex(&(Adapter->EnumerationWorklistMutex));
|
|
|
|
} else {
|
|
|
|
NTSTATUS status;
|
|
PIRP irp = NULL;
|
|
|
|
//
|
|
// We're going to try and satisfy this request immediately.
|
|
// If there is currently an enumeration running then we'll try to
|
|
// acquire the EnumerationDeviceMutex. If that fails we'll just
|
|
// queue the request for the worker to complete. If the worker is
|
|
// not running then we just acquire the mutex and process the request.
|
|
//
|
|
|
|
ASSERT(Adapter->EnumerationRunning == FALSE);
|
|
|
|
ExReleaseFastMutex(&(Adapter->EnumerationWorklistMutex));
|
|
|
|
status = KeWaitForMutexObject(&(Adapter->EnumerationDeviceMutex),
|
|
UserRequest,
|
|
UserMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
//
|
|
// If this is an async request, save the IRP so we can complete
|
|
// it after we've filled in the completion information. We can't
|
|
// touch the request after we return from our completion callback.
|
|
//
|
|
|
|
if (Request->Synchronous == FALSE) {
|
|
irp = (PIRP) Request->Context;
|
|
}
|
|
|
|
//
|
|
// Either we got the mutex (STATUS_SUCCESS) or the thread is being
|
|
// terminated (STATUS_USER_APC - since we're not alertable a
|
|
// user-mode APC can't be run except in certain special cases).
|
|
//
|
|
// Either way the completion routine will do the correct thing.
|
|
//
|
|
|
|
Request->CompletionRoutine(Adapter, Request, status);
|
|
KeReleaseMutex(&(Adapter->EnumerationDeviceMutex), FALSE);
|
|
|
|
//
|
|
// If this is an async request, complete the IRP or pass it down
|
|
// depending on the status.
|
|
//
|
|
|
|
if (irp != NULL) {
|
|
SpCompleteEnumRequest(Adapter, irp);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpSignalEnumerationCompletion(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSP_ENUMERATION_REQUEST Request,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
if(ARGUMENT_PRESENT(Request->CompletionStatus)) {
|
|
*(Request->CompletionStatus) = Status;
|
|
}
|
|
|
|
KeSetEvent((PKEVENT) Request->Context, IO_NO_INCREMENT, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpEnumerationWorker(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PSP_ENUMERATION_REQUEST request;
|
|
PKTHREAD thread;
|
|
PIRP currentIrp;
|
|
PLIST_ENTRY currentEntry;
|
|
LIST_ENTRY completedListHead;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(Adapter->DeviceObject);
|
|
|
|
ASSERT(Adapter->EnumerationRunning == TRUE);
|
|
|
|
//
|
|
// Initialize the list of completed IRPs.
|
|
//
|
|
|
|
InitializeListHead(&completedListHead);
|
|
|
|
Adapter->EnumerationWorkThread = KeGetCurrentThread();
|
|
|
|
//
|
|
// Grab the device mutex and enumerate the bus.
|
|
//
|
|
|
|
KeWaitForMutexObject(&(Adapter->EnumerationDeviceMutex),
|
|
UserRequest,
|
|
UserMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
SpScanAdapter(Adapter);
|
|
|
|
//
|
|
// Drop the device mutex & grab the WorkList mutex.
|
|
//
|
|
|
|
KeReleaseMutex(&(Adapter->EnumerationDeviceMutex), FALSE);
|
|
ExAcquireFastMutex(&(Adapter->EnumerationWorklistMutex));
|
|
|
|
//
|
|
// Update the time of this bus scan.
|
|
//
|
|
|
|
KeQuerySystemTime(&(Adapter->LastBusScanTime));
|
|
|
|
//
|
|
// Grab a temporary remove lock. Use the address of the work item as a
|
|
// cheap way of ensuring that we haven't requeued the work item while the
|
|
// thread is still running.
|
|
//
|
|
|
|
SpAcquireRemoveLock(Adapter->DeviceObject, &(Adapter->EnumerationWorkItem));
|
|
|
|
//
|
|
// Run through the list of enumeration requests. For each one:
|
|
// * remove it from the work list.
|
|
// * save the irp if it's an async request
|
|
// * call its completion routine
|
|
//
|
|
|
|
for(request = Adapter->EnumerationWorkList;
|
|
request != NULL;
|
|
request = Adapter->EnumerationWorkList) {
|
|
|
|
//
|
|
// Remove this entry from the list. Clear the next request pointer
|
|
// as a bugcatcher.
|
|
//
|
|
|
|
Adapter->EnumerationWorkList = request->NextRequest;
|
|
request->NextRequest = NULL;
|
|
|
|
//
|
|
// If this is an asynchronous request, add the IRP to the completed list.
|
|
//
|
|
|
|
if (request->Synchronous == FALSE) {
|
|
currentIrp = (PIRP)request->Context;
|
|
InsertTailList(&completedListHead, ¤tIrp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
//
|
|
// Release the remove lock we acquired on behalf of the request object
|
|
// before we call the completion routine. The temporary lock we
|
|
// acquired above protects us.
|
|
//
|
|
|
|
SpReleaseRemoveLock(Adapter->DeviceObject, request);
|
|
|
|
//
|
|
// Call our completion callback routine.
|
|
//
|
|
|
|
request->CompletionRoutine(Adapter, request, STATUS_SUCCESS);
|
|
request = NULL;
|
|
}
|
|
|
|
//
|
|
// Indicate that the work item is no longer running.
|
|
//
|
|
|
|
Adapter->EnumerationRunning = FALSE;
|
|
Adapter->EnumerationWorkThread = NULL;
|
|
|
|
//
|
|
// Release the lock.
|
|
//
|
|
|
|
ExReleaseFastMutex(&(Adapter->EnumerationWorklistMutex));
|
|
|
|
//
|
|
// For asynchronous bus scans, we must wait until we've released the fast
|
|
// mutex to complete the IRPs. Doing so while holding the fast mutex
|
|
// completes the IRP at APC_LEVEL and this opens the door to filter
|
|
// drivers completion routines calling one of our dispatch routines at
|
|
// elevated IRQL. This is a problem because some of these dispatch
|
|
// routines process requests synchronously by blocking the thread and
|
|
// waiting for the IO Manager to set an event upon request completion.
|
|
// The problem is that the IO Manager, for synchronous operations,
|
|
// schedules an APC for the original thread in order to set the event
|
|
// and do buffer copying in the caller's thread context. This of course
|
|
// deadlocks because the waiting thread is already at APC_LEVEL.
|
|
//
|
|
// By releasing the mutex first, we drop the thread's IRQL back to
|
|
// PASSIVE_LEVEL and the problem is solved.
|
|
//
|
|
// The completion callback set the IRP's status and information fields;
|
|
// all we have to do is either forward the IRP down the stack if the
|
|
// status indicates success or complete it if the request failed.
|
|
//
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
while (IsListEmpty(&completedListHead) == FALSE) {
|
|
|
|
//
|
|
// Get the next entry from the list.
|
|
//
|
|
|
|
currentEntry = RemoveHeadList(&completedListHead);
|
|
|
|
//
|
|
// Extract a pointer to the IRP.
|
|
//
|
|
|
|
currentIrp = CONTAINING_RECORD(currentEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
|
|
SpCompleteEnumRequest(Adapter, currentIrp);
|
|
}
|
|
|
|
//
|
|
// Release the temporary remove lock we acquired above.
|
|
//
|
|
|
|
SpReleaseRemoveLock(Adapter->DeviceObject, &(Adapter->EnumerationWorkItem));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpScanAdapter(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans all of the busses on an adapter. It locks down the
|
|
necessary memory pages, checks the registry to see if we should be
|
|
exposing disconnected luns, powers up the controller (if needed) and
|
|
then scans each bus for devices.
|
|
|
|
This routine is very much non-reenterant and should not be called outside
|
|
of the enumeration mutex (ie. outside of an enumeration request).
|
|
|
|
Arguments:
|
|
|
|
Adapter - a pointer to the adapter being enumerated.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject = Adapter->DeviceObject;
|
|
|
|
UCHAR i;
|
|
|
|
BOOLEAN exposeDisconnectedLuns = FALSE;
|
|
|
|
PLOGICAL_UNIT_EXTENSION rescanLun;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
PVOID sectionHandle;
|
|
#endif
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((EnumDebug, "SpScanAdapter: Beginning scan of adapter %#p\n", Adapter));
|
|
|
|
//
|
|
// Try to allocate a logical unit to use for probeing new bus addresses.
|
|
// Assume that it's going to be a SCSI-2 device.
|
|
//
|
|
|
|
status = SpCreateLogicalUnit(Adapter,
|
|
0xff,
|
|
0xff,
|
|
0xff,
|
|
TRUE,
|
|
FALSE,
|
|
&rescanLun);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Lock down the PAGELOCK section - we'll need it in order to call
|
|
// IssueInquiry.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
sectionHandle = MmLockPagableCodeSection(GetNextLuRequestWithoutLock);
|
|
InterlockedIncrement(&SpPAGELOCKLockCount);
|
|
#endif
|
|
|
|
//
|
|
// Check to see if we should be exposing disconnected LUNs.
|
|
//
|
|
|
|
for(i = 0; i < 3; i++) {
|
|
|
|
PWCHAR locations[] = {
|
|
L"Scsiport",
|
|
SCSIPORT_CONTROL_KEY,
|
|
DISK_SERVICE_KEY
|
|
};
|
|
|
|
UNICODE_STRING unicodeString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE instanceHandle = NULL;
|
|
HANDLE handle;
|
|
PKEY_VALUE_FULL_INFORMATION key = NULL;
|
|
|
|
if(i == 0) {
|
|
status = IoOpenDeviceRegistryKey(Adapter->LowerPdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ,
|
|
&instanceHandle);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpScanAdapter: Error %#08lx opening device registry key\n", status));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeString, locations[i]);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
instanceHandle,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&handle,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpScanAdapter: Error %#08lx opening %wZ key\n", status, &unicodeString));
|
|
if(instanceHandle != NULL) {
|
|
ZwClose(instanceHandle);
|
|
instanceHandle = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
status = SpGetRegistryValue(deviceObject->DriverObject,
|
|
handle,
|
|
L"ScanDisconnectedDevices",
|
|
&key);
|
|
|
|
ZwClose(handle);
|
|
if(instanceHandle != NULL) {
|
|
ZwClose(instanceHandle);
|
|
instanceHandle = NULL;
|
|
}
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
if(key->Type == REG_DWORD) {
|
|
PULONG value;
|
|
value = (PULONG) ((PUCHAR) key + key->DataOffset);
|
|
if(*value) {
|
|
exposeDisconnectedLuns = TRUE;
|
|
}
|
|
}
|
|
ExFreePool(key);
|
|
break;
|
|
} else {
|
|
DebugPrint((2, "SpScanAdapter: Error %#08lx opening %wZ\\ScanDisconnectedDevices value\n", status, &unicodeString));
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to be powered up in order to do a bus enumeration - make
|
|
// sure that we are. This is because we create new PDO's and new
|
|
// PDO's are assumed to be at D0.
|
|
//
|
|
|
|
status = SpRequestValidAdapterPowerStateSynchronous(Adapter);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
UCHAR pathId;
|
|
|
|
for (pathId = 0; pathId < Adapter->NumberOfBuses; pathId++) {
|
|
status = SpScanBus(Adapter, pathId, exposeDisconnectedLuns, rescanLun);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
InterlockedDecrement(&SpPAGELOCKLockCount);
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
|
|
SpDeleteLogicalUnit(rescanLun);
|
|
ASSERT(Adapter->RescanLun == NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpScanBus(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun
|
|
)
|
|
{
|
|
UCHAR targetIndex;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
DebugPrint((EnumDebug, "SpScanBus: Beginning scan of bus %x\n", PathId));
|
|
|
|
for(targetIndex = 0; targetIndex < Adapter->MaximumTargetIds; targetIndex++) {
|
|
|
|
UCHAR targetId;
|
|
|
|
if(Adapter->Capabilities.AdapterScansDown) {
|
|
targetId = Adapter->MaximumTargetIds - targetIndex - 1;
|
|
} else {
|
|
targetId = targetIndex;
|
|
}
|
|
|
|
DebugPrint((EnumDebug, "SpScanBus: targetIndex = %x -> targetId = %x\n",
|
|
targetIndex, targetId));
|
|
|
|
ASSERT(targetId != 255);
|
|
ASSERT(Adapter->PortConfig);
|
|
|
|
if(targetId == Adapter->PortConfig->InitiatorBusId[PathId]) {
|
|
DebugPrint((EnumDebug, "SpScanBus: Target ID matches initiator ID - skipping\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Mark all of the logical units as needing verification. At the
|
|
// end of scanning the target and LUNs which still need to be verified
|
|
// will be purged (marked as missing).
|
|
//
|
|
|
|
SpSetVerificationMarks(Adapter, PathId, targetId);
|
|
RescanLun->NeedsVerification = TRUE;
|
|
|
|
status = SpScanTarget(Adapter,
|
|
PathId,
|
|
targetId,
|
|
ExposeDisconnectedLuns,
|
|
RescanLun);
|
|
|
|
SpPurgeTarget(Adapter, PathId, targetId);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpScanTarget(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN PLOGICAL_UNIT_EXTENSION RescanLun
|
|
)
|
|
{
|
|
BOOLEAN sparseLun = FALSE;
|
|
|
|
PLOGICAL_UNIT_EXTENSION lunZero;
|
|
BOOLEAN checkNextLun;
|
|
|
|
BOOLEAN scsi1 = FALSE;
|
|
|
|
PLUN_LIST lunList = NULL;
|
|
BOOLEAN saveLunList = FALSE;
|
|
ULONG numLunsReported;
|
|
|
|
UCHAR maxLuCount;
|
|
ULONG lunIndex;
|
|
|
|
NTSTATUS resetStatus;
|
|
NTSTATUS status;
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Beginning scan of target %x\n", TargetId));
|
|
|
|
//
|
|
// Use the SCSI-2 dispatch table when checking LUN 0.
|
|
//
|
|
|
|
ASSERT(RescanLun->CommonExtension.MajorFunction == DeviceMajorFunctionTable);
|
|
|
|
//
|
|
// Issue an inquiry to LUN 0.
|
|
//
|
|
|
|
status = SpInquireLogicalUnit(Adapter,
|
|
PathId,
|
|
TargetId,
|
|
(UCHAR) 0,
|
|
TRUE,
|
|
RescanLun,
|
|
&lunZero,
|
|
&checkNextLun);
|
|
|
|
//
|
|
// reset the rescan lun so that we can safely use it again. If this fails
|
|
// we still continue as far as possible with this target, but we return the
|
|
// reset status to the caller so it can abort its scan.
|
|
//
|
|
|
|
resetStatus = SpPrepareLogicalUnitForReuse(RescanLun);
|
|
|
|
if(!NT_SUCCESS(resetStatus)) {
|
|
RescanLun = NULL;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// There is no device present at LUN 0. Skip to the next target.
|
|
// Even if sparse luns is enabled there MUST be a LUN 0 for us to
|
|
// continue scanning the target.
|
|
//
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Lun 0 not found - terminating scan "
|
|
"(status %#08lx)\n", status));
|
|
|
|
return resetStatus;
|
|
}
|
|
|
|
//
|
|
// Indicate that lun 0 does not require verification.
|
|
//
|
|
|
|
SpClearVerificationMark(lunZero);
|
|
|
|
//
|
|
// Check for the special case of only having one LUN on this target.
|
|
//
|
|
|
|
if(lunZero->SpecialFlags.OneLun) {
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) is listed as having "
|
|
"only one lun\n", PathId, TargetId));
|
|
return resetStatus;
|
|
}
|
|
|
|
//
|
|
// Set the rescan LUN to use whatever lun zero uses for a dispatch table.
|
|
//
|
|
|
|
RescanLun->CommonExtension.MajorFunction =
|
|
lunZero->CommonExtension.MajorFunction;
|
|
|
|
//
|
|
// Determine if we should be handling sparse LUNs on this target.
|
|
//
|
|
|
|
sparseLun = TEST(lunZero->SpecialFlags.SparseLun);
|
|
|
|
if(sparseLun) {
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) will be checked for "
|
|
"sparse luns\n", PathId, TargetId));
|
|
}
|
|
|
|
//
|
|
// Issue a report luns command to the device if it supports it.
|
|
// If it doesn't support it then use the default LUN list.
|
|
//
|
|
|
|
if((lunZero->InquiryData.HiSupport || lunZero->SpecialFlags.LargeLuns)) {
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) may support REPORT_LUNS\n", PathId, TargetId));
|
|
|
|
//
|
|
// Indicate that we should indeed save the lun list. If it turns out
|
|
// that we're unable to retrieve one to be saved then we will
|
|
// clear the flag below.
|
|
//
|
|
|
|
saveLunList = TRUE;
|
|
|
|
status = IssueReportLuns(lunZero, &lunList);
|
|
|
|
//
|
|
// If the request fails for some reason then try to use the lun list
|
|
// which was saved for this target (in the extension of logical unit
|
|
// zero). If that hasn't been set either then we'll use the default
|
|
// one down below.
|
|
//
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) returned %#08lx to REPORT_LUNS command - using old list\n", PathId, TargetId, status));
|
|
lunList = lunZero->TargetLunList;
|
|
}
|
|
|
|
//
|
|
// If we can now or have in the past gotten a report luns list from the
|
|
// device then enable sparse lun scanning. In this case we also assume
|
|
// that up to 255 luns can be supported on this target.
|
|
//
|
|
|
|
if(lunList != NULL) {
|
|
sparseLun = TRUE;
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) will be checked for "
|
|
"sparse luns(2)\n", PathId, TargetId));
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we still don't have a lun list then use the "default" one. In that
|
|
// event don't save it.
|
|
//
|
|
|
|
if(lunList == NULL) {
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) will use default lun list\n", PathId, TargetId));
|
|
lunList = (PLUN_LIST) &(ScsiPortDefaultLunList);
|
|
saveLunList = FALSE;
|
|
}
|
|
|
|
numLunsReported = lunList->LunListLength[3] << 0;
|
|
numLunsReported |= lunList->LunListLength[2] << 8;
|
|
numLunsReported |= lunList->LunListLength[1] << 16;
|
|
numLunsReported |= lunList->LunListLength[0] << 24;
|
|
numLunsReported /= sizeof (lunList->Lun[0]);
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Target (%x,%x,*) has reported %d luns\n", PathId, TargetId, numLunsReported));
|
|
|
|
//
|
|
// Walk through each entry in the LUN list. Stop when we run out of entries
|
|
// or the logical unit number is > MaximumNumberOfLogicalUnits (the lun
|
|
// list is assumed to be sorted in increasing order). For each entry,
|
|
// issue an inquiry. If the inquiry succeeds then clear the verification
|
|
// mark.
|
|
//
|
|
|
|
for(lunIndex = 0; lunIndex < numLunsReported; lunIndex++) {
|
|
PULONGLONG largeLun;
|
|
USHORT lun;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
NTSTATUS resetStatus;
|
|
|
|
largeLun = (PULONGLONG) (lunList->Lun[lunIndex]);
|
|
|
|
lun = lunList->Lun[lunIndex][1] << 0;
|
|
lun |= lunList->Lun[lunIndex][0] << 8;
|
|
lun &= 0x3fff;
|
|
|
|
//
|
|
// If the target reports a lun 0 just skip it.
|
|
//
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Checking lun %I64lx (%x): ", *largeLun, lun));
|
|
|
|
if(lun == 0) {
|
|
DebugPrint((EnumDebug, "Skipping LUN 0\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the target reports a lun outside the range the driver can support
|
|
// then skip it.
|
|
//
|
|
|
|
if(lun >= Adapter->PortConfig->MaximumNumberOfLogicalUnits) {
|
|
DebugPrint((EnumDebug, "Skipping LUN out of range (> %x)\n",
|
|
Adapter->PortConfig->MaximumNumberOfLogicalUnits));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Issue an inquiry to each logical unit in the system.
|
|
//
|
|
|
|
status = SpInquireLogicalUnit(Adapter,
|
|
PathId,
|
|
TargetId,
|
|
(UCHAR) lun,
|
|
ExposeDisconnectedLuns,
|
|
RescanLun,
|
|
&logicalUnit,
|
|
&checkNextLun);
|
|
|
|
if(RescanLun != NULL) {
|
|
resetStatus = SpPrepareLogicalUnitForReuse(RescanLun);
|
|
|
|
if(!NT_SUCCESS(resetStatus)) {
|
|
RescanLun = NULL;
|
|
}
|
|
}
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((EnumDebug, "Inquiry succeeded\n"));
|
|
SpClearVerificationMark(logicalUnit);
|
|
|
|
} else {
|
|
|
|
DebugPrint((EnumDebug, "inquiry returned %#08lx.", status));
|
|
|
|
if((sparseLun == FALSE)&&(checkNextLun == FALSE)) {
|
|
DebugPrint((EnumDebug, "Aborting\n"));
|
|
break;
|
|
} else {
|
|
DebugPrint((EnumDebug, " - checking next (%c%c)\n",
|
|
sparseLun ? 's' : ' ',
|
|
checkNextLun ? 'c' : ' '));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're supposed to save the lun list then replace the one in lun0
|
|
// with this one.
|
|
//
|
|
|
|
if(saveLunList) {
|
|
|
|
DebugPrint((EnumDebug, "SpScanTarget: Saving LUN list %#08lx\n", lunList));
|
|
ASSERT(lunZero->TargetLunList != (PLUN_LIST) &(ScsiPortDefaultLunList));
|
|
if(lunZero->TargetLunList != NULL && lunZero->TargetLunList != lunList) {
|
|
DebugPrint((EnumDebug, "SpScanTarget: Freeing old LUN list %#08lx\n", lunZero->TargetLunList));
|
|
ExFreePool(lunZero->TargetLunList);
|
|
}
|
|
|
|
lunZero->TargetLunList = lunList;
|
|
|
|
} else {
|
|
ASSERT(lunList == (PLUN_LIST) &(ScsiPortDefaultLunList));
|
|
}
|
|
|
|
//
|
|
// reset the rescan LUN to use the scsi 2 dispatch table.
|
|
//
|
|
|
|
RescanLun->CommonExtension.MajorFunction = DeviceMajorFunctionTable;
|
|
|
|
return resetStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpSetVerificationMarks(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId
|
|
)
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
KIRQL oldIrql;
|
|
|
|
ULONG bin;
|
|
|
|
//
|
|
// Code is paged until locked down.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
ASSERT(SpPAGELOCKLockCount != 0);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
|
|
for(bin = 0; bin < NUMBER_LOGICAL_UNIT_BINS; bin++) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
|
|
for(logicalUnit = Adapter->LogicalUnitList[bin].List;
|
|
logicalUnit != NULL;
|
|
logicalUnit = logicalUnit->NextLogicalUnit) {
|
|
|
|
ASSERT(logicalUnit->IsTemporary == FALSE);
|
|
|
|
if((logicalUnit->PathId == PathId) &&
|
|
(logicalUnit->TargetId == TargetId)) {
|
|
|
|
logicalUnit->NeedsVerification = TRUE;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
}
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpClearVerificationMark(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(LogicalUnit->IsTemporary == FALSE);
|
|
ASSERT(LogicalUnit->NeedsVerification == TRUE);
|
|
LogicalUnit->NeedsVerification = FALSE;
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpPurgeTarget(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId
|
|
)
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
KIRQL oldIrql;
|
|
|
|
ULONG bin;
|
|
|
|
//
|
|
// Code is paged until locked down.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
ASSERT(SpPAGELOCKLockCount != 0);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
|
|
for(bin = 0; bin < NUMBER_LOGICAL_UNIT_BINS; bin++) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
|
|
for(logicalUnit = Adapter->LogicalUnitList[bin].List;
|
|
logicalUnit != NULL;
|
|
logicalUnit = logicalUnit->NextLogicalUnit) {
|
|
|
|
ASSERT(logicalUnit->IsTemporary == FALSE);
|
|
|
|
if((logicalUnit->PathId == PathId) &&
|
|
(logicalUnit->TargetId == TargetId) &&
|
|
(logicalUnit->NeedsVerification == TRUE)) {
|
|
|
|
|
|
//
|
|
// This device was not found to be present during our bus scan.
|
|
//
|
|
|
|
DebugPrint((EnumDebug, "SpPurgeTarget: Lun (%x,%x,%x) is still marked and will be made missing\n", logicalUnit->PathId, logicalUnit->TargetId, logicalUnit->Lun));
|
|
logicalUnit->IsMissing = TRUE;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&(Adapter->LogicalUnitList[bin].Lock));
|
|
}
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpCreateLogicalUnit(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN OPTIONAL UCHAR PathId,
|
|
IN OPTIONAL UCHAR TargetId,
|
|
IN OPTIONAL UCHAR Lun,
|
|
IN BOOLEAN Temporary,
|
|
IN BOOLEAN Scsi1,
|
|
OUT PLOGICAL_UNIT_EXTENSION *NewLun
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create a physical device object for the specified device
|
|
|
|
Arguments:
|
|
|
|
Adapter - the parent adapter for this new lun
|
|
|
|
PathId, TargetId, Lun - the address of this lun. Not used if Temporary is
|
|
TRUE (see below).
|
|
|
|
Temporary - indicates whether this device is real (FALSE) or simply for
|
|
the purposes of scanning the bus (TRUE). If TRUE then the
|
|
address info is ignored and this lun is NOT inserted into the
|
|
logical unit list.
|
|
|
|
Scsi1 - indicates that this LUN is a scsi1 lun and needs to use the
|
|
dispatch routines which stick the LUN number into the CDB itself.
|
|
|
|
NewLun - a location to store the pointer to the new lun
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP senseIrp;
|
|
|
|
PDEVICE_OBJECT pdo = NULL;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnitExtension;
|
|
|
|
WCHAR wideDeviceName[64];
|
|
UNICODE_STRING unicodeDeviceName;
|
|
|
|
PVOID hwExtension = NULL;
|
|
|
|
PVOID serialNumberBuffer = NULL;
|
|
PVOID idBuffer = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Attempt to allocate all the persistent resources we need before we
|
|
// try to create the device object itself.
|
|
//
|
|
|
|
//
|
|
// Allocate a request sense irp.
|
|
//
|
|
|
|
senseIrp = SpAllocateIrp(1, FALSE, Adapter->DeviceObject->DriverObject);
|
|
|
|
if(senseIrp == NULL) {
|
|
DebugPrint((0, "SpCreateLogicalUnit: Could not allocate request sense "
|
|
"irp\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Build the name for the device
|
|
//
|
|
|
|
if(Temporary == FALSE) {
|
|
|
|
swprintf(wideDeviceName,
|
|
L"%wsPort%xPath%xTarget%xLun%x",
|
|
Adapter->DeviceName,
|
|
Adapter->PortNumber,
|
|
PathId,
|
|
TargetId,
|
|
Lun);
|
|
} else {
|
|
swprintf(wideDeviceName,
|
|
L"%wsPort%xRescan",
|
|
Adapter->DeviceName,
|
|
Adapter->PortNumber);
|
|
|
|
PathId = 0xff;
|
|
TargetId = 0xff;
|
|
Lun = 0xff;
|
|
|
|
ASSERT(Adapter->RescanLun == NULL);
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeDeviceName, wideDeviceName);
|
|
|
|
//
|
|
// Round the size of the Hardware logical extension to the size of a
|
|
// PVOID and add it to the port driver's logical extension.
|
|
//
|
|
|
|
if(Adapter->HwLogicalUnitExtensionSize != 0) {
|
|
hwExtension = SpAllocatePool(
|
|
NonPagedPoolCacheAligned,
|
|
Adapter->HwLogicalUnitExtensionSize,
|
|
SCSIPORT_TAG_LUN_EXT,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(hwExtension == NULL) {
|
|
|
|
*NewLun = NULL;
|
|
IoFreeIrp(senseIrp);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(hwExtension,
|
|
Adapter->HwLogicalUnitExtensionSize);
|
|
}
|
|
|
|
//
|
|
// If this is a temporary lun then allocate a large buffer to store the
|
|
// identify data.
|
|
//
|
|
|
|
if(Temporary) {
|
|
serialNumberBuffer = SpAllocatePool(
|
|
PagedPool,
|
|
VPD_MAX_BUFFER_SIZE,
|
|
SCSIPORT_TAG_TEMP_ID_BUFFER,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(serialNumberBuffer == NULL) {
|
|
|
|
if (hwExtension != NULL) {
|
|
ExFreePool(hwExtension);
|
|
}
|
|
IoFreeIrp(senseIrp);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
idBuffer = SpAllocatePool(PagedPool,
|
|
VPD_MAX_BUFFER_SIZE,
|
|
SCSIPORT_TAG_TEMP_ID_BUFFER,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(idBuffer == NULL) {
|
|
|
|
if (hwExtension != NULL) {
|
|
ExFreePool(hwExtension);
|
|
}
|
|
IoFreeIrp(senseIrp);
|
|
ExFreePool(serialNumberBuffer);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(serialNumberBuffer, VPD_MAX_BUFFER_SIZE);
|
|
RtlZeroMemory(idBuffer, VPD_MAX_BUFFER_SIZE);
|
|
}
|
|
|
|
//
|
|
// Create a physical device object
|
|
//
|
|
|
|
status = IoCreateDevice(
|
|
Adapter->DeviceObject->DriverObject,
|
|
sizeof(LOGICAL_UNIT_EXTENSION),
|
|
&unicodeDeviceName,
|
|
FILE_DEVICE_MASS_STORAGE,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&pdo
|
|
);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
PCOMMON_EXTENSION commonExtension;
|
|
UCHAR i;
|
|
ULONG bin;
|
|
|
|
UCHAR rawDeviceName[64];
|
|
ANSI_STRING ansiDeviceName;
|
|
|
|
//
|
|
// Set the device object's stack size
|
|
//
|
|
|
|
//
|
|
// We need one stack location for the PDO to do lock tracking and
|
|
// one stack location to issue scsi request to the FDO.
|
|
//
|
|
|
|
pdo->StackSize = 1;
|
|
|
|
pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
|
|
|
|
pdo->Flags |= DO_DIRECT_IO;
|
|
|
|
pdo->AlignmentRequirement = Adapter->DeviceObject->AlignmentRequirement;
|
|
|
|
//
|
|
// Initialize the device extension for the root device
|
|
//
|
|
|
|
commonExtension = pdo->DeviceExtension;
|
|
logicalUnitExtension = pdo->DeviceExtension;
|
|
|
|
RtlZeroMemory(logicalUnitExtension, sizeof(LOGICAL_UNIT_EXTENSION));
|
|
|
|
commonExtension->DeviceObject = pdo;
|
|
commonExtension->IsPdo = TRUE;
|
|
commonExtension->LowerDeviceObject = Adapter->DeviceObject;
|
|
|
|
if(Scsi1) {
|
|
commonExtension->MajorFunction = Scsi1DeviceMajorFunctionTable;
|
|
} else {
|
|
commonExtension->MajorFunction = DeviceMajorFunctionTable;
|
|
}
|
|
|
|
commonExtension->WmiInitialized = FALSE;
|
|
commonExtension->WmiMiniPortSupport =
|
|
Adapter->CommonExtension.WmiMiniPortSupport;
|
|
|
|
commonExtension->WmiScsiPortRegInfoBuf = NULL;
|
|
commonExtension->WmiScsiPortRegInfoBufSize = 0;
|
|
|
|
//
|
|
// Initialize value to zero. It will be incremented once pnp is aware
|
|
// of its existance.
|
|
//
|
|
|
|
commonExtension->RemoveLock = 0;
|
|
#if DBG
|
|
KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
|
|
commonExtension->RemoveTrackingList = NULL;
|
|
|
|
ExInitializeNPagedLookasideList(
|
|
&(commonExtension->RemoveTrackingLookasideList),
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(REMOVE_TRACKING_BLOCK),
|
|
SCSIPORT_TAG_LOCK_TRACKING,
|
|
64);
|
|
|
|
commonExtension->RemoveTrackingLookasideListInitialized = TRUE;
|
|
#else
|
|
commonExtension->RemoveTrackingSpinlock = (ULONG) -1L;
|
|
commonExtension->RemoveTrackingList = (PVOID) -1L;
|
|
#endif
|
|
|
|
commonExtension->CurrentPnpState = 0xff;
|
|
commonExtension->PreviousPnpState = 0xff;
|
|
|
|
//
|
|
// Initialize the remove lock event.
|
|
//
|
|
|
|
KeInitializeEvent(
|
|
&(logicalUnitExtension->CommonExtension.RemoveEvent),
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
logicalUnitExtension->PortNumber = Adapter->PortNumber;
|
|
|
|
logicalUnitExtension->PathId = 0xff;
|
|
logicalUnitExtension->TargetId = 0xff;
|
|
logicalUnitExtension->Lun = 0xff;
|
|
|
|
logicalUnitExtension->HwLogicalUnitExtension = hwExtension;
|
|
|
|
logicalUnitExtension->AdapterExtension = Adapter;
|
|
|
|
//
|
|
// Give the caller the benefit of the doubt.
|
|
//
|
|
|
|
logicalUnitExtension->IsMissing = FALSE;
|
|
|
|
//
|
|
// The device cannot have been enumerated yet.
|
|
//
|
|
|
|
logicalUnitExtension->IsEnumerated = FALSE;
|
|
|
|
//
|
|
// Set timer counters to -1 to inidicate that there are no outstanding
|
|
// requests.
|
|
//
|
|
|
|
logicalUnitExtension->RequestTimeoutCounter = -1;
|
|
|
|
//
|
|
// Initialize the maximum queue depth size.
|
|
//
|
|
|
|
logicalUnitExtension->MaxQueueDepth = 0xFF;
|
|
|
|
//
|
|
// Initialize the request list.
|
|
//
|
|
|
|
InitializeListHead(&logicalUnitExtension->RequestList);
|
|
|
|
//
|
|
// Initialize the push/pop list of SRB_DATA blocks for use with bypass
|
|
// requests.
|
|
//
|
|
|
|
KeInitializeSpinLock(&(logicalUnitExtension->BypassSrbDataSpinLock));
|
|
ExInitializeSListHead(&(logicalUnitExtension->BypassSrbDataList));
|
|
for(i = 0; i < NUMBER_BYPASS_SRB_DATA_BLOCKS; i++) {
|
|
ExInterlockedPushEntrySList(
|
|
&(logicalUnitExtension->BypassSrbDataList),
|
|
&(logicalUnitExtension->BypassSrbDataBlocks[i].Reserved),
|
|
&(logicalUnitExtension->BypassSrbDataSpinLock));
|
|
}
|
|
|
|
//
|
|
// Assume devices are powered on by default.
|
|
//
|
|
|
|
commonExtension->CurrentDeviceState = PowerDeviceD0;
|
|
commonExtension->DesiredDeviceState = PowerDeviceUnspecified;
|
|
|
|
//
|
|
// Assume that we're being initialized in a working system.
|
|
//
|
|
|
|
commonExtension->CurrentSystemState = PowerSystemWorking;
|
|
|
|
//
|
|
// Setup the request sense resources.
|
|
//
|
|
|
|
logicalUnitExtension->RequestSenseIrp = senseIrp;
|
|
|
|
//
|
|
// If this is temporary record that fact in the logical unit extension
|
|
// and save a pointer in the adapter (cleared when the LUN is
|
|
// destroyed). If it's real then stick it into the logical unit list.
|
|
//
|
|
|
|
logicalUnitExtension->IsTemporary = Temporary;
|
|
|
|
//
|
|
// Initialize
|
|
|
|
RtlInitAnsiString(&(logicalUnitExtension->SerialNumber), serialNumberBuffer);
|
|
|
|
if(serialNumberBuffer != NULL) {
|
|
logicalUnitExtension->SerialNumber.MaximumLength = VPD_MAX_BUFFER_SIZE;
|
|
}
|
|
|
|
logicalUnitExtension->DeviceIdentifierPage = idBuffer;
|
|
|
|
//
|
|
// I guess this is as ready to be opened as it ever will be.
|
|
//
|
|
|
|
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
//
|
|
// Initialize the lock & unlock request queue.
|
|
//
|
|
|
|
KeInitializeDeviceQueue(&(logicalUnitExtension->LockRequestQueue));
|
|
logicalUnitExtension->CurrentLockRequest = NULL;
|
|
|
|
} else {
|
|
|
|
DebugPrint((1, "ScsiBusCreatePdo: Error %#08lx creating device object\n",
|
|
status));
|
|
|
|
logicalUnitExtension = NULL;
|
|
|
|
if(hwExtension != NULL) {
|
|
ExFreePool(hwExtension);
|
|
}
|
|
IoFreeIrp(senseIrp);
|
|
|
|
ExFreePool(serialNumberBuffer);
|
|
ExFreePool(idBuffer);
|
|
}
|
|
|
|
*NewLun = logicalUnitExtension;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpSetLogicalUnitAddress(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
)
|
|
{
|
|
UCHAR i;
|
|
ULONG bin;
|
|
|
|
ASSERT_PDO(LogicalUnit->DeviceObject);
|
|
|
|
ASSERT(LogicalUnit->PathId == 0xff);
|
|
ASSERT(LogicalUnit->TargetId == 0xff);
|
|
ASSERT(LogicalUnit->Lun == 0xff);
|
|
|
|
LogicalUnit->PathId = PathId;
|
|
LogicalUnit->TargetId = TargetId;
|
|
LogicalUnit->Lun = Lun;
|
|
|
|
SpAddLogicalUnitToBin(LogicalUnit->AdapterExtension, LogicalUnit);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpClearLogicalUnitAddress(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
ASSERT_PDO(LogicalUnit->DeviceObject);
|
|
ASSERT(LogicalUnit->IsTemporary == TRUE);
|
|
|
|
SpRemoveLogicalUnitFromBin(LogicalUnit->AdapterExtension, LogicalUnit);
|
|
|
|
LogicalUnit->PathId = 0xff;
|
|
LogicalUnit->TargetId = 0xff;
|
|
LogicalUnit->Lun = 0xff;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpCloneAndSwapLogicalUnit(
|
|
IN PLOGICAL_UNIT_EXTENSION TemplateLun,
|
|
IN PINQUIRYDATA InquiryData,
|
|
IN ULONG InquiryDataSize,
|
|
OUT PLOGICAL_UNIT_EXTENSION *NewLun
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will create a new logical unit object with the properties of
|
|
TemplateLun. The supplied inquiry data will be assigned to the new
|
|
logical unit. Finally the new logical unit will be swapped for
|
|
TemplateLun in the adapter's logical unit list.
|
|
|
|
TemplateLun must be a temporary logical unit which has been assigned an
|
|
address and is present in the logical unit lists.
|
|
|
|
Regardless of whether this function succeeds, the TemplateLun will be
|
|
removed from the logical unit list (effectively swapped with nothing).
|
|
|
|
Arguments:
|
|
|
|
TemplateLun - the logical unit to be cloned
|
|
|
|
InquiryData, InquiryDataSize - the inquiry data to be used for the new
|
|
logical unit
|
|
|
|
NewLun - a location to store the pointer to the new logical unit.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS indicates that a new lun has been created and swapped in
|
|
the logical unit list.
|
|
|
|
error status indicates that the new logical unit could not be created for
|
|
some reason.
|
|
|
|
--*/
|
|
{
|
|
PADAPTER_EXTENSION adapter = TemplateLun->AdapterExtension;
|
|
PSCSIPORT_DRIVER_EXTENSION driverExtension =
|
|
IoGetDriverObjectExtension(
|
|
adapter->DeviceObject->DriverObject,
|
|
ScsiPortInitialize);
|
|
|
|
UCHAR pathId, targetId, lun;
|
|
|
|
PVOID serialNumber = NULL;
|
|
USHORT serialNumberLength = 0;
|
|
|
|
PVOID identifier = NULL;
|
|
ULONG identifierLength = 0;
|
|
|
|
PLOGICAL_UNIT_EXTENSION newLun;
|
|
|
|
BOOLEAN scsi1;
|
|
|
|
NTSTATUS status;
|
|
|
|
ASSERT_PDO(TemplateLun->DeviceObject);
|
|
ASSERT(TemplateLun->IsTemporary);
|
|
|
|
*NewLun = NULL;
|
|
|
|
#if DBG
|
|
newLun = GetLogicalUnitExtension(adapter,
|
|
TemplateLun->PathId,
|
|
TemplateLun->TargetId,
|
|
TemplateLun->Lun,
|
|
NULL,
|
|
TRUE);
|
|
ASSERT(newLun == TemplateLun);
|
|
#endif
|
|
|
|
//
|
|
// Wait for any outstanding i/o on the template lun to complete.
|
|
//
|
|
|
|
SpReleaseRemoveLock(TemplateLun->DeviceObject, SpInquireLogicalUnit);
|
|
SpWaitForRemoveLock(TemplateLun->DeviceObject, SP_BASE_REMOVE_LOCK);
|
|
|
|
//
|
|
// Save the address away and then remove the template object from the
|
|
// logical unit list.
|
|
//
|
|
|
|
pathId = TemplateLun->PathId;
|
|
targetId = TemplateLun->TargetId;
|
|
lun = TemplateLun->Lun;
|
|
|
|
SpClearLogicalUnitAddress(TemplateLun);
|
|
|
|
//
|
|
// Before creating a named object, preallocate any resources we'll need
|
|
// that SpCreateLogicalUnit doesn't provide.
|
|
//
|
|
|
|
if(TemplateLun->SerialNumber.Length != 0) {
|
|
serialNumberLength = (TemplateLun->SerialNumber.Length +
|
|
sizeof(UNICODE_NULL));
|
|
|
|
serialNumber = SpAllocatePool(PagedPool,
|
|
serialNumberLength,
|
|
SCSIPORT_TAG_ID_BUFFER,
|
|
TemplateLun->DeviceObject->DriverObject);
|
|
|
|
if(serialNumber == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if(TemplateLun->DeviceIdentifierPageLength != 0) {
|
|
|
|
identifier = SpAllocatePool(
|
|
PagedPool,
|
|
TemplateLun->DeviceIdentifierPageLength,
|
|
SCSIPORT_TAG_ID_BUFFER,
|
|
TemplateLun->DeviceObject->DriverObject);
|
|
|
|
if(identifier == NULL) {
|
|
|
|
if(serialNumber != NULL) {
|
|
ExFreePool(serialNumber);
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the lun is scsi-1 or if the magic registry flag was set then use the
|
|
// scsi 1 dispatch table for this device.
|
|
//
|
|
|
|
if((driverExtension->BusType == BusTypeScsi) &&
|
|
((InquiryData->ANSIVersion == 0) ||
|
|
(InquiryData->ANSIVersion == 1) ||
|
|
(TemplateLun->SpecialFlags.SetLunInCdb))) {
|
|
scsi1 = TRUE;
|
|
} else {
|
|
scsi1 = FALSE;
|
|
}
|
|
|
|
//
|
|
// Now create a new logical unit with the same address.
|
|
//
|
|
|
|
status = SpCreateLogicalUnit(adapter,
|
|
pathId,
|
|
targetId,
|
|
lun,
|
|
FALSE,
|
|
scsi1,
|
|
&newLun);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
if(serialNumber != NULL) {
|
|
ExFreePool(serialNumber);
|
|
}
|
|
if(identifier) {
|
|
ExFreePool(identifier);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Copy the important information from the template logical unit over to
|
|
// the new one. Zero out the original so that we know to reallocate one
|
|
// later.
|
|
//
|
|
|
|
newLun->HwLogicalUnitExtension = TemplateLun->HwLogicalUnitExtension;
|
|
|
|
TemplateLun->HwLogicalUnitExtension = NULL;
|
|
|
|
newLun->LuFlags = TemplateLun->LuFlags;
|
|
newLun->IsVisible = TemplateLun->IsVisible;
|
|
newLun->TargetLunList = TemplateLun->TargetLunList;
|
|
newLun->SpecialFlags = TemplateLun->SpecialFlags;
|
|
|
|
newLun->NeedsVerification = TemplateLun->NeedsVerification;
|
|
|
|
newLun->CommonExtension.SrbFlags = TemplateLun->CommonExtension.SrbFlags;
|
|
|
|
//
|
|
// Copy over any characteristics flags which were set during enumeration.
|
|
//
|
|
|
|
newLun->DeviceObject->Characteristics |=
|
|
(TemplateLun->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
|
|
|
|
//
|
|
// Copy the list of supported vital product data pages.
|
|
//
|
|
|
|
newLun->DeviceIdentifierPageSupported = TemplateLun->DeviceIdentifierPageSupported;
|
|
newLun->SerialNumberPageSupported = TemplateLun->SerialNumberPageSupported;
|
|
|
|
//
|
|
// If this device reports a serial number in it's vital product data then
|
|
// copy it in to the new lun.
|
|
//
|
|
|
|
if(serialNumber != NULL) {
|
|
newLun->SerialNumber.Length = TemplateLun->SerialNumber.Length;
|
|
newLun->SerialNumber.MaximumLength = serialNumberLength;
|
|
newLun->SerialNumber.Buffer = serialNumber;
|
|
RtlCopyMemory(newLun->SerialNumber.Buffer,
|
|
TemplateLun->SerialNumber.Buffer,
|
|
serialNumberLength);
|
|
}
|
|
|
|
//
|
|
// If this has a device identifier page then copy it over two.
|
|
//
|
|
|
|
if(identifier != NULL) {
|
|
newLun->DeviceIdentifierPage = identifier;
|
|
newLun->DeviceIdentifierPageLength =
|
|
TemplateLun->DeviceIdentifierPageLength;
|
|
|
|
RtlCopyMemory(newLun->DeviceIdentifierPage,
|
|
TemplateLun->DeviceIdentifierPage,
|
|
newLun->DeviceIdentifierPageLength);
|
|
}
|
|
|
|
//
|
|
// Copy the inquiry data over.
|
|
//
|
|
|
|
ASSERT(InquiryDataSize <= sizeof(INQUIRYDATA));
|
|
RtlCopyMemory(&(newLun->InquiryData), InquiryData, InquiryDataSize);
|
|
|
|
//
|
|
// Acquire the appropriate remove locks on the new logical unit.
|
|
//
|
|
|
|
SpAcquireRemoveLock(newLun->DeviceObject, SP_BASE_REMOVE_LOCK);
|
|
SpAcquireRemoveLock(newLun->DeviceObject, SpInquireLogicalUnit);
|
|
|
|
//
|
|
// Now insert this new lun into the logical unit list.
|
|
//
|
|
|
|
SpSetLogicalUnitAddress(newLun, pathId, targetId, lun);
|
|
|
|
*NewLun = newLun;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpPrepareLogicalUnitForReuse(
|
|
PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
PADAPTER_EXTENSION adapter = LogicalUnit->AdapterExtension;
|
|
PCOMMON_EXTENSION commonExtension = &(LogicalUnit->CommonExtension);
|
|
|
|
PVOID hwExtension = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
ASSERT_PDO(LogicalUnit->DeviceObject);
|
|
|
|
ASSERT(LogicalUnit->CommonExtension.WmiInitialized == FALSE);
|
|
ASSERT(LogicalUnit->CommonExtension.WmiScsiPortRegInfoBuf == NULL);
|
|
ASSERT(LogicalUnit->CommonExtension.WmiScsiPortRegInfoBufSize == 0);
|
|
|
|
//
|
|
// Clear the remove lock event.
|
|
//
|
|
|
|
ASSERT(LogicalUnit->CommonExtension.RemoveLock == 0);
|
|
|
|
//
|
|
// Initialize the remove lock event.
|
|
//
|
|
|
|
KeClearEvent(&(LogicalUnit->CommonExtension.RemoveEvent));
|
|
|
|
LogicalUnit->PathId = 0xff;
|
|
LogicalUnit->TargetId = 0xff;
|
|
LogicalUnit->Lun = 0xff;
|
|
|
|
//
|
|
// Round the size of the Hardware logical extension to the size of a
|
|
// PVOID and add it to the port driver's logical extension.
|
|
//
|
|
|
|
if((LogicalUnit->HwLogicalUnitExtension == NULL) &&
|
|
(adapter->HwLogicalUnitExtensionSize != 0)) {
|
|
hwExtension = SpAllocatePool(NonPagedPoolCacheAligned,
|
|
adapter->HwLogicalUnitExtensionSize,
|
|
SCSIPORT_TAG_LUN_EXT,
|
|
LogicalUnit->DeviceObject->DriverObject);
|
|
|
|
if(hwExtension == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
LogicalUnit->HwLogicalUnitExtension = hwExtension;
|
|
}
|
|
|
|
if(LogicalUnit->HwLogicalUnitExtension != NULL) {
|
|
RtlZeroMemory(LogicalUnit->HwLogicalUnitExtension,
|
|
adapter->HwLogicalUnitExtensionSize);
|
|
}
|
|
|
|
LogicalUnit->IsMissing = FALSE;
|
|
LogicalUnit->IsVisible = FALSE;
|
|
|
|
ASSERT(LogicalUnit->IsEnumerated == FALSE);
|
|
|
|
//
|
|
// Device has no longer been removed.
|
|
//
|
|
|
|
LogicalUnit->CommonExtension.IsRemoved = NO_REMOVE;
|
|
|
|
//
|
|
// Clear cached infomation about the device identifier(s).
|
|
//
|
|
|
|
LogicalUnit->DeviceIdentifierPageSupported = FALSE;
|
|
LogicalUnit->SerialNumberPageSupported = FALSE;
|
|
|
|
RtlZeroMemory(LogicalUnit->SerialNumber.Buffer,
|
|
LogicalUnit->SerialNumber.MaximumLength);
|
|
LogicalUnit->SerialNumber.Length = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
SpCompareInquiryData(
|
|
IN PUCHAR InquiryData1,
|
|
IN PUCHAR InquiryData2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two sets of inquiry data for equality.
|
|
|
|
Arguments:
|
|
|
|
InquiryData1 - Supplies a pointer to the first inquiry data to compare.
|
|
|
|
InquiryData2 - Supplies a pointer to the second inquiry data to compare.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the supplied inquiry data sets match, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN match;
|
|
UCHAR save1;
|
|
UCHAR save2;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (((PINQUIRYDATA)InquiryData1)->ANSIVersion == 3) {
|
|
|
|
//
|
|
// SCSI3 Specific:
|
|
// Save bytes 6 and 7. These bytes contain vendor specific bits which
|
|
// we're going to exclude from the comparison by just setting them equal
|
|
// to the corresponding bits in InquiryData2. We'll restore them after
|
|
// the comparison.
|
|
//
|
|
|
|
save1 = InquiryData1[6];
|
|
save2 = InquiryData1[7];
|
|
|
|
//
|
|
// Force the vendor specific bits in InquiryData1 to match the
|
|
// corresponsing bits in InquiryData2.
|
|
//
|
|
|
|
InquiryData1[6] &= ~0x20;
|
|
InquiryData1[7] &= ~0x01;
|
|
InquiryData1[6] |= (InquiryData2[6] & 0x20);
|
|
InquiryData1[7] |= (InquiryData2[7] & 0x01);
|
|
}
|
|
|
|
//
|
|
// Compare the entire inquiry data blob.
|
|
//
|
|
|
|
match = RtlEqualMemory((((PUCHAR) InquiryData1) + 1),
|
|
(((PUCHAR) InquiryData2) + 1),
|
|
(INQUIRYDATABUFFERSIZE - 1));
|
|
|
|
if (((PINQUIRYDATA)InquiryData1)->ANSIVersion == 3) {
|
|
|
|
//
|
|
// SCSI3 Specific:
|
|
// Restore bytes 6 and 7 to their original state.
|
|
//
|
|
|
|
InquiryData1[6] = save1;
|
|
InquiryData1[7] = save2;
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpInquireLogicalUnit(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN BOOLEAN ExposeDisconnectedLuns,
|
|
IN OPTIONAL PLOGICAL_UNIT_EXTENSION RescanLun,
|
|
OUT PLOGICAL_UNIT_EXTENSION *LogicalUnit,
|
|
OUT PBOOLEAN CheckNextLun
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will issue an inquiry to the logical unit at the specified
|
|
address. If there is not already a device object allocated for that
|
|
logical unit, it will create one. If it turns out the device does not
|
|
exist, the logical unit can be destroyed before returning.
|
|
|
|
If the logical unit exists, this routine will clear the PD_RESCAN_ACTIVE
|
|
flag in the LuFlags to indicate that the unit is safe.
|
|
|
|
If it does not respond, the IsMissing flag will be set to indicate that the
|
|
unit should not be reported during enumeration. If the IsRemoved flag has
|
|
already been set on the logical unit extension, the device object will be
|
|
destroyed. Otherwise the device object will not be destroyed until a
|
|
remove can be issued.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter which this device would exist on
|
|
|
|
PathId, TargetId, Lun - the address of the lun to inquire.
|
|
|
|
ExposeDisconnectedLuns - indicates whether luns with a qualifier of
|
|
disconnected should be instantiated.
|
|
|
|
RescanLun - a pointer to the logical unit extension to be used when
|
|
checking logical unit numbers which do not currently have an
|
|
extension associated with them.
|
|
|
|
LogicalUnit - the logical unit created for this address - valid if
|
|
success is returned.
|
|
|
|
CheckNextLun - indicates whether the caller should check the next
|
|
address for a logical unit.
|
|
|
|
Return Value:
|
|
|
|
STATUS_NO_SUCH_DEVICE if the device does not exist.
|
|
|
|
STATUS_SUCCESS if the device does exist.
|
|
|
|
error description otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
INQUIRYDATA inquiryData;
|
|
|
|
BOOLEAN newDevice = FALSE;
|
|
BOOLEAN deviceMismatch = FALSE;
|
|
|
|
UCHAR bytesReturned;
|
|
|
|
NTSTATUS status;
|
|
|
|
*LogicalUnit = NULL;
|
|
*CheckNextLun = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(TargetId != BreakOnTarget);
|
|
|
|
//
|
|
// Find or create the device object for this address. if it exists we'll
|
|
// grab a temporary lock (using SpInquireLogicalUnit as a tag).
|
|
//
|
|
|
|
logicalUnit = GetLogicalUnitExtension(Adapter,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
SpInquireLogicalUnit,
|
|
TRUE);
|
|
|
|
if(logicalUnit == NULL) {
|
|
|
|
if(!ARGUMENT_PRESENT(RescanLun)) {
|
|
|
|
//
|
|
// No RescanLun was provided (generally means we're low on memory).
|
|
// Don't scan this logical unit.
|
|
//
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT(RescanLun->IsTemporary == TRUE);
|
|
|
|
//
|
|
// Acquire the temporary lock for the rescan lun. We also grab the
|
|
// base lock here.
|
|
//
|
|
|
|
SpAcquireRemoveLock(RescanLun->DeviceObject, SP_BASE_REMOVE_LOCK);
|
|
SpAcquireRemoveLock(RescanLun->DeviceObject, SpInquireLogicalUnit);
|
|
|
|
//
|
|
// Set the address of the RescanLun appropriately - this operation
|
|
// will make the logical unit available for our use.
|
|
//
|
|
|
|
SpSetLogicalUnitAddress(RescanLun, PathId, TargetId, Lun);
|
|
|
|
logicalUnit = RescanLun;
|
|
newDevice = TRUE;
|
|
|
|
} else {
|
|
ASSERT(logicalUnit->IsTemporary == FALSE);
|
|
|
|
if(logicalUnit->IsMissing) {
|
|
|
|
DebugPrint((1, "SpInquireLogicalUnit: logical unit @ (%d,%d,%d) "
|
|
"(%#p) is marked as missing and will not be "
|
|
"rescanned\n",
|
|
PathId, TargetId, Lun,
|
|
logicalUnit->DeviceObject));
|
|
|
|
SpReleaseRemoveLock(logicalUnit->DeviceObject, SpInquireLogicalUnit);
|
|
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Issue an inquiry to the potential logical unit.
|
|
//
|
|
|
|
DebugPrint((2, "SpInquireTarget: Try %s device @ Bus %d, Target %d, "
|
|
"Lun %d\n",
|
|
(newDevice ? "new" : "existing"),
|
|
PathId,
|
|
TargetId,
|
|
Lun));
|
|
|
|
status = IssueInquiry(logicalUnit, FALSE, 0, &inquiryData, &bytesReturned);
|
|
|
|
//
|
|
// If the inquiry succeeds then check the data returned to determine if
|
|
// there's a device there we should expose.
|
|
//
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
UCHAR qualifier;
|
|
BOOLEAN present = FALSE;
|
|
|
|
//
|
|
// Check in the registry for special device flags for this lun.
|
|
// If this is disconnected then set the qualifier to be 0 so that we
|
|
// use the normal hardware ids instead of the "disconnected" ones.
|
|
//
|
|
|
|
qualifier = inquiryData.DeviceTypeQualifier;
|
|
|
|
SpCheckSpecialDeviceFlags(logicalUnit, &(inquiryData));
|
|
|
|
//
|
|
// The inquiry was successful. Determine whether a device is present.
|
|
//
|
|
|
|
switch(qualifier) {
|
|
case DEVICE_QUALIFIER_ACTIVE: {
|
|
|
|
//
|
|
// Active devices are always present.
|
|
//
|
|
|
|
present = TRUE;
|
|
break;
|
|
}
|
|
|
|
case DEVICE_QUALIFIER_NOT_ACTIVE: {
|
|
|
|
if (Lun == 0) {
|
|
//
|
|
// If we're using REPORT_LUNS commands for LUN 0 of a target
|
|
// then we should always indicate that LUN 0 is present.
|
|
//
|
|
|
|
if ((inquiryData.HiSupport == TRUE) ||
|
|
(logicalUnit->SpecialFlags.LargeLuns == TRUE)) {
|
|
present = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// Expose inactive luns only if the caller has requested that
|
|
// we do so.
|
|
//
|
|
|
|
present = ExposeDisconnectedLuns;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case DEVICE_QUALIFIER_NOT_SUPPORTED: {
|
|
present = FALSE;
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
present = TRUE;
|
|
break;
|
|
}
|
|
};
|
|
|
|
if(present == FALSE) {
|
|
|
|
//
|
|
// setup an error value so we'll clean up the logical unit.
|
|
// No need to do any more processing in this case.
|
|
//
|
|
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else if(newDevice == FALSE) {
|
|
|
|
//
|
|
// Verify that the inquiry data hasn't changed since the last time
|
|
// we did a rescan. Ignore the device type qualifier in this
|
|
// check.
|
|
//
|
|
|
|
deviceMismatch = FALSE;
|
|
|
|
if(inquiryData.DeviceType != logicalUnit->InquiryData.DeviceType) {
|
|
|
|
DebugPrint((1, "SpInquireTarget: Found different type of "
|
|
"device @ (%d,%d,%d)\n",
|
|
PathId,
|
|
TargetId,
|
|
Lun));
|
|
|
|
deviceMismatch = TRUE;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else if(inquiryData.DeviceTypeQualifier !=
|
|
logicalUnit->InquiryData.DeviceTypeQualifier) {
|
|
|
|
//
|
|
// The device qualifiers don't match. This isn't necessarily
|
|
// a device mismatch if the existing device just went offline.
|
|
// lower down we'll check the remaining inquiry data to
|
|
// ensure that the LUN hasn't changed.
|
|
//
|
|
|
|
DebugPrint((1, "SpInquireLogicalUnit: Device @ (%d,%d,%d) type "
|
|
"qualifier was %d is now %d\n",
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
logicalUnit->InquiryData.DeviceTypeQualifier,
|
|
inquiryData.DeviceTypeQualifier
|
|
));
|
|
|
|
//
|
|
// If the device was offline but no longer is then we
|
|
// treat this as a device mismatch. If the device has gone
|
|
// offline then we pretend it's the same device.
|
|
//
|
|
// the goal is to provide PNP with a new device object when
|
|
// bringing a device online, but to reuse the same device
|
|
// object when bringing the device offline.
|
|
//
|
|
|
|
if(logicalUnit->InquiryData.DeviceTypeQualifier ==
|
|
DEVICE_QUALIFIER_NOT_ACTIVE) {
|
|
|
|
DebugPrint((1, "SpInquireLogicalUnit: device mismatch\n"));
|
|
deviceMismatch = TRUE;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else {
|
|
|
|
DebugPrint((1, "SpInquireLogicalUnit: device went offline\n"));
|
|
deviceMismatch = FALSE;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (deviceMismatch == FALSE) {
|
|
|
|
//
|
|
// Ok, the device type and qualifier are compatible. Now we
|
|
// need to compare all applicable parts of the inquiry
|
|
// data with the data we already have on the device at this
|
|
// address to see if the device that answered this time is the
|
|
// same one we found last time.
|
|
//
|
|
|
|
BOOLEAN same = SpCompareInquiryData(
|
|
(PUCHAR)&(inquiryData),
|
|
(PUCHAR)&(logicalUnit->InquiryData));
|
|
|
|
if (same == FALSE) {
|
|
|
|
//
|
|
// Despite the fact that the device type & qualifier are
|
|
// compatible, a mismatch still occurred.
|
|
//
|
|
|
|
deviceMismatch = TRUE;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
DebugPrint((1, "SpInquireLogicalUnit: Device @ (%d,%d,%d) has "
|
|
"changed\n",
|
|
PathId,
|
|
TargetId,
|
|
Lun));
|
|
} else {
|
|
|
|
//
|
|
// The device that answered is the same one we found
|
|
// earlier. Depending on the SCSI version of the device,
|
|
// we might need to update the vendor specific portions of
|
|
// the existing inquiry data for this device.
|
|
//
|
|
|
|
if (inquiryData.ANSIVersion == 3) {
|
|
|
|
//
|
|
// For SCSI 3 devices, bytes 6 and 7 contain vendor
|
|
// specific bits that may differ between bus scans.
|
|
// Update these bytes of the existing inquiry data.
|
|
//
|
|
|
|
((PUCHAR)&(logicalUnit->InquiryData))[6] =
|
|
((PUCHAR)&(inquiryData))[6];
|
|
((PUCHAR)&(logicalUnit->InquiryData))[7] =
|
|
((PUCHAR)&(inquiryData))[7];
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
DebugPrint((1, "SpInquireTarget: Found new %sDevice at address "
|
|
"(%d,%d,%d)\n",
|
|
(inquiryData.RemovableMedia ? "Removable " : ""),
|
|
PathId,
|
|
TargetId,
|
|
Lun));
|
|
|
|
|
|
}
|
|
|
|
if(NT_SUCCESS(status) && (deviceMismatch == FALSE)) {
|
|
|
|
deviceMismatch = SpGetDeviceIdentifiers(logicalUnit, newDevice);
|
|
|
|
if(deviceMismatch == FALSE) {
|
|
ASSERT(newDevice);
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
*CheckNextLun = FALSE;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Nothing was found at this address. If it's a new lun which hasn't
|
|
// been enumerated yet then just destroy it here. If, however, it
|
|
// has been enumerated we have to mark it as missing and wait for
|
|
// PNP to learn that it's gone and ask us to remove it. Then we can
|
|
// destroy it.
|
|
//
|
|
// If we were just using the RescanLun to check this address then do
|
|
// nothing - the rescan lun will be reset down below.
|
|
//
|
|
|
|
logicalUnit->IsMissing = TRUE;
|
|
|
|
if(newDevice) {
|
|
|
|
//
|
|
// Release the temporary lock. the base one will be released at
|
|
// the end of this routine.
|
|
//
|
|
|
|
SpReleaseRemoveLock(logicalUnit->DeviceObject,
|
|
SpInquireLogicalUnit);
|
|
logicalUnit = NULL;
|
|
|
|
} else if (logicalUnit->IsEnumerated == FALSE) {
|
|
|
|
//
|
|
// It's safe to destroy this device object ourself since it's not
|
|
// a device PNP is aware of. However we may have outstanding i/o
|
|
// due to pass-through requests or legacy class driver so we need
|
|
// to properly wait for all i/o to complete.
|
|
//
|
|
|
|
logicalUnit->CommonExtension.CurrentPnpState =
|
|
IRP_MN_REMOVE_DEVICE;
|
|
|
|
SpReleaseRemoveLock(logicalUnit->DeviceObject, SpInquireLogicalUnit);
|
|
|
|
//
|
|
// Mark this device temporarily as visible so that
|
|
// SpRemoveLogicalUnit will do the right thing. Since the rescan
|
|
// active bit is set the enumeration code won't return this device.
|
|
//
|
|
|
|
logicalUnit->IsVisible = TRUE;
|
|
|
|
ASSERT(logicalUnit->IsEnumerated == FALSE);
|
|
ASSERT(logicalUnit->IsMissing == TRUE);
|
|
ASSERT(logicalUnit->IsVisible == TRUE);
|
|
|
|
SpRemoveLogicalUnit(logicalUnit, IRP_MN_REMOVE_DEVICE);
|
|
|
|
if(deviceMismatch) {
|
|
|
|
//
|
|
// Call this routine again. This is the only recursion and
|
|
// since we've deleted the device object there should be no
|
|
// cause for a mismatch there.
|
|
//
|
|
|
|
status = SpInquireLogicalUnit(Adapter,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
ExposeDisconnectedLuns,
|
|
RescanLun,
|
|
LogicalUnit,
|
|
CheckNextLun);
|
|
}
|
|
|
|
return status;
|
|
|
|
} else {
|
|
|
|
//
|
|
// CODEWORK - freeze and flush the queue. This way we don't need
|
|
// to deal with handling get next request calls
|
|
//
|
|
|
|
//
|
|
// Mark the device as being mismatched so that it's destruction
|
|
// will cause us to rescan the bus (and pickup the new device).
|
|
//
|
|
|
|
if(deviceMismatch) {
|
|
logicalUnit->IsMismatched = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
logicalUnit->IsMissing = FALSE;
|
|
|
|
if(newDevice) {
|
|
|
|
status = SpCloneAndSwapLogicalUnit(logicalUnit,
|
|
&(inquiryData),
|
|
bytesReturned,
|
|
&logicalUnit);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
logicalUnit = NULL;
|
|
}
|
|
|
|
ASSERT(logicalUnit != RescanLun);
|
|
|
|
//
|
|
// Clear the new device flag so we don't attempt to clear the
|
|
// address of the RescanLun down below.
|
|
//
|
|
|
|
newDevice = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Update the state of the device and the device map entry if
|
|
// necessary.
|
|
//
|
|
|
|
if(logicalUnit->InquiryData.DeviceTypeQualifier !=
|
|
inquiryData.DeviceTypeQualifier) {
|
|
|
|
logicalUnit->InquiryData.DeviceTypeQualifier =
|
|
inquiryData.DeviceTypeQualifier;
|
|
|
|
SpUpdateLogicalUnitDeviceMapEntry(logicalUnit);
|
|
}
|
|
}
|
|
|
|
if(logicalUnit != NULL) {
|
|
|
|
if(logicalUnit->InquiryData.DeviceTypeQualifier ==
|
|
DEVICE_QUALIFIER_NOT_ACTIVE) {
|
|
logicalUnit->IsVisible = FALSE;
|
|
|
|
//
|
|
// Scsiport won't create a device-map entry for this device since
|
|
// it's never been exposed to PNP (and definately won't be now).
|
|
// Create one here. If the init-device routine tries to generate
|
|
// one later on down the road it will deal with this case just fine.
|
|
//
|
|
|
|
SpBuildDeviceMapEntry(&(logicalUnit->CommonExtension));
|
|
} else {
|
|
logicalUnit->IsVisible = TRUE;
|
|
}
|
|
|
|
if(inquiryData.RemovableMedia) {
|
|
SET_FLAG(logicalUnit->DeviceObject->Characteristics,
|
|
FILE_REMOVABLE_MEDIA);
|
|
}
|
|
|
|
ASSERT(logicalUnit->IsTemporary != TRUE);
|
|
}
|
|
|
|
*LogicalUnit = logicalUnit;
|
|
}
|
|
|
|
//
|
|
// If this was a new device then clean up the RescanLun.
|
|
//
|
|
|
|
if(newDevice) {
|
|
SpWaitForRemoveLock(RescanLun->DeviceObject, SP_BASE_REMOVE_LOCK);
|
|
SpClearLogicalUnitAddress(RescanLun);
|
|
}
|
|
|
|
if(logicalUnit) {
|
|
ASSERT(logicalUnit != RescanLun);
|
|
SpReleaseRemoveLock(logicalUnit->DeviceObject, SpInquireLogicalUnit);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpSendSrbSynchronous(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN OPTIONAL PIRP Irp,
|
|
IN OPTIONAL PMDL Mdl,
|
|
IN PVOID DataBuffer,
|
|
IN ULONG TransferLength,
|
|
IN OPTIONAL PVOID SenseInfoBuffer,
|
|
IN OPTIONAL UCHAR SenseInfoBufferLength,
|
|
OUT PULONG BytesReturned
|
|
)
|
|
{
|
|
KEVENT event;
|
|
|
|
BOOLEAN irpAllocated = FALSE;
|
|
BOOLEAN mdlAllocated = FALSE;
|
|
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
PSENSE_DATA senseInfo = SenseInfoBuffer;
|
|
|
|
ULONG retryCount = 0;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
SendSrbSynchronousRetry:
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// If the caller provided an IRP we'll use it - if not we allocate one
|
|
// here.
|
|
//
|
|
|
|
if(!ARGUMENT_PRESENT(Irp)) {
|
|
|
|
Irp = SpAllocateIrp(LogicalUnit->DeviceObject->StackSize,
|
|
FALSE,
|
|
LogicalUnit->DeviceObject->DriverObject);
|
|
|
|
if(Irp == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
irpAllocated = TRUE;
|
|
}
|
|
|
|
if(ARGUMENT_PRESENT(DataBuffer)) {
|
|
ASSERT(TransferLength != 0);
|
|
|
|
if(!ARGUMENT_PRESENT(Mdl)) {
|
|
|
|
Mdl = SpAllocateMdl(DataBuffer,
|
|
TransferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL,
|
|
LogicalUnit->DeviceObject->DriverObject);
|
|
|
|
if(Mdl == NULL) {
|
|
IoFreeIrp(Irp);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool(Mdl);
|
|
}
|
|
|
|
Irp->MdlAddress = Mdl;
|
|
} else {
|
|
ASSERT(TransferLength == 0);
|
|
ASSERT(!ARGUMENT_PRESENT(Mdl));
|
|
}
|
|
|
|
irpStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Mark the minor function to indicate that this is an internal scsiport
|
|
// request and that the start state of the device can be ignored.
|
|
//
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
irpStack->MinorFunction = 1;
|
|
|
|
irpStack->Parameters.Scsi.Srb = Srb;
|
|
|
|
Srb->SrbStatus = Srb->ScsiStatus = 0;
|
|
|
|
Srb->OriginalRequest = Irp;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
if(ARGUMENT_PRESENT(SenseInfoBuffer)) {
|
|
Srb->SenseInfoBuffer = SenseInfoBuffer;
|
|
Srb->SenseInfoBufferLength = SenseInfoBufferLength;
|
|
} else {
|
|
Srb->SenseInfoBuffer = NULL;
|
|
Srb->SenseInfoBufferLength = 0;
|
|
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
|
|
}
|
|
|
|
if(ARGUMENT_PRESENT(Mdl)) {
|
|
Srb->DataBuffer = MmGetMdlVirtualAddress(Mdl);
|
|
Srb->DataTransferLength = TransferLength;
|
|
} else {
|
|
Srb->DataBuffer = NULL;
|
|
Srb->DataTransferLength = 0;
|
|
}
|
|
|
|
//
|
|
// Call port driver to handle this request.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
SpSignalCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
KeEnterCriticalRegion();
|
|
|
|
status = IoCallDriver(LogicalUnit->DeviceObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
*BytesReturned = (ULONG) Irp->IoStatus.Information;
|
|
|
|
if(Srb->SrbStatus == SRB_STATUS_PENDING) {
|
|
|
|
//
|
|
// Request was never even issued to the controller.
|
|
//
|
|
|
|
ASSERT(!NT_SUCCESS(status));
|
|
|
|
} else if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((2,"SpSendSrbSynchronous: Command failed SRB status %x\n",
|
|
Srb->SrbStatus));
|
|
|
|
//
|
|
// Unfreeze queue if necessary
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
|
|
DebugPrint((3, "SpSendSrbSynchronous: Unfreeze Queue TID %d\n",
|
|
Srb->TargetId));
|
|
|
|
LogicalUnit->LuFlags &= ~LU_QUEUE_FROZEN;
|
|
|
|
GetNextLuRequestWithoutLock(LogicalUnit);
|
|
}
|
|
|
|
//
|
|
// NOTE: if INQUIRY fails with a data underrun,
|
|
// indicate success and let the class drivers
|
|
// determine whether the inquiry information
|
|
// is useful.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
//
|
|
// Copy INQUIRY buffer to LUNINFO.
|
|
//
|
|
|
|
DebugPrint((1,"SpSendSrbSynchronous: Data underrun at TID %d\n",
|
|
LogicalUnit->TargetId));
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(senseInfo->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)) {
|
|
|
|
//
|
|
// A sense key of illegal request was recieved. This indicates
|
|
// that the logical unit number of not valid but there is a
|
|
// target device out there.
|
|
//
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else {
|
|
//
|
|
// If the selection did not time out then retry the request.
|
|
//
|
|
|
|
if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT) &&
|
|
(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_NO_DEVICE) &&
|
|
(retryCount++ < INQUIRY_RETRY_COUNT)) {
|
|
|
|
DebugPrint((2,"SpSendSrbSynchronous: Retry %d\n", retryCount));
|
|
KeLeaveCriticalRegion();
|
|
goto SendSrbSynchronousRetry;
|
|
}
|
|
|
|
status = SpTranslateScsiStatus(Srb);
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
KeLeaveCriticalRegion();
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IssueInquiry(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN BOOLEAN EnableVitalProductData,
|
|
IN UCHAR PageCode,
|
|
OUT PVOID InquiryData,
|
|
OUT PUCHAR BytesReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build IRP, SRB and CDB for SCSI INQUIRY command.
|
|
|
|
This routine MUST be called while holding the enumeration lock.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - address of the logical unit extension
|
|
|
|
EnableVitalProductData - indicates whether the EVPD bit should be set in
|
|
the inquiry data causing the LUN to return product
|
|
data pages (specified by page code below) rather
|
|
than the standard inquiry data.
|
|
|
|
PageCode - which VPD page to retrieve
|
|
|
|
InquiryData - the location to store the inquiry data for the LUN.
|
|
|
|
BytesReturned - the number of bytes of inquiry data returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
PVOID dataBuffer;
|
|
PSENSE_DATA senseInfoBuffer;
|
|
|
|
UCHAR allocationLength;
|
|
ULONG bytesReturned;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
dataBuffer = LogicalUnit->AdapterExtension->InquiryBuffer;
|
|
senseInfoBuffer = LogicalUnit->AdapterExtension->InquirySenseBuffer;
|
|
|
|
ASSERT(dataBuffer != NULL);
|
|
ASSERT(senseInfoBuffer != NULL);
|
|
|
|
irp = LogicalUnit->AdapterExtension->InquiryIrp;
|
|
|
|
IoInitializeIrp(irp,
|
|
IoSizeOfIrp(INQUIRY_STACK_LOCATIONS),
|
|
INQUIRY_STACK_LOCATIONS);
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
RtlZeroMemory(dataBuffer, SP_INQUIRY_BUFFER_SIZE);
|
|
RtlZeroMemory(senseInfoBuffer, SENSE_BUFFER_SIZE);
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Set flags to disable synchronous negociation.
|
|
//
|
|
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
//
|
|
// Set timeout to 2 seconds.
|
|
//
|
|
|
|
srb.TimeOutValue = 4;
|
|
|
|
srb.CdbLength = 6;
|
|
|
|
cdb = (PCDB)srb.Cdb;
|
|
|
|
//
|
|
// Set CDB operation code.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
|
|
|
|
//
|
|
// Set allocation length to inquiry data buffer size.
|
|
//
|
|
|
|
if(EnableVitalProductData) {
|
|
allocationLength = VPD_MAX_BUFFER_SIZE;
|
|
} else {
|
|
allocationLength = INQUIRYDATABUFFERSIZE;
|
|
}
|
|
|
|
cdb->CDB6INQUIRY3.AllocationLength = allocationLength;
|
|
|
|
cdb->CDB6INQUIRY3.EnableVitalProductData = TEST(EnableVitalProductData);
|
|
|
|
if(EnableVitalProductData == FALSE) {
|
|
ASSERT(PageCode == 0);
|
|
}
|
|
|
|
cdb->CDB6INQUIRY3.PageCode = PageCode;
|
|
|
|
status = SpSendSrbSynchronous(LogicalUnit,
|
|
&srb,
|
|
irp,
|
|
LogicalUnit->AdapterExtension->InquiryMdl,
|
|
dataBuffer,
|
|
allocationLength,
|
|
senseInfoBuffer,
|
|
SENSE_BUFFER_SIZE,
|
|
&bytesReturned
|
|
);
|
|
|
|
ASSERT(bytesReturned <= allocationLength);
|
|
|
|
//
|
|
// Return the inquiry data for the device if the call was successful.
|
|
// Otherwise cleanup.
|
|
//
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If the caller passed in the inquiry buffer then don't bother to copy
|
|
// the data.
|
|
//
|
|
|
|
if(InquiryData != dataBuffer) {
|
|
RtlCopyMemory(InquiryData, dataBuffer, bytesReturned);
|
|
}
|
|
*BytesReturned = (UCHAR) bytesReturned;
|
|
} else if(BreakOnMissingLun) {
|
|
ASSERT(LogicalUnit->IsTemporary == TRUE);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpGetDeviceIdentifiers(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN BOOLEAN NewDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retreives the device identifiers supported by the logical
|
|
unit in question and compares them to the ones (if any) which are currently
|
|
saved in the LogicalUnit extension. If they do not match this routine
|
|
will return false to indicate a device mismatch.
|
|
|
|
As a side effect this routine will save the serial numbers for new devices
|
|
in the logical unit extension, as well as a list of the supported vital
|
|
product data pages.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - the logical unit being prodded.
|
|
|
|
NewDevice - whether this device has been prodded before or not. If it has
|
|
not been then the list of supported EVPD pages will need to be
|
|
retreived.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the data retrieved matches the data which was stored in the
|
|
logical unit extension (TRUE is always returned for a new device).
|
|
|
|
FALSE otherwise.
|
|
--*/
|
|
{
|
|
PVOID buffer = LogicalUnit->AdapterExtension->InquiryBuffer;
|
|
|
|
UCHAR bytesReturned;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If this is a new device then get the list of supported VPD pages and
|
|
// process it.
|
|
//
|
|
|
|
if(NewDevice) {
|
|
PVPD_SUPPORTED_PAGES_PAGE supportedPages = buffer;
|
|
UCHAR i;
|
|
|
|
ASSERT(LogicalUnit->DeviceIdentifierPageSupported == FALSE);
|
|
ASSERT(LogicalUnit->SerialNumberPageSupported == FALSE);
|
|
|
|
//
|
|
// If this device is a known non-compliant device that does not support
|
|
// VPD 0x00 but does support VPDs 0x80 and/or 0x83, bypass the INQUIRY
|
|
// and just indicate that the LU does support the other VPDs based on
|
|
// the special flags.
|
|
//
|
|
|
|
if (LogicalUnit->SpecialFlags.NonStandardVPD == 0) {
|
|
|
|
status = IssueInquiry(LogicalUnit,
|
|
TRUE,
|
|
VPD_SUPPORTED_PAGES,
|
|
buffer,
|
|
&bytesReturned);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if(bytesReturned < sizeof(VPD_SUPPORTED_PAGES_PAGE)) {
|
|
|
|
//
|
|
// If the device didn't return enough data to include any pages
|
|
// then we're done.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
for(i = 0; i < supportedPages->PageLength; i++) {
|
|
switch(supportedPages->SupportedPageList[i]) {
|
|
case VPD_SERIAL_NUMBER: {
|
|
LogicalUnit->SerialNumberPageSupported = TRUE;
|
|
break;
|
|
}
|
|
case VPD_DEVICE_IDENTIFIERS: {
|
|
LogicalUnit->DeviceIdentifierPageSupported = TRUE;
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ULONG vpdFlags = LogicalUnit->SpecialFlags.NonStandardVPD;
|
|
|
|
//
|
|
// This is one of the devices that does not support VPD 0x00 but
|
|
// does support one or more of the other VPD pages.
|
|
//
|
|
|
|
LogicalUnit->SerialNumberPageSupported =
|
|
(vpdFlags & NON_STANDARD_VPD_SUPPORTS_PAGE80) ? TRUE : FALSE;
|
|
|
|
LogicalUnit->DeviceIdentifierPageSupported =
|
|
(vpdFlags & NON_STANDARD_VPD_SUPPORTS_PAGE83) ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this device supports the serial number page then retrieve it,
|
|
// convert it into an ansi string, and compare it to the one previously
|
|
// retreived (if there was a previous attempt).
|
|
//
|
|
|
|
if(LogicalUnit->SerialNumberPageSupported) {
|
|
PVPD_SERIAL_NUMBER_PAGE serialNumberPage = buffer;
|
|
ANSI_STRING serialNumber;
|
|
|
|
status = IssueInquiry(LogicalUnit,
|
|
TRUE,
|
|
VPD_SERIAL_NUMBER,
|
|
serialNumberPage,
|
|
&bytesReturned);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((0, "SpGetDeviceIdentifiers: Error %#08lx retreiving "
|
|
"serial number page from lun %#p\n",
|
|
status, LogicalUnit));
|
|
|
|
//
|
|
// We can't get the serial number - give this device the benefit
|
|
// of the doubt.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Fix for bug #143313:
|
|
// On rare occasions, junk appears to get copied into the serial
|
|
// number buffer. This causes us problems because the junk is
|
|
// interpreted as part of the serial number. When we compare the
|
|
// string containing junk to a previously acquired serial number, the
|
|
// comparison fails. In an effort to fix, I'll zero out all bytes
|
|
// in the buffer following the actual serial number. This will only
|
|
// work if the PageSize reported by the device does NOT include the
|
|
// junk bytes.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
serialNumberPage->SerialNumber + serialNumberPage->PageLength,
|
|
SP_INQUIRY_BUFFER_SIZE - 4 - serialNumberPage->PageLength);
|
|
|
|
//
|
|
// If this is a device known to return binary SN data, convert the
|
|
// returned bytes to ascii.
|
|
//
|
|
// Note: It is assumed that the SN data is numeric. Any bytes that
|
|
// cannot be converted to an ASCII hex number, are left alone.
|
|
//
|
|
|
|
if (LogicalUnit->SpecialFlags.BinarySN != 0) {
|
|
int i;
|
|
PUCHAR p = serialNumberPage->SerialNumber;
|
|
for (i = 0; i < serialNumberPage->PageLength; i++) {
|
|
if (*p < 0xa) {
|
|
*p += '0';
|
|
} else if (*p < 0x10) {
|
|
*p += 'A';
|
|
} else {
|
|
ASSERT(FALSE && "Data out of range");
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a string using the serial number. The buffer was zeroed
|
|
// before transfer (and is one character longer than the max buffer
|
|
// which can be returned) so the string is null terminated.
|
|
//
|
|
|
|
RtlInitAnsiString(&(serialNumber), serialNumberPage->SerialNumber);
|
|
|
|
if(NewDevice) {
|
|
|
|
//
|
|
// A new device will always have a large buffer into which we can
|
|
// copy the string. The clone & swap process will take care of
|
|
// moving this into a smaller sized buffer.
|
|
//
|
|
|
|
ASSERT(LogicalUnit->SerialNumber.MaximumLength != 0);
|
|
ASSERT(LogicalUnit->SerialNumber.Buffer != NULL);
|
|
|
|
RtlCopyString(&(LogicalUnit->SerialNumber), &serialNumber);
|
|
|
|
} else if(LogicalUnit->SerialNumber.Buffer == NULL &&
|
|
serialNumber.Length != 0) {
|
|
|
|
//
|
|
// ISSUE-2000-25-02-peterwie
|
|
// We didn't previously have a serial number. Since the device
|
|
// claimed that it supported one it's likely we got an error back
|
|
// when we tried to retreive it. Since we didn't get back one
|
|
// now it was a transient error (ie. not likely to be a violation
|
|
// of the spec). Should we assign the serial number to the device
|
|
// here? Or should we have failed to instantiate a device with
|
|
// a serial number we couldn't retreive?
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
|
|
} else if(RtlEqualString(&serialNumber,
|
|
&(LogicalUnit->SerialNumber),
|
|
FALSE) == FALSE) {
|
|
DebugPrint((1, "SpInquireLogicalUnit: serial number mismatch\n"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this device supports the device identifiers page then read it out.
|
|
// We don't use this page to check for mismatches at the moment, so we
|
|
// just read it out of the device if this is a new device.
|
|
//
|
|
|
|
if((NewDevice == TRUE) && (LogicalUnit->DeviceIdentifierPageSupported)) {
|
|
|
|
status = IssueInquiry(LogicalUnit,
|
|
TRUE,
|
|
VPD_DEVICE_IDENTIFIERS,
|
|
buffer,
|
|
&bytesReturned);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Copy the page into the buffer allocated in the template logical
|
|
// unit. The clone & swap process will take care of moving this
|
|
// into an appropriately sized buffer in the new lun.
|
|
//
|
|
|
|
ASSERT(LogicalUnit->DeviceIdentifierPage != NULL);
|
|
|
|
RtlCopyMemory(LogicalUnit->DeviceIdentifierPage,
|
|
buffer,
|
|
bytesReturned);
|
|
LogicalUnit->DeviceIdentifierPageLength = bytesReturned;
|
|
} else {
|
|
DebugPrint((1, "SpGetDeviceIdentifiers: Error %#08lx retreiving "
|
|
"serial number page from lun %#p\n",
|
|
status, LogicalUnit));
|
|
LogicalUnit->DeviceIdentifierPageLength = 0;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|