|
|
/*++
Copyright (C) Microsoft Corporation, 2000
Module Name:
enum.c
Abstract:
This file contains device enumeration routines
Environment:
kernel mode only
Revision History:
--*/ #include "port.h"
VOID iSpEnumerateDevicesAsynchronous( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PISCSI_FDO_EXTENSION fdoExtension; PCOMMON_EXTENSION commonExtension; PISCSI_CONNECTION iScsiConnection; PIRP Irp; LARGE_INTEGER Timeout; NTSTATUS status = STATUS_SUCCESS; UCHAR oldIrql;
fdoExtension = (PISCSI_FDO_EXTENSION) Context; commonExtension = DeviceObject->DeviceExtension;
IoFreeWorkItem(fdoExtension->EnumerationWorkItem); fdoExtension->EnumerationWorkItem = NULL;
//
// Local network nodes should be setup at this point.
// If not, fail the enumeration irp
//
if ((fdoExtension->LocalNodesInitialized) == TRUE) { PDEVICE_OBJECT pdo; PISCSI_PDO_EXTENSION pdoExtension; ULONG inx;
DebugPrint((3, "Number of targets : %d\n", (fdoExtension->NumberOfTargets)));
fdoExtension->TargetsYetToRespond = fdoExtension->NumberOfTargets;
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx]; pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension); iScsiConnection = pdoExtension->ClientNodeInfo;
DebugPrint((3, "Will connect to the server\n"));
pdoExtension->LogonTickCount = 0;
//
// Connection timeout is 60 seconds. Is this enough???
//
Timeout.QuadPart = -600000000; status = iSpTdiConnect(iScsiConnection->ConnectionDeviceObject, iScsiConnection->ConnectionFileObject, pdoExtension->TargetIPAddress, htons(pdoExtension->TargetPortNumber), Timeout); if (NT_SUCCESS(status)) { DebugPrint((3, "Connected to the server\n"));
iScsiConnection->ConnectionState = ConnectionStateConnected;
pdoExtension->CurrentProtocolState = PSLogonInProgress;
DebugPrint((3, "Will send logon packet\n"));
status = iSpSendLoginCommand(pdoExtension); if (NT_SUCCESS(status)) {
pdoExtension->LogonTickCount = 0;
DebugPrint((3, "Login command sent successfully\n")); } else {
LARGE_INTEGER disconnectTimeout;
DebugPrint((1, "Send failed for logon. Status : %x\n", status));
InterlockedDecrement(&(fdoExtension->TargetsYetToRespond)); pdoExtension->CurrentProtocolState = PSLogonFailed;
disconnectTimeout.QuadPart = -100000000L;
iScsiConnection->ConnectionState = ConnectionStateStopping;
status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject, iScsiConnection->ConnectionFileObject, TDI_DISCONNECT_RELEASE, iSpTdiCompletionRoutine, iScsiConnection, disconnectTimeout);
DebugPrint((3, "iSpTdiDisconnect returned : %x\n", status));
} } else {
DebugPrint((1, "Could not connect to server. Status : %x\n", status)); pdoExtension->CurrentProtocolState = PSConnectToServerFailed; InterlockedDecrement(&(fdoExtension->TargetsYetToRespond)); } }
KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock), &oldIrql);
//
// Launch the enum completion thread if all targets have
// responded or none could be contacted.
//
if (((fdoExtension->TargetsYetToRespond) == 0) && ((fdoExtension->EnumerationThreadLaunched) == FALSE)) {
DebugPrint((0, "All or no targets responded. Will complete QDR\n"));
iSpLaunchEnumerationCompletion(fdoExtension);
}
KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock), oldIrql);
} else { DebugPrint((1, "iSpEnumerateDevices : Client node not setup yet\n"));
status = STATUS_UNSUCCESSFUL;
Irp = fdoExtension->EnumerationIrp; fdoExtension->EnumerationIrp = NULL;
Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0L; IoCompleteRequest(Irp, IO_NO_INCREMENT); }
return; }
/*
NTSTATUS iSpPerformDeviceEnumeration( IN PDEVICE_OBJECT DeviceObject, PIRP Irp ) { PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PISCSI_CONNECTION iScsiConnection = fdoExtension->ClientNodeInfo; PISCSI_LOGIN_COMMAND iscsiLoginCommand; LARGE_INTEGER disconnectTimeout; NTSTATUS status; USHORT connectionID;
ASSERT((iScsiConnection != NULL)); ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE); ASSERT((iScsiConnection->ConnectionState) == ConnectionStateConnected);
if ((fdoExtension->CurrentProtocolState) != PSConnectedToServer) { DebugPrint((1, "Probably already logged on. CurrentState : %d\n", (fdoExtension->CurrentProtocolState))); return STATUS_UNSUCCESSFUL; }
//
// First send logon packet
//
fdoExtension->CurrentIrp = Irp; fdoExtension->CurrentProtocolState = PSLogonInProgress;
status = iSpSendLoginCommand(fdoExtension); if (NT_SUCCESS(status)) { DebugPrint((3, "Login command sent successfully\n")); } else { DebugPrint((1, "Send failed for logon. Status : %x\n", status));
fdoExtension->CurrentIrp = NULL; fdoExtension->CurrentProtocolState = PSLogonFailed;
disconnectTimeout.QuadPart = -100000000L;
iScsiConnection->ConnectionState = ConnectionStateStopping;
status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject, iScsiConnection->ConnectionFileObject, TDI_DISCONNECT_RELEASE, iSpTdiCompletionRoutine, iScsiConnection, disconnectTimeout);
DebugPrint((3, "iSpTdiDisconnect returned : %x\n", status));
return STATUS_UNSUCCESSFUL; }
//
// QDR Irp will be completed upon receipt of login response
//
// ISSUE : nramas : 12/24/2000
// Should have a timer here to take care of the case where
// the server fails to send logon response.
//
return STATUS_SUCCESS; } */
NTSTATUS iSpQueryDeviceRelationsCompletion( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PISCSI_PDO_EXTENSION pdoExtension;
PCOMMON_EXTENSION commonExtension; PISCSI_CONNECTION iScsiConnection; PISCSI_LOGIN_RESPONSE loginResponse; PIRP Irp; PDEVICE_OBJECT pdo; PDEVICE_RELATIONS deviceRelations;
PACTIVE_REQUESTS activeClientRequests = NULL;
ULONG relationSize; ULONG maxCmdRN = 0; ULONG inx; ULONG targetIndex;
NTSTATUS status = STATUS_SUCCESS;
IoFreeWorkItem((PIO_WORKITEM) Context);
Irp = fdoExtension->EnumerationIrp; fdoExtension->EnumerationIrp = NULL;
relationSize = sizeof(DEVICE_RELATIONS) + ((fdoExtension->NumberOfTargets) * sizeof(PDEVICE_OBJECT));
deviceRelations = iSpAllocatePool(PagedPool, relationSize, ISCSI_TAG_DEVICE_RELATIONS); if (deviceRelations == NULL) { DebugPrint((1, "Failed to allocate memory for device relations\n"));
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) { pdo = fdoExtension->PDOList[inx];
iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL; }
fdoExtension->NumberOfTargets = 0;
fdoExtension->LocalNodesInitialized = FALSE;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Information = 0L; IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES; }
deviceRelations->Count = 0;
targetIndex = 0; for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx];
pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension);
iScsiConnection = pdoExtension->ClientNodeInfo;
if ((pdoExtension->CurrentProtocolState) == PSLogonSucceeded) {
loginResponse = (PISCSI_LOGIN_RESPONSE) (iScsiConnection->IScsiPacket);
//
// Allocate memory to keep active requests. Size of this
// array is the value returned in MaxCmdRN field
//
GetUlongFromArray((loginResponse->InitStatRN), (iScsiConnection->CurrentStatusRefNum));
GetUlongFromArray((loginResponse->ExpCmdRN), (iScsiConnection->CommandRefNum));
GetUlongFromArray((loginResponse->MaxCmdRN), maxCmdRN);
iScsiConnection->MaxCommandRefNum = maxCmdRN;
DebugPrint((1, "InitStatRN : %d, ExpCmdRN : %d, MaxCmdRN : %d\n", (iScsiConnection->CurrentStatusRefNum), (iScsiConnection->CommandRefNum), maxCmdRN));
ASSERT((maxCmdRN != 0));
ASSERT(((iScsiConnection->CommandRefNum) <= maxCmdRN));
iScsiConnection->NumberOfReqsInProgress = 0;
iScsiConnection->ReceiveState = ReceiveHeader;
iScsiConnection->MaxPendingRequests = maxCmdRN;
activeClientRequests = iSpAllocatePool( NonPagedPool, (sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)), ISCSI_TAG_ACTIVE_REQ);
if (activeClientRequests == NULL) {
DebugPrint((1, "Failed to allocate ActiveClientRequests array\n"));
iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL; } else { RtlZeroMemory(activeClientRequests, (sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)));
commonExtension = pdo->DeviceExtension; pdoExtension = pdo->DeviceExtension; pdo->StackSize = 1; pdo->Flags |= (DO_BUS_ENUMERATED_DEVICE | DO_DIRECT_IO); pdo->AlignmentRequirement = DeviceObject->AlignmentRequirement;
commonExtension->DeviceObject = pdo; commonExtension->LowerDeviceObject = DeviceObject; commonExtension->IsPdo = TRUE; commonExtension->MajorFunction = PdoMajorFunctionTable; commonExtension->RemoveLock = 0; commonExtension->CurrentPnpState = 0xff; commonExtension->PreviousPnpState = 0xff;
iScsiConnection->ActiveClientRequests = activeClientRequests;
//
// Inquiry data will be filled when we get the
// first query id irp
//
pdoExtension->InquiryDataInitialized = FALSE;
pdoExtension->CurrentProtocolState = PSFullFeaturePhase;
pdoExtension->IsEnumerated = TRUE;
//
// Initialize the remove lock event.
//
KeInitializeEvent( &(commonExtension->RemoveEvent), SynchronizationEvent, FALSE);
//
// Initialize the request list for this PDO
//
InitializeListHead(&(iScsiConnection->RequestList));
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
fdoExtension->PDOList[targetIndex] = pdo; targetIndex++;
ObReferenceObject(pdo); DebugPrint((1, "PDO %d : 0x%x\n", (deviceRelations->Count), pdo));
deviceRelations->Objects[deviceRelations->Count] = pdo; (deviceRelations->Count)++;
} } else { iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL; } }
//
// Group all the targets to the beginning of the PDOList
//
for (inx = targetIndex; inx < MAX_TARGETS_SUPPORTED; inx++) { fdoExtension->PDOList[inx] = NULL; }
DebugPrint((1, "Number of PDOs reported : %d\n", (deviceRelations->Count)));
if ((deviceRelations->Count) > 0) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
fdoExtension->EnumerationComplete = TRUE; fdoExtension->NumberOfTargets = deviceRelations->Count;
IoCopyCurrentIrpStackLocationToNext(Irp);
return IoCallDriver((fdoExtension->CommonExtension.LowerDeviceObject), Irp); } else { DebugPrint((1, "No PDOs to report in QDR\n")); ExFreePool(deviceRelations);
fdoExtension->NumberOfTargets = 0;
fdoExtension->LocalNodesInitialized = FALSE;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL; } }
NTSTATUS IssueInquiry( IN PDEVICE_OBJECT LogicalUnit ) { PISCSI_PDO_EXTENSION pdoExtension = LogicalUnit->DeviceExtension; PIRP irp; SCSI_REQUEST_BLOCK srb; PCDB cdb; PVOID dataBuffer; PSENSE_DATA senseInfoBuffer;
UCHAR allocationLength; ULONG bytesReturned;
NTSTATUS status;
PAGED_CODE();
dataBuffer = &(pdoExtension->InquiryData); senseInfoBuffer = &(pdoExtension->InquirySenseBuffer);
irp = IoAllocateIrp((LogicalUnit->StackSize) + 1, FALSE); if (irp == NULL) { DebugPrint((1, "IssueInquiry : Failed to allocate IRP.\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IoInitializeIrp(irp, IoSizeOfIrp((LogicalUnit->StackSize) + 1), ((LogicalUnit->StackSize) + 1));
//
// Fill in SRB fields.
//
RtlZeroMemory(dataBuffer, sizeof(INQUIRYDATA)); RtlZeroMemory(senseInfoBuffer, SENSE_BUFFER_SIZE); RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
srb.Function = SRB_FUNCTION_EXECUTE_SCSI; srb.Length = SCSI_REQUEST_BLOCK_SIZE;
//
// Set flags to disable synchronous negociation.
//
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// Set timeout to 4 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.
//
allocationLength = sizeof(INQUIRYDATA);
cdb->CDB6INQUIRY3.AllocationLength = allocationLength;
cdb->CDB6INQUIRY3.EnableVitalProductData = FALSE;
cdb->CDB6INQUIRY3.PageCode = 0;
status = iSpSendSrbSynchronous(LogicalUnit, &srb, irp, 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)) {
pdoExtension->InquiryDataInitialized = TRUE;
DebugPrint((3, "Inquiry data obtained successfully\n")); } else { DebugPrint((1, "Failed to obtain inquiry data. Status : %x\n", status)); }
IoFreeIrp(irp);
return status; }
NTSTATUS iSpSendSrbSynchronous( IN PDEVICE_OBJECT LogicalUnit, IN PSCSI_REQUEST_BLOCK Srb, IN PIRP Irp, IN PVOID DataBuffer, IN ULONG TransferLength, IN OPTIONAL PVOID SenseInfoBuffer, IN OPTIONAL UCHAR SenseInfoBufferLength, OUT PULONG BytesReturned ) { KEVENT event;
PIO_STACK_LOCATION irpStack; PMDL Mdl = NULL;
PSENSE_DATA senseInfo = SenseInfoBuffer;
NTSTATUS status;
PAGED_CODE();
KeInitializeEvent(&event, NotificationEvent, FALSE);
if(ARGUMENT_PRESENT(DataBuffer)) { ASSERT(TransferLength != 0);
Mdl = IoAllocateMdl(DataBuffer, TransferLength, FALSE, FALSE, NULL);
if(Mdl == NULL) { IoFreeIrp(Irp); return STATUS_INSUFFICIENT_RESOURCES; }
MmBuildMdlForNonPagedPool(Mdl); Irp->MdlAddress = Mdl; } else { ASSERT(TransferLength == 0); }
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; }
IoSetCompletionRoutine(Irp, iSpSetEvent, &event, TRUE, TRUE, TRUE);
KeEnterCriticalRegion();
status = IoCallDriver(LogicalUnit, Irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
*BytesReturned = (ULONG) Irp->IoStatus.Information;
IoFreeMdl(Mdl);
KeLeaveCriticalRegion();
return status; }
VOID iSpTickHandler( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PISCSI_FDO_EXTENSION fdoExtension; PISCSI_PDO_EXTENSION pdoExtension; PDEVICE_OBJECT pdo; ULONG inx; UCHAR oldIrql;
fdoExtension = (PISCSI_FDO_EXTENSION) DeviceObject->DeviceExtension;
KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock), &oldIrql);
if ((fdoExtension->TargetsYetToRespond) > 0) {
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx]; if (pdo != NULL) {
pdoExtension = (PISCSI_PDO_EXTENSION) (pdo->DeviceExtension);
if ((pdoExtension->CurrentProtocolState) == PSLogonInProgress) { (pdoExtension->LogonTickCount)++; }
if ((pdoExtension->LogonTickCount) == MAX_LOGON_WAIT_TIME) {
DebugPrint((0, "Timeout waiting for logon response\n"));
pdoExtension->CurrentProtocolState = PSLogonTimedOut;
(fdoExtension->TargetsYetToRespond)--;
if ((fdoExtension->TargetsYetToRespond) == 0) {
DebugPrint((0, "TickHandler : All targets responded.\n"));
iSpLaunchEnumerationCompletion(fdoExtension); } } }
} }
KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock), oldIrql); }
VOID iSpLaunchEnumerationCompletion( IN PISCSI_FDO_EXTENSION FdoExtension ) { PIO_WORKITEM workItem;
if ((FdoExtension->EnumerationThreadLaunched) == FALSE) { workItem = IoAllocateWorkItem(FdoExtension->DeviceObject); if (workItem != NULL) {
IoQueueWorkItem(workItem, iSpQueryDeviceRelationsCompletion, DelayedWorkQueue, workItem);
FdoExtension->EnumerationThreadLaunched = TRUE; }
} }
|