Windows2003-3790/drivers/storage/scsiport/pdo.c
2020-09-30 16:53:55 +02:00

1621 lines
44 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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
SpPdoHandleIoctlStorageQueryProperty(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
);
NTSTATUS
SpPdoHandleIoctlScsiGetAddress(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
);
NTSTATUS
SpPdoHandleIoctlScsiPassthrough(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
);
NTSTATUS
SpPdoHandleIoctlScsiPassthroughDirect(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
);
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)
#pragma alloc_text(PAGE, ScsiPortInitPdoWmi)
#pragma alloc_text(PAGE, SpPdoHandleIoctlStorageQueryProperty)
#pragma alloc_text(PAGE, SpPdoHandleIoctlScsiGetAddress)
#pragma alloc_text(PAGE, SpPdoHandleIoctlScsiPassthrough)
#pragma alloc_text(PAGE, SpPdoHandleIoctlScsiPassthroughDirect)
#endif
NTSTATUS
SpPdoHandleIoctlStorageQueryProperty(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles the IOCTL_STORAGE_QUERY_PROPERTY request for a PDO.
It validates the input buffer and passes calls a helper routine to do the
work. The helper routine handles completing or forwarding the request and
releasing the remove lock.
Arguments:
Pdo - Supplies a pointer to the physical device object
Irp - Supplies a pointer to the io request packet
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PAGED_CODE();
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(STORAGE_PROPERTY_QUERY)) {
//
// The input buffer is not big enough to hold a STORAGE_PROPERTY_QUERY
// structure. Fail the request.
//
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Status = status;
SpReleaseRemoveLock(Pdo, Irp);
SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT);
} else {
//
// Call helper routine to do the bulk of the work. The helper completes
// or forwards the request down the stack and releases the remove lock.
//
status = ScsiPortQueryPropertyPdo(Pdo, Irp);
}
return status;
}
NTSTATUS
SpPdoHandleIoctlScsiGetAddress(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles the IOCTL_SCSI_GET_ADDRESS. It validates the input
that the output buffer is big enough to hold a SCSI_ADDRESS structure. If
the buffer is big enough, it copies the address information into the buffer.
Arguments:
Pdo - Supplies a pointer to the physical device object
Irp - Supplies a pointer to the io request packet
Return Value:
STATUS_SUCCESS if the supplied buffer is big enough to hold the address.
STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PLOGICAL_UNIT_EXTENSION luExtension = Pdo->DeviceExtension;
PSCSI_ADDRESS scsiAddress = Irp->AssociatedIrp.SystemBuffer;
PAGED_CODE();
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SCSI_ADDRESS)) {
//
// The output buffer is too small to hold a SCSI_ADDRESS structure,
// so we fail the request.
//
status = STATUS_BUFFER_TOO_SMALL;
} else {
//
// Fill in the address information and set the IoStatus.Information
// to the sizeof the SCSI_ADDRESS structure.
//
scsiAddress->Length = sizeof(SCSI_ADDRESS);
scsiAddress->PortNumber = (UCHAR) luExtension->PortNumber;
scsiAddress->PathId = luExtension->PathId;
scsiAddress->TargetId = luExtension->TargetId;
scsiAddress->Lun = luExtension->Lun;
status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(SCSI_ADDRESS);
}
//
// Complete the request and release the remove lock.
//
Irp->IoStatus.Status = status;
SpReleaseRemoveLock(Pdo, Irp);
SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT);
return status;
}
NTSTATUS
SpPdoHandleIoctlScsiPassthrough(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles IOCTL_SCSI_PASS_THROUGH. It checks that the input
buffer is big enough to hold a SCSI_PASS_THROUGH structure, fills in the
address information for this LU and passes the request down to the FDO
handler to do the real work.
Arguments:
Pdo - Supplies a pointer to the physical device object
Irp - Supplies a pointer to the io request packet
Return Value:
NTSTATUS
--*/
{
PLOGICAL_UNIT_EXTENSION luExtension = Pdo->DeviceExtension;
NTSTATUS status;
PAGED_CODE();
//
// Initialize the address of the SCSI_PASS_THROUGH structure embedded
// in the supplied IRP. This routine verifies that the size of the
// SystemBuffer is big enough to hold a SCSI_PASS_THROUGH structure
// before it touches it.
//
status = PortSetPassThroughAddress(
Irp,
luExtension->PathId,
luExtension->TargetId,
luExtension->Lun
);
if (status != STATUS_SUCCESS) {
Irp->IoStatus.Status = status;
SpReleaseRemoveLock(Pdo, Irp);
SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT);
} else {
//
// Forward the request down to the FDO handler.
//
PCOMMON_EXTENSION commonExtension = Pdo->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
SpReleaseRemoveLock(Pdo, Irp);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
}
return status;
}
NTSTATUS
SpPdoHandleIoctlScsiPassthroughDirect(
IN PDEVICE_OBJECT Pdo,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles IOCTL_SCSI_PASS_THROUGH_DIRECT. It simply delegates
to the handler for IOCTL_SCSI_PASS_THROUGH since they both do exactly the
same thing.
Arguments:
Pdo - Supplies a pointer to the physical device object
Irp - Supplies a pointer to the io request packet
Return Value:
NTSTATUS
--*/
{
PAGED_CODE();
return SpPdoHandleIoctlScsiPassthrough(Pdo, Irp);
}
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
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
PCOMMON_EXTENSION commonExtension = Pdo->DeviceExtension;
NTSTATUS status;
ULONG isRemoved;
//
// If the device has been removed or is in the process of being removed,
// we must fail this request.
//
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);
//
// Initialize the status.
//
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = 0;
switch (ioControlCode) {
case IOCTL_STORAGE_QUERY_PROPERTY:
status = SpPdoHandleIoctlStorageQueryProperty(Pdo, Irp);
break;
case IOCTL_SCSI_GET_ADDRESS:
status = SpPdoHandleIoctlScsiGetAddress(Pdo, Irp);
break;
case IOCTL_SCSI_PASS_THROUGH:
status = SpPdoHandleIoctlScsiPassthrough(Pdo, Irp);
break;
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
status = SpPdoHandleIoctlScsiPassthroughDirect(Pdo, Irp);
break;
case IOCTL_SCSI_GET_DUMP_POINTERS:
IoSkipCurrentIrpStackLocation(Irp);
SpReleaseRemoveLock(Pdo, Irp);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
break;
default: {
DebugPrint((1, "ScsiPortPdoDeviceControl: unsupported IOCTL %08x\n",
ioControlCode));
SpReleaseRemoveLock(Pdo, Irp);
status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = status;
SpCompleteRequest(Pdo, Irp, NULL, IO_NO_INCREMENT);
break;
}
}
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_RELEASE_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, lun);
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;
}
VOID
ScsiPortInitPdoWmi(
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
)
/*++
Routine Description:
This routine will attempt WMI initialization for the PDO.
Arguments:
DeviceObject - a pointer to the PDO being started
Return Value:
status
--*/
{
PCOMMON_EXTENSION commonExtension = &(LogicalUnit->CommonExtension);
PADAPTER_EXTENSION adapterExtension = LogicalUnit->AdapterExtension;
PAGED_CODE();
//
// 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);
}
}
return;
}
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);
ScsiPortInitPdoWmi(LogicalUnit);
//
// 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;
}