/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: pdo.c Abstract: This module contains the dispatch routines for scsiport's physical device objects Authors: Peter Wieland Environment: Kernel mode only Notes: Revision History: --*/ #define KEEP_COMPLETE_REQUEST #include "port.h" #define __FILE_ID__ 'pdo ' #if DBG static const char *__file__ = __FILE__; #endif LONG SpPowerIdleTimeout = -1; // use system default NTSTATUS SpPagingPathNotificationCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP LowerIrp, IN PDEVICE_OBJECT Fdo ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, ScsiPortPdoPnp) #pragma alloc_text(PAGE, ScsiPortPdoCreateClose) #pragma alloc_text(PAGE, ScsiPortStartLogicalUnit) #endif NTSTATUS ScsiPortPdoDeviceControl( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine handles device control requests for scsi target devices Arguments: Pdo - a pointer to the physical device object Irp - a pointer to the io request packet Return Value: status --*/ { PLOGICAL_UNIT_EXTENSION physicalExtension = Pdo->DeviceExtension; PCOMMON_EXTENSION commonExtension = Pdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); ULONG ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; NTSTATUS status; BOOLEAN completeRequest = TRUE; ULONG isRemoved; isRemoved = SpAcquireRemoveLock(Pdo, Irp); if(isRemoved) { SpReleaseRemoveLock(Pdo, Irp); Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT); return STATUS_DEVICE_DOES_NOT_EXIST; } ASSERT(commonExtension->IsPdo); Irp->IoStatus.Status = 0; switch(ioControlCode) { case IOCTL_STORAGE_QUERY_PROPERTY: { // // Validate the query // PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer; if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY)) { status = STATUS_INVALID_PARAMETER; break; } status = ScsiPortQueryProperty(Pdo, Irp); return status; break; } case IOCTL_SCSI_GET_ADDRESS: { PSCSI_ADDRESS scsiAddress = Irp->AssociatedIrp.SystemBuffer; if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SCSI_ADDRESS)) { status = STATUS_BUFFER_TOO_SMALL; break; } scsiAddress->Length = sizeof(PSCSI_ADDRESS); scsiAddress->PortNumber = (UCHAR) physicalExtension->PortNumber; scsiAddress->PathId = physicalExtension->PathId; scsiAddress->TargetId = physicalExtension->TargetId; scsiAddress->Lun = physicalExtension->Lun; Irp->IoStatus.Information = sizeof(SCSI_ADDRESS); status = STATUS_SUCCESS; break; } // // XXX - need to handle // // IOCTL_STORAGE_PASS_THROUGH // IOCTL_STORAGE_PASS_THROUGH_DIRECT // case IOCTL_SCSI_PASS_THROUGH: case IOCTL_SCSI_PASS_THROUGH_DIRECT: { PSCSI_PASS_THROUGH srbControl = Irp->AssociatedIrp.SystemBuffer; if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH)) { status = STATUS_BUFFER_TOO_SMALL; break; } srbControl->PathId = physicalExtension->PathId; srbControl->TargetId = physicalExtension->TargetId; srbControl->Lun = physicalExtension->Lun; // // Fall through to the default handler // } default: { IoSkipCurrentIrpStackLocation(Irp); SpReleaseRemoveLock(Pdo, Irp); status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); completeRequest = FALSE; } } if(completeRequest) { Irp->IoStatus.Status = status; SpReleaseRemoveLock(Pdo, Irp); SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT); } return status; } NTSTATUS ScsiPortPdoPnp( IN PDEVICE_OBJECT LogicalUnit, IN PIRP Irp ) /*++ Routine Description: This routine handles pnp-power requests. Currently it will just be successful Arguments: LogicalUnit - pointer to the physical device object Irp - pointer to the io request packet Return Value: status --*/ { PLOGICAL_UNIT_EXTENSION logicalUnitExtension = LogicalUnit->DeviceExtension; PCOMMON_EXTENSION commonExtension = LogicalUnit->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); static ULONG i = 0; NTSTATUS status = STATUS_SUCCESS; ULONG isRemoved; PAGED_CODE(); isRemoved = SpAcquireRemoveLock(LogicalUnit, Irp); #if 0 if(isRemoved != ) { ASSERT(isRemoved != REMOVE_PENDING); status = STATUS_DEVICE_DOES_NOT_EXIST; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } #else ASSERT(isRemoved != REMOVE_COMPLETE); #endif switch(irpStack->MinorFunction) { case IRP_MN_QUERY_PNP_DEVICE_STATE: { // // If the device is in the paging path then mark it as // not-disableable. // PPNP_DEVICE_STATE deviceState = (PPNP_DEVICE_STATE) &(Irp->IoStatus.Information); DebugPrint((1, "ScsiPortPdoPnp: QUERY_DEVICE_STATE for PDO %#x\n", LogicalUnit)); if(commonExtension->PagingPathCount != 0) { SET_FLAG((*deviceState), PNP_DEVICE_NOT_DISABLEABLE); DebugPrint((1, "ScsiPortPdoPnp: QUERY_DEVICE_STATE: %#x - not disableable\n", LogicalUnit)); } Irp->IoStatus.Status = STATUS_SUCCESS; SpReleaseRemoveLock(LogicalUnit, Irp); SpCompleteRequest(LogicalUnit, Irp, NULL, IO_NO_INCREMENT); return STATUS_SUCCESS; } case IRP_MN_START_DEVICE: { if(commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) { Irp->IoStatus.Status = STATUS_SUCCESS; break; } if(commonExtension->IsInitialized == FALSE) { status = ScsiPortInitLogicalUnit(logicalUnitExtension); } if(NT_SUCCESS(status)) { commonExtension->IsInitialized = TRUE; status = ScsiPortStartLogicalUnit(logicalUnitExtension); } if(NT_SUCCESS(status)) { commonExtension->CurrentPnpState = IRP_MN_START_DEVICE; commonExtension->PreviousPnpState = 0xff; } Irp->IoStatus.Status = status; break; } case IRP_MN_QUERY_ID: { UCHAR rawIdString[64] = "UNKNOWN ID TYPE"; ANSI_STRING ansiIdString; UNICODE_STRING unicodeIdString; BOOLEAN multiStrings; PINQUIRYDATA inquiryData = &(logicalUnitExtension->InquiryData); // // We've been asked for the id of one of the physical device objects // DebugPrint((2, "ScsiPortPnp: got IRP_MN_QUERY_ID\n")); RtlInitUnicodeString(&unicodeIdString, NULL); RtlInitAnsiString(&ansiIdString, NULL); switch(irpStack->Parameters.QueryId.IdType) { case BusQueryDeviceID: { status = ScsiPortGetDeviceId(LogicalUnit, &unicodeIdString); multiStrings = FALSE; break; } case BusQueryInstanceID: { status = ScsiPortGetInstanceId(LogicalUnit, &unicodeIdString); multiStrings = FALSE; break; } case BusQueryHardwareIDs: { status = ScsiPortGetHardwareIds( LogicalUnit->DriverObject, &(logicalUnitExtension->InquiryData), &unicodeIdString); multiStrings = TRUE; break; } case BusQueryCompatibleIDs: { status = ScsiPortGetCompatibleIds( LogicalUnit->DriverObject, &(logicalUnitExtension->InquiryData), &unicodeIdString); multiStrings = TRUE; break; } default: { status = Irp->IoStatus.Status; Irp->IoStatus.Information = 0; multiStrings = FALSE; break; } } Irp->IoStatus.Status = status; if(NT_SUCCESS(status)) { PWCHAR idString; // // fix up all invalid characters // idString = unicodeIdString.Buffer; while (*idString) { if ((*idString <= L' ') || (*idString > (WCHAR)0x7F) || (*idString == L',')) { *idString = L'_'; } idString++; if ((*idString == L'\0') && multiStrings) { idString++; } } Irp->IoStatus.Information = (ULONG_PTR) unicodeIdString.Buffer; } else { Irp->IoStatus.Information = (ULONG_PTR) NULL; } SpReleaseRemoveLock(LogicalUnit, Irp); SpCompleteRequest(LogicalUnit, Irp, NULL, IO_NO_INCREMENT); return status; break; } case IRP_MN_QUERY_RESOURCES: case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) NULL; SpReleaseRemoveLock(LogicalUnit, Irp); SpCompleteRequest(LogicalUnit, Irp, NULL, IO_NO_INCREMENT); return STATUS_SUCCESS; } case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_REMOVE_DEVICE: { BOOLEAN destroyed; // // Release the lock for this IRP before going in. // if(commonExtension->IsRemoved == NO_REMOVE) { commonExtension->IsRemoved = REMOVE_PENDING; } SpReleaseRemoveLock(LogicalUnit, Irp); destroyed = SpRemoveLogicalUnit(logicalUnitExtension, irpStack->MinorFunction); if(destroyed) { commonExtension->PreviousPnpState = commonExtension->CurrentPnpState; commonExtension->CurrentPnpState = irpStack->MinorFunction; } else { commonExtension->CurrentPnpState = 0xff; commonExtension->PreviousPnpState = irpStack->MinorFunction; } status = STATUS_SUCCESS; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } case IRP_MN_QUERY_DEVICE_TEXT: { Irp->IoStatus.Status = SpQueryDeviceText( LogicalUnit, irpStack->Parameters.QueryDeviceText.DeviceTextType, irpStack->Parameters.QueryDeviceText.LocaleId, (PWSTR *) &Irp->IoStatus.Information ); break; } case IRP_MN_QUERY_CAPABILITIES: { PDEVICE_CAPABILITIES capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities; PSCSIPORT_DEVICE_TYPE deviceType = NULL; capabilities->RawDeviceOK = 1; deviceType = SpGetDeviceTypeInfo( logicalUnitExtension->InquiryData.DeviceType ); if((deviceType != NULL) && (deviceType->IsStorage)) { capabilities->SilentInstall = 1; } capabilities->Address = logicalUnitExtension->TargetId; Irp->IoStatus.Status = STATUS_SUCCESS; break; } case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: { if ((commonExtension->PagingPathCount != 0) || (logicalUnitExtension->IsLegacyClaim == TRUE)) { Irp->IoStatus.Status = STATUS_DEVICE_BUSY; } else { Irp->IoStatus.Status = STATUS_SUCCESS; commonExtension->PreviousPnpState = commonExtension->CurrentPnpState; commonExtension->CurrentPnpState = irpStack->MinorFunction; } break; } case IRP_MN_CANCEL_STOP_DEVICE: { if(commonExtension->CurrentPnpState == IRP_MN_QUERY_STOP_DEVICE) { commonExtension->CurrentPnpState = commonExtension->PreviousPnpState; commonExtension->PreviousPnpState = 0xff; } Irp->IoStatus.Status = STATUS_SUCCESS; break; } case IRP_MN_CANCEL_REMOVE_DEVICE: { if(commonExtension->CurrentPnpState == IRP_MN_QUERY_REMOVE_DEVICE) { commonExtension->CurrentPnpState = commonExtension->PreviousPnpState; commonExtension->PreviousPnpState = 0xff; } Irp->IoStatus.Status = STATUS_SUCCESS; break; } case IRP_MN_STOP_DEVICE: { ASSERT(commonExtension->CurrentPnpState == IRP_MN_QUERY_STOP_DEVICE); status = ScsiPortStopLogicalUnit(logicalUnitExtension); ASSERT(NT_SUCCESS(status)); Irp->IoStatus.Status = status; Irp->IoStatus.Information = (ULONG_PTR) NULL; if(NT_SUCCESS(status)) { commonExtension->CurrentPnpState = IRP_MN_STOP_DEVICE; commonExtension->PreviousPnpState = 0xff; } SpReleaseRemoveLock(LogicalUnit, Irp); SpCompleteRequest(LogicalUnit, Irp, NULL, IO_NO_INCREMENT); return status; } case IRP_MN_QUERY_DEVICE_RELATIONS: { PDEVICE_RELATIONS deviceRelations; if(irpStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) { break; } // // DEVICE_RELATIONS definition contains one object pointer. // deviceRelations = SpAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS), SCSIPORT_TAG_DEVICE_RELATIONS, LogicalUnit->DriverObject); if(deviceRelations == NULL) { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(deviceRelations, sizeof(DEVICE_RELATIONS)); deviceRelations->Count = 1; deviceRelations->Objects[0] = LogicalUnit; ObReferenceObject(deviceRelations->Objects[0]); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; break; } case IRP_MN_DEVICE_USAGE_NOTIFICATION: { PIRP newIrp; PIO_STACK_LOCATION nextStack; DebugPrint((1, "Pdo - IRP_MN_DEVICE_USAGE_NOTIFICATION %#p received for " "logical unit %#p\n", Irp, LogicalUnit)); newIrp = SpAllocateIrp( commonExtension->LowerDeviceObject->StackSize, FALSE, LogicalUnit->DriverObject); if(newIrp == NULL) { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; break; } newIrp->AssociatedIrp.MasterIrp = Irp; newIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; nextStack = IoGetNextIrpStackLocation(newIrp); *nextStack = *IoGetCurrentIrpStackLocation(Irp); IoSetCompletionRoutine(newIrp, SpPagingPathNotificationCompletion, commonExtension->LowerDeviceObject, TRUE, TRUE, TRUE); status = IoCallDriver(commonExtension->LowerDeviceObject, newIrp); return status; break; } } SpReleaseRemoveLock(LogicalUnit, Irp); status = Irp->IoStatus.Status; SpCompleteRequest(LogicalUnit, Irp, NULL, IO_NO_INCREMENT); return status; } NTSTATUS SpPagingPathNotificationCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP LowerIrp, IN PDEVICE_OBJECT Fdo ) { PIRP upperIrp = LowerIrp->AssociatedIrp.MasterIrp; PIO_STACK_LOCATION lowerStack = IoGetCurrentIrpStackLocation(LowerIrp); PIO_STACK_LOCATION upperStack = IoGetCurrentIrpStackLocation(upperIrp); PDEVICE_OBJECT pdo = upperStack->DeviceObject; PADAPTER_EXTENSION lowerExtension; PLOGICAL_UNIT_EXTENSION upperExtension; ASSERT(Fdo != NULL); ASSERT(pdo != NULL); DebugPrint((1, "Completion - IRP_MN_DEVICE_USAGE_NOTIFICATION: Completion of " "paging notification irp %#p sent due to irp %#p\n", LowerIrp, upperIrp)); lowerExtension = (PADAPTER_EXTENSION) Fdo->DeviceExtension; upperExtension = (PLOGICAL_UNIT_EXTENSION) pdo->DeviceExtension; ASSERT_FDO(lowerExtension->DeviceObject); ASSERT_PDO(upperExtension->DeviceObject); DebugPrint((1, "Completion - IRP_MN_DEVICE_USAGE_NOTIFICATION: irp status %#08lx\n", LowerIrp->IoStatus.Status)); if(NT_SUCCESS(LowerIrp->IoStatus.Status)) { PUCHAR typeName = "INSERT TYPE HERE"; PULONG lowerCount; PULONG upperCount; // // The parameters have already been erased from the lower irp stack // location - use the parameters from the upper once since they're // just a copy. // switch(upperStack->Parameters.UsageNotification.Type) { case DeviceUsageTypePaging: { lowerCount = &(lowerExtension->CommonExtension.PagingPathCount); upperCount = &(upperExtension->CommonExtension.PagingPathCount); typeName = "PagingPathCount"; break; } case DeviceUsageTypeHibernation: { lowerCount = &(lowerExtension->CommonExtension.HibernatePathCount); upperCount = &(upperExtension->CommonExtension.HibernatePathCount); typeName = "HibernatePathCount"; break; } case DeviceUsageTypeDumpFile: { lowerCount = &(lowerExtension->CommonExtension.DumpPathCount); upperCount = &(upperExtension->CommonExtension.DumpPathCount); typeName = "DumpPathCount"; break; } default: { typeName = "unknown type"; lowerCount = upperCount = NULL; break; } } if(lowerCount != NULL) { IoAdjustPagingPathCount( lowerCount, upperStack->Parameters.UsageNotification.InPath ); DebugPrint((1, "Completion - IRP_MN_DEVICE_USAGE_NOTIFICATION: " "Fdo %s count - %d\n", typeName, *lowerCount)); IoInvalidateDeviceState(lowerExtension->LowerPdo); } if(upperCount != NULL) { IoAdjustPagingPathCount( upperCount, upperStack->Parameters.UsageNotification.InPath ); DebugPrint((1, "Completion - IRP_MN_DEVICE_USAGE_NOTIFICATION: " "Pdo %s count - %d\n", typeName, *upperCount)); IoInvalidateDeviceState(upperExtension->DeviceObject); } } upperIrp->IoStatus = LowerIrp->IoStatus; SpReleaseRemoveLock(upperExtension->CommonExtension.DeviceObject, upperIrp); SpCompleteRequest(upperExtension->CommonExtension.DeviceObject, upperIrp, NULL, IO_NO_INCREMENT); IoFreeIrp(LowerIrp); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS ScsiPortPdoCreateClose( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine handles creates and closes for bus device pdo's Arguments: Pdo - a pointer to the physical device object Irp - a pointer to the io request packet Return Value: status --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = Pdo->DeviceExtension; ULONG isRemoved; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); ASSERT_PDO(Pdo); isRemoved = SpAcquireRemoveLock(Pdo, Irp); if(IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_CREATE) { if(isRemoved) { status = STATUS_DEVICE_DOES_NOT_EXIST; } else if(logicalUnit->IsTemporary == TRUE) { status = STATUS_DEVICE_NOT_READY; } } Irp->IoStatus.Status = status; SpReleaseRemoveLock(Pdo, Irp); SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT); return status; } NTSTATUS ScsiPortScsi1PdoScsi( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine is a wrapper around ScsiPortPdoScsi. It inserts the LUN number into the CDB before calling the generic version. this is for use with older target controllers which don't pay attention to the identify message sent before the command phase. Arguments: Pdo - a pointer to the physical device object Irp - a pointer to the io request packet Return Value: status --*/ { PLOGICAL_UNIT_EXTENSION lun = Pdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; // // NOTICE: The SCSI-II specification indicates that this field should be // zero; however, some target controllers ignore the logical unit number // in the INDENTIFY message and only look at the logical unit number field // in the CDB. // srb->Cdb[1] |= lun->Lun << 5; return ScsiPortPdoScsi(Pdo, Irp); } NTSTATUS ScsiPortPdoScsi( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine dispatches SRB's for a particular target device. It will fill in the Port, Path, Target and Lun values and then forward the request through to the FDO for the bus Arguments: Pdo - a pointer to the physical device object Irp - a pointer to the io request packet Return Value: status --*/ { PLOGICAL_UNIT_EXTENSION lun = Pdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); #if DBG PDRIVER_OBJECT lowerDriverObject = lun->CommonExtension.LowerDeviceObject->DriverObject; #endif PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; ULONG isRemoved; PSRB_DATA srbData; BOOLEAN isLock = FALSE; NTSTATUS status; isRemoved = SpAcquireRemoveLock(Pdo, Irp); if(isRemoved && !IS_CLEANUP_REQUEST(irpStack) && (srb->Function != SRB_FUNCTION_CLAIM_DEVICE)) { Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; SpReleaseRemoveLock(Pdo, Irp); SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT); return STATUS_DEVICE_DOES_NOT_EXIST; } srb->PathId = lun->PathId; srb->TargetId = lun->TargetId; srb->Lun = lun->Lun; // // Queue tags should be assigned only by the StartIo routine. Set it to // a benign value here so we can tell later on that we don't have to // clear the tag value in the bitmap. // srb->QueueTag = SP_UNTAGGED; #if DBG ASSERT(lowerDriverObject->MajorFunction[IRP_MJ_SCSI] != NULL); ASSERT(lowerDriverObject->MajorFunction[IRP_MJ_SCSI] == ScsiPortGlobalDispatch); #endif switch(srb->Function) { case SRB_FUNCTION_ABORT_COMMAND: { status = STATUS_NOT_SUPPORTED; break; } case SRB_FUNCTION_CLAIM_DEVICE: case SRB_FUNCTION_REMOVE_DEVICE: { status = SpClaimLogicalUnit( lun->CommonExtension.LowerDeviceObject->DeviceExtension, lun, Irp, FALSE); break; } case SRB_FUNCTION_UNLOCK_QUEUE: case SRB_FUNCTION_LOCK_QUEUE: { SpStartLockRequest(lun, Irp); return STATUS_PENDING; } case SRB_FUNCTION_RELEASE_QUEUE: case SRB_FUNCTION_FLUSH_QUEUE: { srbData = SpAllocateBypassSrbData(lun); ASSERT(srbData != NULL); goto RunSrb; } default: { if(TEST_FLAG(srb->SrbFlags, (SRB_FLAGS_BYPASS_LOCKED_QUEUE | SRB_FLAGS_BYPASS_FROZEN_QUEUE))) { srbData = SpAllocateBypassSrbData(lun); ASSERT(srbData != NULL); } else { srbData = SpAllocateSrbData( lun->AdapterExtension, Irp); if(srbData == NULL) { // // There wasn't an SRB_DATA block available for this // request so it's been queued waiting for resources - // leave the logical unit remove-locked and return pending. // DebugPrint((1, "ScsiPortPdoScsi: Insufficient resources " "to allocate SRB_DATA structure\n")); return STATUS_PENDING; } } RunSrb: srbData->CurrentIrp = Irp; srbData->CurrentSrb = srb; srbData->LogicalUnit = lun; srb->OriginalRequest = srbData; return SpDispatchRequest(lun, Irp); } } Irp->IoStatus.Status = status; SpReleaseRemoveLock(Pdo, Irp); SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT); return status; } NTSTATUS ScsiPortStartLogicalUnit( IN PLOGICAL_UNIT_EXTENSION LogicalUnit ) /*++ Routine Description: This routine will attempt to start the specified device object. Currently this involves clearing the INITIALIZING flag if it was set, and running through to the device node and marking itself as started. This last is a kludge Arguments: LogicalUnit - a pointer to the PDO being started Return Value: status --*/ { PADAPTER_EXTENSION adapterExtension = LogicalUnit->AdapterExtension; HANDLE instanceHandle; NTSTATUS status; PAGED_CODE(); // // Open the devnode for this PDO and see if anyone's given us some // default SRB flags. // status = IoOpenDeviceRegistryKey(LogicalUnit->DeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &instanceHandle); if(NT_SUCCESS(status)) { RTL_QUERY_REGISTRY_TABLE queryTable[2]; ULONG zero = 0; RtlZeroMemory(queryTable, sizeof(queryTable)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"DefaultRequestFlags"; queryTable[0].EntryContext = &(LogicalUnit->CommonExtension.SrbFlags); queryTable[0].DefaultType = REG_DWORD; queryTable[0].DefaultData = &zero; queryTable[0].DefaultLength = sizeof(ULONG); status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, (PWSTR) instanceHandle, queryTable, NULL, NULL); // // CODEWORK: need a way to turn off tagged queuing and caching. Ie. // keep track of negative flags as well. // LogicalUnit->CommonExtension.SrbFlags &= ( SRB_FLAGS_DISABLE_DISCONNECT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_SYNCH_TRANSFER); DebugPrint((1, "SpStartDevice: Default SRB flags for (%d,%d,%d) are " "%#08lx\n", LogicalUnit->PathId, LogicalUnit->TargetId, LogicalUnit->Lun, LogicalUnit->CommonExtension.SrbFlags)); ZwClose(instanceHandle); } else { DebugPrint((1, "SpStartDevice: Error opening instance key for pdo " "[%#08lx]\n", status)); } // // If the queue is locked then unlock it to start i/o processing. // if(LogicalUnit->QueueLockCount > 0) { status = SpLockUnlockQueue(LogicalUnit->DeviceObject, FALSE, TRUE); } return status; } NTSTATUS ScsiPortInitLogicalUnit( IN PLOGICAL_UNIT_EXTENSION LogicalUnit ) /*++ Routine Description: This routine will attempt to start the specified device object. Arguments: DeviceObject - a pointer to the PDO being started Return Value: status --*/ { PCOMMON_EXTENSION commonExtension = &(LogicalUnit->CommonExtension); PADAPTER_EXTENSION adapterExtension = LogicalUnit->AdapterExtension; HANDLE instanceHandle; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); // // Initialize the idle detection timer. Tell the system to put us into a // D3 state when we're not being used. // LogicalUnit->CommonExtension.IdleTimer = PoRegisterDeviceForIdleDetection(LogicalUnit->DeviceObject, SpPowerIdleTimeout, SpPowerIdleTimeout, PowerDeviceD3); // // Now that we have a LUN, we can initialize WMI support for the adapter if // the miniport supports WMI. This may be a re-register if we've already // registered on behalf of scsiport itself. We have to wait until we have // a LUN when the miniport supports WMI because we send it an SRB to do // its own initialization. We can't send it an SRB until we have a logical // unit. // if (adapterExtension->CommonExtension.WmiMiniPortInitialized == FALSE && adapterExtension->CommonExtension.WmiMiniPortSupport == TRUE) { ULONG action; // // Decide whether we are registering or reregistering WMI for the FDO. // action = (adapterExtension->CommonExtension.WmiInitialized == FALSE) ? WMIREG_ACTION_REGISTER : WMIREG_ACTION_REREGISTER; // // Register/reregister. We can get WMI irps as soon as we do this. // IoWMIRegistrationControl(adapterExtension->DeviceObject, action); adapterExtension->CommonExtension.WmiMiniPortInitialized = TRUE; adapterExtension->CommonExtension.WmiInitialized = TRUE; } // // Initialize WMI support. // if (commonExtension->WmiInitialized == FALSE) { // // Build the SCSIPORT WMI registration information buffer for this PDO. // SpWmiInitializeSpRegInfo(LogicalUnit->DeviceObject); // // Register this device object only if the miniport supports WMI and/or // SCSIPORT will support certain WMI GUIDs on behalf of the miniport. // if (commonExtension->WmiMiniPortSupport || commonExtension->WmiScsiPortRegInfoBuf) { // // Register this physical device object as a WMI data provider, // instructing WMI that it is ready to receive WMI IRPs. // IoWMIRegistrationControl(LogicalUnit->DeviceObject, WMIREG_ACTION_REGISTER); commonExtension->WmiInitialized = TRUE; } // // Allocate several WMI_MINIPORT_REQUEST_ITEM blocks to satisfy a // potential onslaught of WMIEvent notifications by the miniport. // if (commonExtension->WmiMiniPortSupport) { // // Currently we only allocate two per new SCSI target (PDO). // SpWmiInitializeFreeRequestList(LogicalUnit->DeviceObject, 2); } } // // Build a device map entry for this logical unit. // SpBuildDeviceMapEntry(commonExtension); return status; } VOID SpStartLockRequest( IN PLOGICAL_UNIT_EXTENSION LogicalUnit, IN PIRP Irp OPTIONAL ) /*++ Routine Description: This routine is responsible for queueing, starting or restarting a lock request. If Irp is provided then it will dispatched iff no existing lock or unlock request is already running. If one is already running this request will be queued. If Irp is not provided then the next request on the LockRequestQueue will be removed and dispatched. This routine relies on the device queue to provide synchronization. Since we can only have one request get past the device queue at any given time we should only have one call at any given time with Irp set to NULL. Arguments: LogicalUnit - the logical unit to which this lock request was sent. Irp - the irp for the lock request. Return Value: none --*/ { KIRQL oldIrql; PIO_STACK_LOCATION irpStack; PSCSI_REQUEST_BLOCK srb; PSRB_DATA srbData; BOOLEAN lock; oldIrql = KeRaiseIrqlToDpcLevel(); // // If no IRP was provided then get one out of the device queue. // Otherwise make sure the device queue is not busy. // if(Irp == NULL) { PKDEVICE_QUEUE_ENTRY entry; ASSERT(LogicalUnit->CurrentLockRequest != NULL); LogicalUnit->CurrentLockRequest = NULL; entry = KeRemoveDeviceQueue(&(LogicalUnit->LockRequestQueue)); if(entry == NULL) { // // No more requests have come in while processing this one - // we can just return. // KeLowerIrql(oldIrql); return; } else { Irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.DeviceQueueEntry); irpStack = IoGetCurrentIrpStackLocation(Irp); srb = irpStack->Parameters.Scsi.Srb; lock = (srb->Function == SRB_FUNCTION_LOCK_QUEUE); } } else { irpStack = IoGetCurrentIrpStackLocation(Irp); srb = irpStack->Parameters.Scsi.Srb; lock = (srb->Function == SRB_FUNCTION_LOCK_QUEUE); DebugPrint((2, "SpStartLockRequest: called to %s queue %#p\n", lock ? "lock" : "unlock", LogicalUnit)); // // See if we can let this request keep processing or if we'll // have to queue it. // IoMarkIrpPending(Irp); if(KeInsertDeviceQueue(&(LogicalUnit->LockRequestQueue), &(Irp->Tail.Overlay.DeviceQueueEntry))) { KeLowerIrql(oldIrql); return; } } ASSERT(Irp != NULL); ASSERT(LogicalUnit->CurrentLockRequest == NULL); // // This srb function is only valid as part of a power up request // and will be ignored if the power state is D0. // CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); SET_FLAG(srb->SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE); // // Throw this request down so it gets processed as a real // request. We need to get the completion dpc to start // things running again. there are too many flags to set // to do it from here. // DebugPrint((2, "SpStartLockRequest: %s %#p into " "queue %#p ... issuing request\n", lock ? "lock" : "unlock", srb, LogicalUnit)); // // There are four bypass srb data blocks available - we should have at most // one lock request awaiting completion and the one we're about to start // so this call should never, ever fail. // srbData = SpAllocateBypassSrbData(LogicalUnit); ASSERT(srbData != NULL); // // Set the current lock request. As long as this is cleared // before the next item is removed from the queue everything // will be happy. // ASSERT(LogicalUnit->CurrentLockRequest == NULL); LogicalUnit->CurrentLockRequest = srbData; srbData->CurrentIrp = Irp; srbData->CurrentSrb = srb; srbData->LogicalUnit = LogicalUnit; srb->OriginalRequest = srbData; SpDispatchRequest(LogicalUnit, Irp); KeLowerIrql(oldIrql); return; }