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.
7093 lines
191 KiB
7093 lines
191 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1990 - 1999
|
|
|
|
Module Name:
|
|
|
|
internal.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT SCSI port driver. This file contains the internal
|
|
code.
|
|
|
|
Authors:
|
|
|
|
Mike Glass
|
|
Jeff Havens
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module is a driver dll for scsi miniports.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ideport.h"
|
|
|
|
|
|
NTSTATUS
|
|
IdeSendMiniPortIoctl(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP RequestIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeSendScsiPassThrough (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP RequestIrp,
|
|
IN BOOLEAN Direct
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeGetInquiryData(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeClaimLogicalUnit(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeRemoveDevice(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
IdeLogResetError(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN ULONG UniqueId
|
|
);
|
|
|
|
#ifdef LOG_GET_NEXT_CALLER
|
|
|
|
VOID
|
|
IdeLogCompletedCommand(
|
|
PFDO_EXTENSION FdoExtension,
|
|
PSCSI_REQUEST_BLOCK Srb
|
|
);
|
|
|
|
VOID
|
|
IdeLogGetNextLuCaller (
|
|
PFDO_EXTENSION FdoExtension,
|
|
PPDO_EXTENSION PdoExtension,
|
|
PUCHAR FileName,
|
|
ULONG LineNumber
|
|
);
|
|
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(NONPAGE, IdePortDeviceControl)
|
|
#pragma alloc_text(PAGE, IdeSendMiniPortIoctl)
|
|
#pragma alloc_text(PAGE, IdeGetInquiryData)
|
|
#pragma alloc_text(PAGE, IdeSendScsiPassThrough)
|
|
#pragma alloc_text(PAGE, IdeClaimLogicalUnit)
|
|
#pragma alloc_text(PAGE, IdeRemoveDevice)
|
|
#endif
|
|
|
|
#if DBG
|
|
#define CheckIrql() {\
|
|
if (saveIrql != KeGetCurrentIrql()){\
|
|
DebugPrint((1, "saveIrql=%x, current=%x\n", saveIrql, KeGetCurrentIrql()));\
|
|
ASSERT(FALSE);}\
|
|
}
|
|
#else
|
|
#define CheckIrql()
|
|
#endif
|
|
|
|
//
|
|
// Routines start
|
|
//
|
|
|
|
NTSTATUS
|
|
IdePortDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Address of device object.
|
|
Irp - Address of I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_EXTENSION_HEADER doExtension;
|
|
PFDO_EXTENSION deviceExtension;
|
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
NTSTATUS status;
|
|
RESET_CONTEXT resetContext;
|
|
KIRQL currentIrql;
|
|
KIRQL saveIrql=KeGetCurrentIrql();
|
|
|
|
#if DBG
|
|
UCHAR savedCdb[16];
|
|
ULONG ki;
|
|
#endif
|
|
|
|
|
|
doExtension = DeviceObject->DeviceExtension;
|
|
if (doExtension->AttacheeDeviceObject == NULL) {
|
|
|
|
//
|
|
// This is a PDO
|
|
//
|
|
PPDO_EXTENSION pdoExtension = (PPDO_EXTENSION) doExtension;
|
|
|
|
srb->PathId = (UCHAR) pdoExtension->PathId;
|
|
srb->TargetId = (UCHAR) pdoExtension->TargetId;
|
|
srb->Lun = (UCHAR) pdoExtension->Lun;
|
|
|
|
((PCDB) (srb->Cdb))->CDB6GENERIC.LogicalUnitNumber = srb->Lun;
|
|
|
|
CheckIrql();
|
|
return IdePortDispatch(
|
|
pdoExtension->ParentDeviceExtension->DeviceObject,
|
|
Irp
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a FDO;
|
|
//
|
|
deviceExtension = (PFDO_EXTENSION) doExtension;
|
|
}
|
|
|
|
//
|
|
// Init SRB Flags for IDE
|
|
//
|
|
INIT_IDE_SRB_FLAGS (srb);
|
|
|
|
//
|
|
// get the target device object extension
|
|
//
|
|
logicalUnit = RefLogicalUnitExtensionWithTag(
|
|
deviceExtension,
|
|
srb->PathId,
|
|
srb->TargetId,
|
|
srb->Lun,
|
|
TRUE,
|
|
Irp
|
|
);
|
|
|
|
if (logicalUnit == NULL) {
|
|
|
|
DebugPrint((1, "IdePortDispatch: Bad logical unit address.\n"));
|
|
|
|
//
|
|
// Fail the request. Set status in Irp and complete it.
|
|
//
|
|
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
CheckIrql();
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
CheckIrql();
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
//
|
|
// special flag for tape device
|
|
//
|
|
TEST_AND_SET_SRB_FOR_RDP(logicalUnit->ScsiDeviceType, srb);
|
|
|
|
//
|
|
// hang the logUnitExtension off the Irp
|
|
//
|
|
IDEPORT_PUT_LUNEXT_IN_IRP (irpStack, logicalUnit);
|
|
|
|
//
|
|
// check for DMA candidate
|
|
// default (0) is DMA candidate
|
|
//
|
|
if (SRB_IS_DMA_CANDIDATE(srb)) {
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) {
|
|
|
|
ULONG deviceFlags = deviceExtension->HwDeviceExtension->DeviceFlags[srb->TargetId];
|
|
|
|
if (deviceFlags & DFLAGS_ATAPI_DEVICE) {
|
|
|
|
if (srb->Cdb[0] == SCSIOP_MODE_SENSE) {
|
|
|
|
if (!(deviceFlags & DFLAGS_TAPE_DEVICE)) {
|
|
|
|
CheckIrql();
|
|
return DeviceAtapiModeSense(logicalUnit, Irp);
|
|
|
|
}
|
|
|
|
//
|
|
// we should do PIO for mode sense/select
|
|
//
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
|
|
} else if (srb->Cdb[0] == SCSIOP_MODE_SELECT) {
|
|
|
|
if (!(deviceFlags & DFLAGS_TAPE_DEVICE)) {
|
|
|
|
CheckIrql();
|
|
return DeviceAtapiModeSelect(logicalUnit, Irp);
|
|
|
|
}
|
|
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
} else if (srb->Cdb[0] == SCSIOP_REQUEST_SENSE) {
|
|
|
|
//
|
|
// SCSIOP_REQUEST_SENSE
|
|
// ALi can't handle odd word udma xfer
|
|
// safest thing to do is do pio
|
|
//
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
} else if ((srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) ||
|
|
(srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH_EX) ||
|
|
(srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH)) {
|
|
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
} else if ((srb->Cdb[0] == ATAPI_MODE_SENSE) ||
|
|
(srb->Cdb[0] == ATAPI_MODE_SELECT) ||
|
|
(srb->Cdb[0] == SCSIOP_INQUIRY) ||
|
|
(srb->Cdb[0] == SCSIOP_GET_EVENT_STATUS) ||
|
|
(srb->Cdb[0] == SCSIOP_GET_CONFIGURATION)) {
|
|
|
|
//
|
|
// ISSUE: use a pio only commands table
|
|
//
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
}
|
|
|
|
} else { // ATA deivce
|
|
|
|
if ((srb->Cdb[0] != SCSIOP_READ) && (srb->Cdb[0] != SCSIOP_WRITE)) {
|
|
|
|
//
|
|
// for ATA device, we can only DMA with SCSIOP_READ and SCSIOP_WRITE
|
|
//
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
if (srb->Cdb[0] == SCSIOP_READ_CAPACITY) {
|
|
|
|
CheckIrql();
|
|
return DeviceIdeReadCapacity (logicalUnit, Irp);
|
|
|
|
} else if (srb->Cdb[0] == SCSIOP_MODE_SENSE) {
|
|
|
|
CheckIrql();
|
|
return DeviceIdeModeSense (logicalUnit, Irp);
|
|
|
|
} else if (srb->Cdb[0] == SCSIOP_MODE_SELECT) {
|
|
|
|
CheckIrql();
|
|
return DeviceIdeModeSelect (logicalUnit, Irp);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//Check with the miniport (Special cases)
|
|
//
|
|
|
|
|
|
ASSERT (doExtension->AttacheeDeviceObject);
|
|
ASSERT (srb->TargetId >=0);
|
|
|
|
#if DBG
|
|
for (ki=0;ki<srb->CdbLength;ki++) {
|
|
savedCdb[ki]=srb->Cdb[ki];
|
|
}
|
|
#endif
|
|
|
|
//Check for NULL.
|
|
if (deviceExtension->TransferModeInterface.UseDma){
|
|
if (!((deviceExtension->TransferModeInterface.UseDma)
|
|
(deviceExtension->TransferModeInterface.VendorSpecificDeviceExtension,
|
|
(PVOID)(srb->Cdb), srb->TargetId))) {
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
for (ki=0;ki<srb->CdbLength;ki++) {
|
|
if (savedCdb[ki] != srb->Cdb[ki]) {
|
|
DebugPrint((DBG_ALWAYS,
|
|
"Miniport modified the Cdb\n"));
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((logicalUnit->DmaTransferTimeoutCount >= PDO_DMA_TIMEOUT_LIMIT) ||
|
|
(logicalUnit->CrcErrorCount >= PDO_UDMA_CRC_ERROR_LIMIT)) {
|
|
|
|
//
|
|
// broken hardware
|
|
//
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
}
|
|
|
|
} else {
|
|
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
}
|
|
}
|
|
|
|
switch (srb->Function) {
|
|
|
|
case SRB_FUNCTION_SHUTDOWN:
|
|
|
|
DebugPrint((1, "IdePortDispatch: SRB_FUNCTION_SHUTDOWN...\n"));
|
|
|
|
// ISSUE: 08/30/2000: disable/restore MSN settings
|
|
|
|
case SRB_FUNCTION_FLUSH:
|
|
{
|
|
ULONG dFlags = deviceExtension->HwDeviceExtension->DeviceFlags[srb->TargetId];
|
|
|
|
//
|
|
// for IDE devices, complete the request with status success if
|
|
// the device doesn't support the flush cache command
|
|
//
|
|
if (!(dFlags & DFLAGS_ATAPI_DEVICE) &&
|
|
((logicalUnit->FlushCacheTimeoutCount >= PDO_FLUSH_TIMEOUT_LIMIT) ||
|
|
(logicalUnit->
|
|
ParentDeviceExtension->
|
|
HwDeviceExtension->
|
|
DeviceParameters[logicalUnit->TargetId].IdeFlushCommand
|
|
== IDE_COMMAND_NO_FLUSH))) {
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
status = STATUS_SUCCESS;
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
|
|
DebugPrint((1,
|
|
"IdePortDispatch: SRB_FUNCTION_%x to target %x\n",
|
|
srb->Function,
|
|
srb->TargetId
|
|
));
|
|
|
|
#if 1
|
|
//
|
|
// we don't really handle these srb functions (shutdown
|
|
// and flush). They just get translated to a flush cache
|
|
// command on ata drives (since we don't know how to flush
|
|
// the adapter cache). We will ignore them, since the
|
|
// upper drivers should use SCSIOP_SYNCHRONIZE_CACHE to
|
|
// flush the device cache.
|
|
//
|
|
if (!(dFlags & DFLAGS_ATAPI_DEVICE)) {
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
status = STATUS_SUCCESS;
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
#endif
|
|
//
|
|
// Fall thru to execute_scsi
|
|
//
|
|
|
|
}
|
|
|
|
case SRB_FUNCTION_ATA_POWER_PASS_THROUGH:
|
|
case SRB_FUNCTION_ATA_PASS_THROUGH:
|
|
case SRB_FUNCTION_ATA_PASS_THROUGH_EX:
|
|
case SRB_FUNCTION_IO_CONTROL:
|
|
case SRB_FUNCTION_EXECUTE_SCSI:
|
|
|
|
if (logicalUnit->PdoState & PDOS_DEADMEAT) {
|
|
|
|
//
|
|
// Fail the request. Set status in Irp and complete it.
|
|
//
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_NO_KEEP_AWAKE) {
|
|
|
|
if (logicalUnit->DevicePowerState != PowerDeviceD0) {
|
|
|
|
DebugPrint ((DBG_POWER, "0x%x powered down. failing SRB_FLAGS_NO_KEEP_AWAKE srb 0x%x\n", logicalUnit, srb));
|
|
|
|
srb->SrbStatus = SRB_STATUS_NOT_POWERED;
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark Irp status pending.
|
|
//
|
|
IoMarkIrpPending(Irp);
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) {
|
|
|
|
//
|
|
// Call start io directly. This will by-pass the
|
|
// frozen queue.
|
|
//
|
|
|
|
DebugPrint((DBG_READ_WRITE,
|
|
"IdePortDispatch: Bypass frozen queue, IRP %lx\n",
|
|
Irp));
|
|
|
|
IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL);
|
|
|
|
CheckIrql();
|
|
return STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
BOOLEAN inserted;
|
|
|
|
//
|
|
// Queue the packet normally.
|
|
//
|
|
status = IdePortInsertByKeyDeviceQueue (
|
|
logicalUnit,
|
|
Irp,
|
|
srb->QueueSortKey,
|
|
&inserted
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && inserted) {
|
|
|
|
//
|
|
// irp is queued
|
|
//
|
|
} else {
|
|
|
|
//
|
|
// irp is ready to go
|
|
//
|
|
|
|
//
|
|
// Clear the active flag. If there is another request, the flag will be
|
|
// set again when the request is passed to the miniport.
|
|
//
|
|
CLRMASK (logicalUnit->LuFlags, PD_LOGICAL_UNIT_IS_ACTIVE);
|
|
|
|
//
|
|
// Clear the retry count.
|
|
//
|
|
|
|
logicalUnit->RetryCount = 0;
|
|
|
|
//
|
|
// Queue is empty; start request.
|
|
//
|
|
IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL);
|
|
}
|
|
|
|
CheckIrql();
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
case SRB_FUNCTION_RELEASE_QUEUE:
|
|
|
|
DebugPrint((2,"IdePortDispatch: SCSI unfreeze queue TID %d\n",
|
|
srb->TargetId));
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure and the saved
|
|
// interrupt context.
|
|
//
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Make sure the queue is frozen.
|
|
//
|
|
|
|
if (!(logicalUnit->LuFlags & PD_QUEUE_FROZEN)) {
|
|
|
|
DebugPrint((DBG_WARNING,
|
|
"IdePortDispatch: Request to unfreeze an unfrozen queue!\n"
|
|
));
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
status = STATUS_SUCCESS;
|
|
CheckIrql();
|
|
break;
|
|
|
|
}
|
|
|
|
CLRMASK (logicalUnit->LuFlags, PD_QUEUE_FROZEN);
|
|
|
|
//
|
|
// If there is not an untagged request running then start the
|
|
// next request for this logical unit. Otherwise free the
|
|
// spin lock.
|
|
//
|
|
|
|
if (logicalUnit->SrbData.CurrentSrb == NULL) {
|
|
|
|
//
|
|
// GetNextLuRequest frees the spinlock.
|
|
//
|
|
|
|
GetNextLuRequest(deviceExtension, logicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
|
|
} else {
|
|
|
|
DebugPrint((DBG_WARNING,
|
|
"IdePortDispatch: Request to unfreeze queue with active request\n"
|
|
));
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
|
|
|
}
|
|
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
status = STATUS_SUCCESS;
|
|
|
|
CheckIrql();
|
|
break;
|
|
|
|
case SRB_FUNCTION_RESET_BUS: {
|
|
|
|
PATA_PASS_THROUGH ataPassThroughData;
|
|
|
|
ataPassThroughData = ExAllocatePool(NonPagedPool, sizeof(ATA_PASS_THROUGH));
|
|
|
|
if (ataPassThroughData == NULL) {
|
|
srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR;
|
|
srb->InternalStatus=STATUS_INSUFFICIENT_RESOURCES;
|
|
status=STATUS_INSUFFICIENT_RESOURCES;
|
|
IdeLogNoMemoryError(deviceExtension,
|
|
logicalUnit->TargetId,
|
|
NonPagedPool,
|
|
sizeof(ATA_PASS_THROUGH),
|
|
IDEPORT_TAG_DISPATCH_RESET
|
|
);
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData));
|
|
ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_BUS_RESET;
|
|
|
|
status = IssueSyncAtaPassThroughSafe (
|
|
logicalUnit->ParentDeviceExtension,
|
|
logicalUnit,
|
|
ataPassThroughData,
|
|
FALSE,
|
|
FALSE,
|
|
30,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IdeLogResetError(deviceExtension,
|
|
srb,
|
|
('R'<<24) | 256);
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// fail to send ata pass through
|
|
//
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
if (status==STATUS_INSUFFICIENT_RESOURCES) {
|
|
srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR;
|
|
srb->InternalStatus=STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
CheckIrql();
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure and the saved
|
|
// interrupt context.
|
|
//
|
|
/*++
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
|
|
|
resetContext.DeviceExtension = deviceExtension;
|
|
resetContext.PathId = srb->PathId;
|
|
resetContext.NewResetSequence = TRUE;
|
|
resetContext.ResetSrb = NULL;
|
|
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeResetBusSynchronized,
|
|
&resetContext)) {
|
|
|
|
DebugPrint((1,"IdePortDispatch: Reset failed\n"));
|
|
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
} else {
|
|
|
|
IdeLogResetError(deviceExtension,
|
|
srb,
|
|
('R'<<24) | 256);
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
|
CheckIrql();
|
|
break;
|
|
--*/
|
|
|
|
|
|
case SRB_FUNCTION_ABORT_COMMAND:
|
|
|
|
DebugPrint((1, "IdePortDispatch: SCSI Abort or Reset command\n"));
|
|
|
|
//
|
|
// Mark Irp status pending.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
//
|
|
// Don't queue these requests in the logical unit
|
|
// queue, rather queue them to the adapter queue.
|
|
//
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
|
|
|
|
IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL);
|
|
|
|
KeLowerIrql(currentIrql);
|
|
|
|
CheckIrql();
|
|
return STATUS_PENDING;
|
|
|
|
break;
|
|
|
|
case SRB_FUNCTION_FLUSH_QUEUE:
|
|
|
|
DebugPrint((1, "IdePortDispatch: SCSI flush queue command\n"));
|
|
|
|
status = IdePortFlushLogicalUnit (
|
|
deviceExtension,
|
|
logicalUnit,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
}
|
|
|
|
CheckIrql();
|
|
break;
|
|
|
|
case SRB_FUNCTION_ATTACH_DEVICE:
|
|
case SRB_FUNCTION_CLAIM_DEVICE:
|
|
case SRB_FUNCTION_RELEASE_DEVICE:
|
|
|
|
status = IdeClaimLogicalUnit(deviceExtension, Irp);
|
|
CheckIrql();
|
|
break;
|
|
|
|
case SRB_FUNCTION_REMOVE_DEVICE:
|
|
|
|
//
|
|
// decrement the refcount before remove the device
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
deviceExtension,
|
|
logicalUnit,
|
|
Irp
|
|
);
|
|
|
|
status = IdeRemoveDevice(deviceExtension, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
CheckIrql();
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
CheckIrql();
|
|
return status;
|
|
|
|
default:
|
|
|
|
//
|
|
// Found unsupported SRB function.
|
|
//
|
|
|
|
DebugPrint((1,"IdePortDispatch: Unsupported function, SRB %lx\n",
|
|
srb));
|
|
|
|
srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
CheckIrql();
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Set status in Irp.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
CheckIrql();
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
deviceExtension,
|
|
logicalUnit,
|
|
Irp
|
|
);
|
|
|
|
IDEPORT_PUT_LUNEXT_IN_IRP (irpStack, NULL);
|
|
|
|
//
|
|
// Complete request at raised IRQ.
|
|
//
|
|
CheckIrql();
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
CheckIrql();
|
|
|
|
return status;
|
|
|
|
} // end IdePortDispatch()
|
|
|
|
|
|
|
|
VOID
|
|
IdePortStartIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies pointer to Adapter device object.
|
|
Irp - Supplies a pointer to an IRP.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PSRB_DATA srbData;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
LONG interlockResult;
|
|
NTSTATUS status;
|
|
|
|
ULONG deviceFlags = deviceExtension->HwDeviceExtension->DeviceFlags[srb->TargetId];
|
|
PCDB cdb;
|
|
|
|
LARGE_INTEGER timer;
|
|
|
|
LogStartTime(TimeStartIo, &timer);
|
|
|
|
DebugPrint((3,"IdePortStartIo: Enter routine\n"));
|
|
|
|
//
|
|
// Set the default flags in the SRB.
|
|
//
|
|
|
|
srb->SrbFlags |= deviceExtension->SrbFlags;
|
|
|
|
//
|
|
// Get logical unit extension.
|
|
//
|
|
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_NO_KEEP_AWAKE) &&
|
|
(srb->Function != SRB_FUNCTION_ATA_POWER_PASS_THROUGH) &&
|
|
(logicalUnit->IdleCounter)) {
|
|
|
|
//
|
|
// Tell Po that we are busy
|
|
//
|
|
PoSetDeviceBusy (logicalUnit->IdleCounter);
|
|
}
|
|
|
|
DebugPrint((2,"IdePortStartIo: Irp 0x%8x Srb 0x%8x DataBuf 0x%8x Len 0x%8x\n", Irp, srb, srb->DataBuffer, srb->DataTransferLength));
|
|
|
|
//
|
|
// No special resources are required. Set the SRB data to the
|
|
// structure in the logical unit extension, set the queue tag value
|
|
// to the untagged value, and clear the SRB extension.
|
|
//
|
|
|
|
srbData = &logicalUnit->SrbData;
|
|
|
|
//
|
|
// Update the sequence number for this request if there is not already one
|
|
// assigned.
|
|
//
|
|
|
|
if (!srbData->SequenceNumber) {
|
|
|
|
//
|
|
// Assign a sequence number to the request and store it in the logical
|
|
// unit.
|
|
//
|
|
|
|
srbData->SequenceNumber = deviceExtension->SequenceNumber++;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is not an ABORT request the set the current srb.
|
|
// NOTE: Lock should be held here!
|
|
//
|
|
|
|
if (srb->Function != SRB_FUNCTION_ABORT_COMMAND) {
|
|
|
|
ASSERT(srbData->CurrentSrb == NULL);
|
|
srbData->CurrentSrb = srb;
|
|
ASSERT(srbData->CurrentSrb);
|
|
|
|
if ((deviceExtension->HwDeviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_USE_DMA) &&
|
|
SRB_IS_DMA_CANDIDATE(srb)) {
|
|
|
|
MARK_SRB_FOR_DMA(srb);
|
|
|
|
} else {
|
|
|
|
MARK_SRB_FOR_PIO(srb);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Only abort requests can be started when there is a current request
|
|
// active.
|
|
//
|
|
|
|
ASSERT(logicalUnit->AbortSrb == NULL);
|
|
logicalUnit->AbortSrb = srb;
|
|
}
|
|
|
|
//
|
|
// Log the command
|
|
//
|
|
IdeLogStartCommandLog(srbData);
|
|
|
|
//
|
|
// Flush the data buffer if necessary.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) {
|
|
|
|
//
|
|
// Save the MDL virtual address.
|
|
//
|
|
|
|
srbData->SrbDataOffset = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
|
|
do {
|
|
|
|
//
|
|
// Determine if the adapter needs mapped memory.
|
|
//
|
|
if (!SRB_USES_DMA(srb)) { // PIO
|
|
|
|
if (Irp->MdlAddress) {
|
|
|
|
//
|
|
// Get the mapped system address and
|
|
// calculate offset into MDL.
|
|
//
|
|
srbData->SrbDataOffset = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
|
|
|
|
if ((srbData->SrbDataOffset == NULL) &&
|
|
(deviceExtension->ReservedPages != NULL)) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// this would set the appropriate flags in the device extension
|
|
// and srbData only when the call succeeds.
|
|
//
|
|
srbData->SrbDataOffset = IdeMapLockedPagesWithReservedMapping(deviceExtension,
|
|
srbData,
|
|
Irp->MdlAddress
|
|
);
|
|
|
|
//
|
|
// if there is another active request using the reserved pages
|
|
// mark this one pending. When the active request completes this
|
|
// one will be picked up
|
|
//
|
|
if (srbData->SrbDataOffset == (PVOID)-1) {
|
|
|
|
DebugPrint ((1,
|
|
"Irp 0x%x marked pending\n",
|
|
Irp
|
|
));
|
|
|
|
//
|
|
// remove the current Srb
|
|
//
|
|
srbData->CurrentSrb = NULL;
|
|
|
|
ASSERT(DeviceObject->CurrentIrp == Irp);
|
|
SETMASK(deviceExtension->Flags, PD_PENDING_DEVICE_REQUEST);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
return;
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
}
|
|
|
|
if (srbData->SrbDataOffset == NULL) {
|
|
|
|
deviceExtension->LastMemoryFailure += IDEPORT_TAG_STARTIO_MDL;
|
|
|
|
srbData->CurrentSrb = NULL;
|
|
|
|
//
|
|
// This is the correct status for insufficient resources
|
|
//
|
|
srb->SrbStatus=SRB_STATUS_INTERNAL_ERROR;
|
|
srb->InternalStatus=STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status=STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
IdeLogNoMemoryError(deviceExtension,
|
|
logicalUnit->TargetId,
|
|
NonPagedPool,
|
|
sizeof(MDL),
|
|
IDEPORT_TAG_STARTIO_MDL
|
|
);
|
|
//
|
|
// Clear the device busy flag
|
|
//
|
|
IoStartNextPacket(DeviceObject, FALSE);
|
|
|
|
//
|
|
// Acquire spin lock to protect the flags
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Get the next request, if this request does not
|
|
// bypass frozen queue. We don't want to start the
|
|
// next request, if the queue is frozen.
|
|
//
|
|
if (!(srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)) {
|
|
|
|
//
|
|
// This flag needs to be set for getnextlu to work
|
|
//
|
|
logicalUnit->LuFlags |= PD_LOGICAL_UNIT_IS_ACTIVE;
|
|
|
|
//
|
|
// Retrieve the next request and give it to the fdo
|
|
// This releases the spinlock
|
|
//
|
|
GetNextLuRequest(deviceExtension, logicalUnit);
|
|
}
|
|
else {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
}
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
deviceExtension,
|
|
logicalUnit,
|
|
Irp
|
|
);
|
|
|
|
//
|
|
// Complete the request
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return;
|
|
}
|
|
|
|
srb->DataBuffer = srbData->SrbDataOffset +
|
|
(ULONG)((PUCHAR)srb->DataBuffer -
|
|
(PCCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress));
|
|
}
|
|
|
|
IdePortAllocateAccessToken (DeviceObject);
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else { // DMA
|
|
|
|
//
|
|
// If the buffer is not mapped then the I/O buffer must be flushed.
|
|
//
|
|
|
|
KeFlushIoBuffers(Irp->MdlAddress,
|
|
(BOOLEAN) (srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE),
|
|
TRUE);
|
|
|
|
#if defined (FAKE_BMSETUP_FAILURE)
|
|
|
|
if (!(FailBmSetupCount++ % FAKE_BMSETUP_FAILURE)) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
|
|
#endif // FAKE_BMSETUP_FAILURE
|
|
status = deviceExtension->HwDeviceExtension->BusMasterInterface.BmSetup (
|
|
deviceExtension->HwDeviceExtension->BusMasterInterface.Context,
|
|
srb->DataBuffer,
|
|
srb->DataTransferLength,
|
|
Irp->MdlAddress,
|
|
(BOOLEAN) (srb->SrbFlags & SRB_FLAGS_DATA_IN),
|
|
IdePortAllocateAccessToken,
|
|
DeviceObject
|
|
);
|
|
|
|
#if defined (FAKE_BMSETUP_FAILURE)
|
|
}
|
|
#endif // FAKE_BMSETUP_FAILURE
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1,
|
|
"IdePortStartIo: IoAllocateAdapterChannel failed(%x). try pio for srb %x\n",
|
|
status, srb));
|
|
|
|
//
|
|
// out of resource for DMA, try PIO
|
|
//
|
|
MARK_SRB_FOR_PIO(srb);
|
|
}
|
|
}
|
|
|
|
} while (!NT_SUCCESS(status));
|
|
|
|
} else {
|
|
|
|
IdePortAllocateAccessToken (DeviceObject);
|
|
}
|
|
|
|
LogStopTime(TimeStartIo, &timer, 0);
|
|
return;
|
|
|
|
} // end IdePortStartIO()
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
IdePortInterrupt(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Interrupt
|
|
|
|
Device Object
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if interrupt expected.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
BOOLEAN returnValue;
|
|
LARGE_INTEGER timer;
|
|
|
|
UNREFERENCED_PARAMETER(Interrupt);
|
|
|
|
#ifdef ENABLE_ATAPI_VERIFIER
|
|
ViAtapiInterrupt(deviceExtension);
|
|
#endif
|
|
|
|
LogStartTime(TimeIsr, &timer);
|
|
returnValue = AtapiInterrupt(deviceExtension->HwDeviceExtension);
|
|
LogStopTime(TimeIsr, &timer, 100);
|
|
|
|
//
|
|
// Check to see if a DPC needs to be queued.
|
|
//
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
|
|
}
|
|
return(returnValue);
|
|
|
|
} // end IdePortInterrupt()
|
|
|
|
VOID
|
|
IdePortCompletionDpc(
|
|
IN PKDPC Dpc,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Dpc
|
|
DeviceObject
|
|
Irp - not used
|
|
// Context - not used
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
INTERRUPT_CONTEXT interruptContext;
|
|
INTERRUPT_DATA savedInterruptData;
|
|
BOOLEAN callStartIo;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSRB_DATA srbData;
|
|
LONG interlockResult;
|
|
LARGE_INTEGER timeValue;
|
|
PMDL mdl;
|
|
|
|
LARGE_INTEGER timer;
|
|
LogStartTime(TimeDpc, &timer);
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
|
|
|
|
//
|
|
// Acquire the spinlock to protect flush adapter buffers information.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Get the interrupt state. This copies the interrupt state to the
|
|
// saved state where it can be processed. It also clears the interrupt
|
|
// flags.
|
|
//
|
|
|
|
|
|
interruptContext.DeviceExtension = deviceExtension;
|
|
interruptContext.SavedInterruptData = &savedInterruptData;
|
|
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeGetInterruptState,
|
|
&interruptContext)) {
|
|
|
|
//
|
|
// There is no work to do so just return.
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
LogStopTime(TimeDpc, &timer, 0);
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// We only support one request at a time, so we can just check
|
|
// the first completed request to determine whether we use DMA
|
|
// and whether we need to flush DMA
|
|
//
|
|
if (savedInterruptData.CompletedRequests != NULL) {
|
|
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
srbData = savedInterruptData.CompletedRequests;
|
|
ASSERT(srbData->CurrentSrb);
|
|
|
|
|
|
srb = srbData->CurrentSrb;
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) {
|
|
|
|
if (SRB_USES_DMA(srb)) {
|
|
|
|
deviceExtension->HwDeviceExtension->BusMasterInterface.BmFlush (
|
|
deviceExtension->HwDeviceExtension->BusMasterInterface.Context
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// check for empty channels
|
|
//
|
|
if (savedInterruptData.InterruptFlags & PD_ALL_DEVICE_MISSING) {
|
|
|
|
PPDO_EXTENSION pdoExtension;
|
|
IDE_PATH_ID pathId;
|
|
ULONG errorCount;
|
|
BOOLEAN rescanActive = FALSE;
|
|
|
|
|
|
pathId.l = 0;
|
|
while (pdoExtension = NextLogUnitExtensionWithTag (
|
|
deviceExtension,
|
|
&pathId,
|
|
TRUE,
|
|
IdePortCompletionDpc
|
|
)) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&pdoExtension->PdoSpinLock);
|
|
|
|
SETMASK (pdoExtension->PdoState, PDOS_DEADMEAT);
|
|
|
|
IdeLogDeadMeatReason( pdoExtension->DeadmeatRecord.Reason,
|
|
reportedMissing
|
|
);
|
|
if (pdoExtension->LuFlags & PD_RESCAN_ACTIVE) {
|
|
rescanActive = TRUE;
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&pdoExtension->PdoSpinLock);
|
|
|
|
UnrefPdoWithTag(
|
|
pdoExtension,
|
|
IdePortCompletionDpc
|
|
);
|
|
}
|
|
|
|
//
|
|
// Don't ask for a rescan if you are in the middle of one.
|
|
//
|
|
if (!rescanActive) {
|
|
|
|
IoInvalidateDeviceRelations (
|
|
deviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
} else {
|
|
|
|
DebugPrint((1,
|
|
"The device marked deadmeat during enumeration\n"
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for timer requests.
|
|
//
|
|
|
|
if (savedInterruptData.InterruptFlags & PD_TIMER_CALL_REQUEST) {
|
|
|
|
//
|
|
// The miniport wants a timer request. Save the timer parameters.
|
|
//
|
|
|
|
deviceExtension->HwTimerRequest = savedInterruptData.HwTimerRequest;
|
|
|
|
//
|
|
// If the requested timer value is zero, then cancel the timer.
|
|
//
|
|
|
|
if (savedInterruptData.MiniportTimerValue == 0) {
|
|
|
|
KeCancelTimer(&deviceExtension->MiniPortTimer);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Convert the timer value from mircoseconds to a negative 100
|
|
// nanoseconds.
|
|
//
|
|
|
|
timeValue.QuadPart = Int32x32To64(
|
|
savedInterruptData.MiniportTimerValue,
|
|
-10);
|
|
|
|
//
|
|
// Set the timer.
|
|
//
|
|
|
|
KeSetTimer(&deviceExtension->MiniPortTimer,
|
|
timeValue,
|
|
&deviceExtension->MiniPortTimerDpc);
|
|
}
|
|
}
|
|
|
|
if (savedInterruptData.InterruptFlags & PD_RESET_REQUEST) {
|
|
|
|
RESET_CONTEXT resetContext;
|
|
|
|
//
|
|
// clear the reset request
|
|
//
|
|
CLRMASK (savedInterruptData.InterruptFlags, PD_RESET_REQUEST);
|
|
|
|
//
|
|
// Request timed out.
|
|
//
|
|
resetContext.DeviceExtension = deviceExtension;
|
|
resetContext.PathId = 0;
|
|
resetContext.NewResetSequence = TRUE;
|
|
resetContext.ResetSrb = NULL;
|
|
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeResetBusSynchronized,
|
|
&resetContext)) {
|
|
|
|
DebugPrint((DBG_WARNING,"IdePortCompletionDpc: Reset failed\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verify that the ready for next request is ok.
|
|
//
|
|
|
|
if (savedInterruptData.InterruptFlags & PD_READY_FOR_NEXT_REQUEST) {
|
|
|
|
//
|
|
// If the device busy bit is not set, then this is a duplicate request.
|
|
// If a no disconnect request is executing, then don't call start I/O.
|
|
// This can occur when the miniport does a NextRequest followed by
|
|
// a NextLuRequest.
|
|
//
|
|
|
|
if ((deviceExtension->Flags & (PD_DEVICE_IS_BUSY | PD_DISCONNECT_RUNNING))
|
|
== (PD_DEVICE_IS_BUSY | PD_DISCONNECT_RUNNING)) {
|
|
|
|
//
|
|
// Clear the device busy flag. This flag is set by
|
|
// IdeStartIoSynchonized.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->Flags, PD_DEVICE_IS_BUSY);
|
|
|
|
if (!(savedInterruptData.InterruptFlags & PD_RESET_HOLD)) {
|
|
|
|
//
|
|
// The miniport is ready for the next request and there is
|
|
// not a pending reset hold, so clear the port timer.
|
|
//
|
|
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_STOPPED;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If a no disconnect request is executing, then clear the
|
|
// busy flag. When the disconnect request completes an
|
|
// IoStartNextPacket will be done.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->Flags, PD_DEVICE_IS_BUSY);
|
|
|
|
//
|
|
// Clear the ready for next request flag.
|
|
//
|
|
|
|
CLRMASK (savedInterruptData.InterruptFlags, PD_READY_FOR_NEXT_REQUEST);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Free Access Token
|
|
//
|
|
if ((savedInterruptData.CompletedRequests != NULL) &&
|
|
(deviceExtension->SyncAccessInterface.FreeAccessToken)) {
|
|
|
|
(*deviceExtension->SyncAccessInterface.FreeAccessToken) (
|
|
deviceExtension->SyncAccessInterface.Token
|
|
);
|
|
}
|
|
|
|
//
|
|
// Check for a ready for next packet.
|
|
//
|
|
|
|
if (savedInterruptData.InterruptFlags & PD_READY_FOR_NEXT_REQUEST) {
|
|
|
|
//
|
|
// Start the next request.
|
|
//
|
|
|
|
IoStartNextPacket(deviceExtension->DeviceObject, FALSE);
|
|
}
|
|
|
|
//
|
|
// Check for an error log requests.
|
|
//
|
|
|
|
if (savedInterruptData.InterruptFlags & PD_LOG_ERROR) {
|
|
|
|
//
|
|
// Process the request.
|
|
//
|
|
|
|
LogErrorEntry(deviceExtension,
|
|
&savedInterruptData.LogEntry);
|
|
}
|
|
|
|
//
|
|
// Process any completed requests.
|
|
//
|
|
|
|
callStartIo = FALSE;
|
|
|
|
while (savedInterruptData.CompletedRequests != NULL) {
|
|
|
|
//
|
|
// Remove the request from the linked-list.
|
|
//
|
|
|
|
srbData = savedInterruptData.CompletedRequests;
|
|
|
|
savedInterruptData.CompletedRequests = srbData->CompletedRequests;
|
|
srbData->CompletedRequests = NULL;
|
|
|
|
//
|
|
// We only supports one request at a time
|
|
//
|
|
ASSERT (savedInterruptData.CompletedRequests == NULL);
|
|
|
|
//
|
|
// Stop the command log. The request sense will be logged as the next request.
|
|
//
|
|
IdeLogStopCommandLog(srbData);
|
|
|
|
IdeProcessCompletedRequest(deviceExtension,
|
|
srbData,
|
|
&callStartIo);
|
|
}
|
|
|
|
//
|
|
// Process any completed abort requests.
|
|
//
|
|
|
|
while (savedInterruptData.CompletedAbort != NULL) {
|
|
|
|
logicalUnit = savedInterruptData.CompletedAbort;
|
|
|
|
//
|
|
// Remove request from the completed abort list.
|
|
//
|
|
|
|
savedInterruptData.CompletedAbort = logicalUnit->CompletedAbort;
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure,
|
|
// and the free of the srb extension.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Note the timer which was started for the abort request is not
|
|
// stopped by the get interrupt routine. Rather the timer is stopped.
|
|
// when the aborted request completes.
|
|
//
|
|
|
|
Irp = logicalUnit->AbortSrb->OriginalRequest;
|
|
|
|
|
|
//
|
|
// Set IRP status. Class drivers will reset IRP status based
|
|
// on request sense if error.
|
|
//
|
|
|
|
if (SRB_STATUS(logicalUnit->AbortSrb->SrbStatus) == SRB_STATUS_SUCCESS) {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
} else {
|
|
Irp->IoStatus.Status = IdeTranslateSrbStatus(logicalUnit->AbortSrb);
|
|
}
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Clear the abort request pointer.
|
|
//
|
|
|
|
logicalUnit->AbortSrb = NULL;
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
deviceExtension,
|
|
IDEPORT_GET_LUNEXT_IN_IRP(IoGetCurrentIrpStackLocation(Irp)),
|
|
Irp
|
|
);
|
|
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Call the start I/O routine if necessary.
|
|
//
|
|
|
|
if (callStartIo) {
|
|
|
|
ASSERT(DeviceObject->CurrentIrp != NULL);
|
|
IdePortStartIo(DeviceObject, DeviceObject->CurrentIrp);
|
|
}
|
|
|
|
//
|
|
// Check for reset
|
|
//
|
|
if (savedInterruptData.InterruptFlags & PD_RESET_REPORTED) {
|
|
|
|
//
|
|
// we had a bus reset. everyone on the bus should be in PowerDeviceD0
|
|
//
|
|
IDE_PATH_ID pathId;
|
|
PPDO_EXTENSION pdoExtension;
|
|
POWER_STATE powerState;
|
|
|
|
pathId.l = 0;
|
|
powerState.DeviceState = PowerDeviceD0;
|
|
|
|
while (pdoExtension = NextLogUnitExtensionWithTag (
|
|
deviceExtension,
|
|
&pathId,
|
|
FALSE,
|
|
IdePortCompletionDpc
|
|
)) {
|
|
|
|
//
|
|
// If rescan is active, the pdo might go away
|
|
//
|
|
if (pdoExtension != savedInterruptData.PdoExtensionResetBus &&
|
|
!(pdoExtension->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
|
|
PoRequestPowerIrp (
|
|
pdoExtension->DeviceObject,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
deviceExtension,
|
|
pdoExtension,
|
|
IdePortCompletionDpc
|
|
);
|
|
}
|
|
}
|
|
|
|
LogStopTime(TimeDpc, &timer, 0);
|
|
return;
|
|
|
|
} // end IdePortCompletionDpc()
|
|
|
|
#ifdef IDEDEBUG_TEST_START_STOP_DEVICE
|
|
|
|
typedef enum {
|
|
|
|
IdeDebugStartStop_Idle=0,
|
|
IdeDebugStartStop_StopPending,
|
|
IdeDebugStartStop_Stopped,
|
|
IdeDebugStartStop_StartPending,
|
|
IdeDebugStartStop_Started,
|
|
IdeDebugStartStop_LastState
|
|
} IDEDEBUG_STARTSTOP_STATE;
|
|
|
|
|
|
PDEVICE_OBJECT IdeDebugStartStopDeviceObject=NULL;
|
|
IDEDEBUG_STARTSTOP_STATE IdeDebugStartStopState = IdeDebugStartStop_Idle;
|
|
IDEDEBUG_STARTSTOP_STATE IdeDebugStartStopTimer = 0;
|
|
|
|
PDEVICE_OBJECT
|
|
IoGetAttachedDevice(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
IdeDebugSynchronousCallCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp,
|
|
IN OUT PVOID Context
|
|
)
|
|
{
|
|
PKEVENT event = Context;
|
|
|
|
*(Irp->UserIosb) = Irp->IoStatus;
|
|
|
|
KeSetEvent( event, IO_NO_INCREMENT, FALSE );
|
|
|
|
IoFreeIrp (Irp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeDebugSynchronousCall(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION TopStackLocation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sends a synchronous irp to the top level device
|
|
object which roots on DeviceObject.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - Supplies the device object of the device being removed.
|
|
|
|
TopStackLocation - Supplies a pointer to the parameter block for the irp.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
IO_STATUS_BLOCK statusBlock;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a pointer to the topmost device object in the stack of devices,
|
|
// beginning with the deviceObject.
|
|
//
|
|
|
|
deviceObject = IoGetAttachedDevice(DeviceObject);
|
|
|
|
//
|
|
// Begin by allocating the IRP for this request. Do not charge quota to
|
|
// the current process for this IRP.
|
|
//
|
|
|
|
irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
|
|
if (irp == NULL){
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Initialize it to failure.
|
|
//
|
|
|
|
irp->IoStatus.Status = statusBlock.Status = STATUS_NOT_SUPPORTED;
|
|
irp->IoStatus.Information = statusBlock.Information = 0;
|
|
|
|
irp->UserIosb = &statusBlock;
|
|
|
|
//
|
|
// Set the pointer to the status block and initialized event.
|
|
//
|
|
|
|
KeInitializeEvent( &event,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
//
|
|
// Set the address of the current thread
|
|
//
|
|
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
//
|
|
// Get a pointer to the stack location of the first driver which will be
|
|
// invoked. This is where the function codes and parameters are set.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
|
|
|
|
//
|
|
// Copy in the caller-supplied stack location contents
|
|
//
|
|
|
|
*irpSp = *TopStackLocation;
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
IdeDebugSynchronousCallCompletionRoutine,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Call the driver
|
|
//
|
|
|
|
status = IoCallDriver(DeviceObject, irp);
|
|
|
|
//
|
|
// If a driver returns STATUS_PENDING, we will wait for it to complete
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
status = statusBlock.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeDebugStartStopWorkRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_WORKITEM WorkItem
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
IO_STACK_LOCATION irpSp;
|
|
PVOID dummy;
|
|
|
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
|
|
//
|
|
// release resource for this worker item
|
|
//
|
|
IoFreeWorkItem(WorkItem);
|
|
|
|
if (IdeDebugStartStopDeviceObject) {
|
|
|
|
if (IdeDebugStartStopState == IdeDebugStartStop_StopPending) {
|
|
|
|
irpSp.MinorFunction = IRP_MN_STOP_DEVICE;
|
|
|
|
status = IdeDebugSynchronousCall(DeviceObject, &irpSp);
|
|
if (!NT_SUCCESS(status)) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
IdeDebugStartStopTimer = 0;
|
|
IdeDebugStartStopState = IdeDebugStartStop_Stopped;
|
|
|
|
} else if (IdeDebugStartStopState == IdeDebugStartStop_StartPending) {
|
|
|
|
// this will only work with legacy ide channels enmerated by pciidex.sys
|
|
|
|
irpSp.MinorFunction = IRP_MN_START_DEVICE;
|
|
|
|
status =IdeDebugSynchronousCall(DeviceObject, &irpSp);
|
|
if (!NT_SUCCESS(status)) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
IdeDebugStartStopTimer = 0;
|
|
IdeDebugStartStopState = IdeDebugStartStop_Started;
|
|
|
|
} else {
|
|
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif //IDEDEBUG_TEST_START_STOP_DEVICE
|
|
|
|
|
|
#ifdef DPC_FOR_EMPTY_CHANNEL
|
|
BOOLEAN
|
|
IdeCheckEmptyChannel(
|
|
IN PVOID ServiceContext
|
|
)
|
|
{
|
|
ULONG status;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
PDEVICE_OBJECT deviceObject = ServiceContext;
|
|
PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension;
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension = deviceExtension->HwDeviceExtension;
|
|
|
|
if ((status=IdePortChannelEmptyQuick(&hwDeviceExtension->BaseIoAddress1, &hwDeviceExtension->BaseIoAddress2,
|
|
hwDeviceExtension->MaxIdeDevice, &hwDeviceExtension->CurrentIdeDevice,
|
|
&hwDeviceExtension->MoreWait, &hwDeviceExtension->NoRetry))!= STATUS_RETRY) {
|
|
|
|
//
|
|
// Clear current SRB.
|
|
//
|
|
Srb=hwDeviceExtension->CurrentSrb;
|
|
|
|
hwDeviceExtension->CurrentSrb = NULL;
|
|
|
|
//
|
|
// Set status in SRB.
|
|
//
|
|
if (status == 1) {
|
|
Srb->SrbStatus = (UCHAR) SRB_STATUS_SUCCESS;
|
|
} else {
|
|
Srb->SrbStatus = (UCHAR) SRB_STATUS_ERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear all the variables
|
|
//
|
|
hwDeviceExtension->MoreWait=0;
|
|
hwDeviceExtension->CurrentIdeDevice=0;
|
|
hwDeviceExtension->NoRetry=0;
|
|
|
|
//
|
|
// Indicate command complete.
|
|
//
|
|
|
|
IdePortNotification(IdeRequestComplete,
|
|
hwDeviceExtension,
|
|
Srb);
|
|
|
|
//
|
|
// Indicate ready for next request.
|
|
//
|
|
|
|
IdePortNotification(IdeNextRequest,
|
|
hwDeviceExtension,
|
|
NULL);
|
|
|
|
IoRequestDpc(deviceObject, NULL, NULL);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
}
|
|
#endif
|
|
|
|
VOID
|
|
IdePortTickHandler(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDO_EXTENSION deviceExtension =
|
|
(PFDO_EXTENSION) DeviceObject->DeviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
ULONG target;
|
|
IDE_PATH_ID pathId;
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
#if DBG
|
|
if (IdeDebugRescanBusFreq) {
|
|
|
|
IdeDebugRescanBusCounter++;
|
|
|
|
if (IdeDebugRescanBusCounter == IdeDebugRescanBusFreq) {
|
|
|
|
IoInvalidateDeviceRelations (
|
|
deviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
|
|
IdeDebugRescanBusCounter = 0;
|
|
}
|
|
}
|
|
#endif //DBG
|
|
|
|
#ifdef IDEDEBUG_TEST_START_STOP_DEVICE
|
|
|
|
if (deviceExtension->LogicalUnitList[0] &&
|
|
(IdeDebugStartStopDeviceObject == deviceExtension->LogicalUnitList[0]->DeviceObject)) {
|
|
|
|
PIO_WORKITEM workItem;
|
|
|
|
if (IdeDebugStartStopState == IdeDebugStartStop_Idle) {
|
|
|
|
IdeDebugStartStopState = IdeDebugStartStop_StopPending;
|
|
|
|
workItem = IoAllocateWorkItem(IdeDebugStartStopDeviceObject);
|
|
|
|
IoQueueWorkItem(
|
|
workItem,
|
|
IdeDebugStartStopWorkRoutine,
|
|
DelayedWorkQueue,
|
|
workItem
|
|
);
|
|
|
|
} else if (IdeDebugStartStopState == IdeDebugStartStop_Stopped) {
|
|
|
|
if (IdeDebugStartStopTimer > 5) {
|
|
|
|
IdeDebugStartStopState = IdeDebugStartStop_StartPending;
|
|
|
|
workItem = IoAllocateWorkItem(IdeDebugStartStopDeviceObject);
|
|
|
|
IoQueueWorkItem(
|
|
workItem,
|
|
IdeDebugStartStopWorkRoutine,
|
|
HyperCriticalWorkQueue,
|
|
workItem
|
|
);
|
|
} else {
|
|
|
|
IdeDebugStartStopTimer++;
|
|
}
|
|
|
|
} else if (IdeDebugStartStopState == IdeDebugStartStop_Started) {
|
|
|
|
if (IdeDebugStartStopTimer > 10) {
|
|
|
|
IdeDebugStartStopState = IdeDebugStartStop_Idle;
|
|
|
|
} else {
|
|
|
|
IdeDebugStartStopTimer++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // IDEDEBUG_TEST_START_STOP_DEVICE
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
#ifdef DPC_FOR_EMPTY_CHANNEL
|
|
|
|
//
|
|
//Holding the lock is OK.
|
|
//The empty channel check is quick
|
|
//
|
|
if (deviceExtension->HwDeviceExtension->MoreWait) {
|
|
if (!KeSynchronizeExecution (
|
|
deviceExtension->InterruptObject,
|
|
IdeCheckEmptyChannel,
|
|
DeviceObject
|
|
)) {
|
|
DebugPrint((0,"ATAPI: ChannelEmpty check- device busy after 1sec\n"));
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
//
|
|
// Check for port timeouts.
|
|
//
|
|
|
|
if (deviceExtension->ResetCallAgain) {
|
|
|
|
RESET_CONTEXT resetContext;
|
|
|
|
//
|
|
// Request timed out.
|
|
//
|
|
resetContext.DeviceExtension = deviceExtension;
|
|
resetContext.PathId = 0;
|
|
resetContext.NewResetSequence = FALSE;
|
|
resetContext.ResetSrb = NULL;
|
|
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeResetBusSynchronized,
|
|
&resetContext)) {
|
|
|
|
DebugPrint((0,"IdePortTickHanlder: Reset failed\n"));
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (deviceExtension->PortTimeoutCounter > 0) {
|
|
|
|
if (--deviceExtension->PortTimeoutCounter == 0) {
|
|
|
|
//
|
|
// Process the port timeout.
|
|
//
|
|
if (deviceExtension->InterruptObject) {
|
|
|
|
if (KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeTimeoutSynchronized,
|
|
deviceExtension->DeviceObject)){
|
|
|
|
//
|
|
// Log error if IdeTimeoutSynchonized indicates this was an error
|
|
// timeout.
|
|
//
|
|
|
|
if (deviceExtension->DeviceObject->CurrentIrp) {
|
|
IdeLogTimeoutError(deviceExtension,
|
|
deviceExtension->DeviceObject->CurrentIrp,
|
|
256);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
PIRP irp = deviceExtension->DeviceObject->CurrentIrp;
|
|
|
|
DebugPrint((0,
|
|
"The device was suprise removed with an active request\n"
|
|
));
|
|
|
|
//
|
|
// the device was probably surprise removed. Complete
|
|
// the request with status_no_such_device
|
|
//
|
|
if (irp) {
|
|
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
deviceExtension,
|
|
IDEPORT_GET_LUNEXT_IN_IRP(irpStack),
|
|
irp
|
|
);
|
|
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// check for busy Luns and restart its request
|
|
//
|
|
pathId.l = 0;
|
|
while (logicalUnit = NextLogUnitExtensionWithTag(
|
|
deviceExtension,
|
|
&pathId,
|
|
TRUE,
|
|
IdePortTickHandler
|
|
)) {
|
|
|
|
AtapiRestartBusyRequest(deviceExtension, logicalUnit);
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
deviceExtension,
|
|
logicalUnit,
|
|
IdePortTickHandler
|
|
);
|
|
}
|
|
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Since a port timeout has been done. Skip the rest of the
|
|
// processing.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Scan each of the logical units. If it has an active request then
|
|
// decrement the timeout value and process a timeout if it is zero.
|
|
//
|
|
|
|
pathId.l = 0;
|
|
while (logicalUnit = NextLogUnitExtensionWithTag(
|
|
deviceExtension,
|
|
&pathId,
|
|
TRUE,
|
|
IdePortTickHandler
|
|
)) {
|
|
|
|
//
|
|
// Check for busy requests.
|
|
//
|
|
|
|
if (AtapiRestartBusyRequest (deviceExtension, logicalUnit)) {
|
|
|
|
//
|
|
// this lun was marked busy
|
|
// skip all other checks
|
|
//
|
|
|
|
} else if (logicalUnit->RequestTimeoutCounter == 0) {
|
|
|
|
RESET_CONTEXT resetContext;
|
|
|
|
//
|
|
// Request timed out.
|
|
//
|
|
logicalUnit->RequestTimeoutCounter = PD_TIMER_STOPPED;
|
|
|
|
DebugPrint((1,"IdePortTickHandler: Request timed out\n"));
|
|
|
|
resetContext.DeviceExtension = deviceExtension;
|
|
resetContext.PathId = logicalUnit->PathId;
|
|
resetContext.NewResetSequence = TRUE;
|
|
resetContext.ResetSrb = NULL;
|
|
|
|
if (deviceExtension->InterruptObject) {
|
|
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
IdeResetBusSynchronized,
|
|
&resetContext)) {
|
|
|
|
DebugPrint((1,"IdePortTickHanlder: Reset failed\n"));
|
|
} else {
|
|
|
|
//
|
|
// Log the reset.
|
|
//
|
|
IdeLogResetError( deviceExtension,
|
|
logicalUnit->SrbData.CurrentSrb,
|
|
('P'<<24) | 257);
|
|
}
|
|
}
|
|
|
|
} else if (logicalUnit->RequestTimeoutCounter > 0) {
|
|
|
|
//
|
|
// Decrement timeout count.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter--;
|
|
|
|
}
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
deviceExtension,
|
|
logicalUnit,
|
|
IdePortTickHandler
|
|
);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
return;
|
|
|
|
} // end IdePortTickHandler()
|
|
|
|
|
|
BOOLEAN
|
|
AtapiRestartBusyRequest (
|
|
PFDO_EXTENSION DeviceExtension,
|
|
PPDO_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
|
|
//
|
|
// Check for busy requests.
|
|
//
|
|
|
|
if (LogicalUnit->LuFlags & PD_LOGICAL_UNIT_IS_BUSY) {
|
|
|
|
//
|
|
// If a request sense is needed or the queue is
|
|
// frozen, defer processing this busy request until
|
|
// that special processing has completed. This prevents
|
|
// a random busy request from being started when a REQUEST
|
|
// SENSE needs to be sent.
|
|
//
|
|
|
|
if (!(LogicalUnit->LuFlags &
|
|
(PD_NEED_REQUEST_SENSE | PD_QUEUE_FROZEN))) {
|
|
|
|
DebugPrint((1,"IdePortTickHandler: Retrying busy status request\n"));
|
|
|
|
//
|
|
// Clear the busy flag and retry the request. Release the
|
|
// spinlock while the call to IoStartPacket is made.
|
|
//
|
|
|
|
CLRMASK (LogicalUnit->LuFlags, PD_LOGICAL_UNIT_IS_BUSY | PD_QUEUE_IS_FULL);
|
|
irp = LogicalUnit->BusyRequest;
|
|
|
|
//
|
|
// Clear the busy request.
|
|
//
|
|
|
|
LogicalUnit->BusyRequest = NULL;
|
|
|
|
//
|
|
// check if the device is gone
|
|
//
|
|
if (LogicalUnit->PdoState & (PDOS_SURPRISE_REMOVED | PDOS_REMOVED)) {
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
LogicalUnit,
|
|
irp
|
|
);
|
|
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
IoStartPacket(DeviceExtension->DeviceObject, irp, (PULONG)NULL, NULL);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
IdePortDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the device control dispatcher.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
UCHAR scsiBus;
|
|
NTSTATUS status;
|
|
ULONG j;
|
|
|
|
|
|
//
|
|
// Initialize the information field.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
//
|
|
// Get adapter capabilities.
|
|
//
|
|
|
|
case IOCTL_SCSI_GET_CAPABILITIES:
|
|
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength
|
|
< sizeof(IO_SCSI_CAPABILITIES)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// this is dynamic
|
|
//
|
|
deviceExtension->Capabilities.AdapterUsesPio = FALSE;
|
|
for (j=0; j<deviceExtension->HwDeviceExtension->MaxIdeDevice; j++) {
|
|
|
|
deviceExtension->Capabilities.AdapterUsesPio |=
|
|
!(deviceExtension->HwDeviceExtension->DeviceFlags[j] & DFLAGS_USE_DMA);
|
|
}
|
|
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
&deviceExtension->Capabilities,
|
|
sizeof(IO_SCSI_CAPABILITIES));
|
|
|
|
Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_SCSI_PASS_THROUGH:
|
|
|
|
status = IdeSendScsiPassThrough(deviceExtension, Irp, FALSE);
|
|
break;
|
|
|
|
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
|
|
|
status = IdeSendScsiPassThrough(deviceExtension, Irp, TRUE);
|
|
break;
|
|
|
|
case IOCTL_SCSI_MINIPORT:
|
|
|
|
status = IdeSendMiniPortIoctl( deviceExtension, Irp);
|
|
break;
|
|
|
|
case IOCTL_SCSI_GET_INQUIRY_DATA:
|
|
|
|
status = IdeGetInquiryData(deviceExtension, Irp);
|
|
break;
|
|
|
|
case IOCTL_SCSI_RESCAN_BUS:
|
|
|
|
//
|
|
// should return only after we get the device relation irp
|
|
// this will be fixed if needed.
|
|
//
|
|
IoInvalidateDeviceRelations (
|
|
deviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
return ChannelDeviceIoControl (DeviceObject, Irp);
|
|
break;
|
|
|
|
} // end switch
|
|
|
|
//
|
|
// Set status in Irp.
|
|
//
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
} // end IdePortDeviceControl()
|
|
|
|
|
|
BOOLEAN
|
|
IdeStartIoSynchronized (
|
|
PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the dependent driver start io routine.
|
|
It also starts the request timer for the logical unit if necesary and
|
|
inserts the SRB data structure in to the requset list.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies the pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns the value returned by the dependent start I/O routine.
|
|
|
|
Notes:
|
|
|
|
The port driver spinlock must be held when this routine is called.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject = ServiceContext;
|
|
PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PSRB_DATA srbData;
|
|
BOOLEAN timerStarted;
|
|
BOOLEAN returnValue;
|
|
BOOLEAN resetRequest;
|
|
|
|
DebugPrint((3, "IdePortStartIoSynchronized: Enter routine\n"));
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(deviceObject->CurrentIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
|
|
//
|
|
// Get the logical unit extension.
|
|
//
|
|
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
//
|
|
// Check for a reset hold. If one is in progress then flag it and return.
|
|
// The timer will reset the current request. This check should be made
|
|
// before anything else is done.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_RESET_HOLD) {
|
|
|
|
DebugPrint ((1, "IdeStartIoSynchronized: PD_RESET_HOLD set...request is held for later..\n"));
|
|
|
|
deviceExtension->InterruptData.InterruptFlags |= PD_HELD_REQUEST;
|
|
return(TRUE);
|
|
}
|
|
|
|
if ((((srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH) ||
|
|
(srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) &&
|
|
(((PATA_PASS_THROUGH) (srb->DataBuffer))->IdeReg.bReserved & ATA_PTFLAGS_BUS_RESET))) {
|
|
|
|
resetRequest = TRUE;
|
|
|
|
} else {
|
|
|
|
resetRequest = FALSE;
|
|
}
|
|
|
|
//
|
|
// Start the port timer. This ensures that the miniport asks for
|
|
// the next request in a resonable amount of time. Set the device
|
|
// busy flag to indicate it is ok to start the next request.
|
|
//
|
|
|
|
deviceExtension->PortTimeoutCounter = srb->TimeOutValue;
|
|
deviceExtension->Flags |= PD_DEVICE_IS_BUSY;
|
|
|
|
//
|
|
// Start the logical unit timer if it is not currently running.
|
|
//
|
|
|
|
if (logicalUnit->RequestTimeoutCounter == PD_TIMER_STOPPED) {
|
|
|
|
//
|
|
// Set request timeout value from Srb SCSI extension in Irp.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = srb->TimeOutValue;
|
|
timerStarted = TRUE;
|
|
|
|
} else {
|
|
timerStarted = FALSE;
|
|
}
|
|
|
|
//
|
|
// Indicate that there maybe more requests queued, if this is not a bypass
|
|
// request.
|
|
//
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)) {
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) {
|
|
|
|
//
|
|
// This request does not allow disconnects. Remember that so
|
|
// no more requests are started until this one completes.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->Flags, PD_DISCONNECT_RUNNING);
|
|
}
|
|
|
|
logicalUnit->LuFlags |= PD_LOGICAL_UNIT_IS_ACTIVE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this is an abort request make sure that it still looks valid.
|
|
//
|
|
|
|
if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
|
|
|
|
srbData = IdeGetSrbData(deviceExtension, srb);
|
|
|
|
//
|
|
// Make sure the srb request is still active.
|
|
//
|
|
|
|
if (srbData == NULL || srbData->CurrentSrb == NULL
|
|
|| !(srbData->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) {
|
|
|
|
//
|
|
// Mark the Srb as active.
|
|
//
|
|
|
|
srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;
|
|
|
|
if (timerStarted) {
|
|
logicalUnit->RequestTimeoutCounter = PD_TIMER_STOPPED;
|
|
}
|
|
|
|
//
|
|
// The request is gone.
|
|
//
|
|
|
|
DebugPrint((1, "IdePortStartIO: Request completed be for it was aborted.\n"));
|
|
srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
|
|
IdePortNotification(IdeRequestComplete,
|
|
deviceExtension + 1,
|
|
srb);
|
|
|
|
IdePortNotification(IdeNextRequest,
|
|
deviceExtension + 1);
|
|
|
|
//
|
|
// Queue a DPC to process the work that was just indicated.
|
|
//
|
|
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Any untagged request that bypasses the queue
|
|
// clears the need request sense flag.
|
|
//
|
|
|
|
CLRMASK (logicalUnit->LuFlags, PD_NEED_REQUEST_SENSE);
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) {
|
|
|
|
//
|
|
// This request does not allow disconnects. Remember that so
|
|
// no more requests are started until this one completes.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->Flags, PD_DISCONNECT_RUNNING);
|
|
}
|
|
|
|
//
|
|
// Set the timeout value in the logical unit.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = srb->TimeOutValue;
|
|
}
|
|
|
|
//
|
|
// Mark the Srb as active.
|
|
//
|
|
|
|
srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE;
|
|
|
|
|
|
#if 0
|
|
//joedai
|
|
{
|
|
ULONG c;
|
|
PUCHAR s;
|
|
PUCHAR d;
|
|
|
|
s = (PUCHAR) deviceObject->CurrentIrp;
|
|
d = (PUCHAR) &deviceExtension->debugData[deviceExtension->nextEntry].irp;
|
|
deviceExtension->debugDataPtr[deviceExtension->nextEntry].irp = (PIRP) d;
|
|
for (c=0; c<sizeof(IRP); c++) {
|
|
d[c] = s[c];
|
|
}
|
|
|
|
if (deviceObject->CurrentIrp->MdlAddress) {
|
|
s = (PUCHAR) deviceObject->CurrentIrp->MdlAddress;
|
|
d = (PUCHAR) &deviceExtension->debugData[deviceExtension->nextEntry].mdl;
|
|
deviceExtension->debugDataPtr[deviceExtension->nextEntry].mdl = (PMDL) d;
|
|
for (c=0; c<sizeof(MDL); c++) {
|
|
d[c] = s[c];
|
|
}
|
|
} else {
|
|
d = (PUCHAR) &deviceExtension->debugData[deviceExtension->nextEntry].mdl;
|
|
deviceExtension->debugDataPtr[deviceExtension->nextEntry].mdl = (PMDL) d;
|
|
for (c=0; c<sizeof(MDL); c++) {
|
|
d[c] = 0;
|
|
}
|
|
}
|
|
s = (PUCHAR) srb;
|
|
d = (PUCHAR) &deviceExtension->debugData[deviceExtension->nextEntry].srb;
|
|
deviceExtension->debugDataPtr[deviceExtension->nextEntry].srb = (PSCSI_REQUEST_BLOCK) d;
|
|
for (c=0; c<sizeof(SCSI_REQUEST_BLOCK); c++) {
|
|
d[c] = s[c];
|
|
}
|
|
ASSERT((((ULONG)srb->DataBuffer) & 0x80000000));
|
|
|
|
deviceExtension->nextEntry = (deviceExtension->nextEntry + 1) % NUM_DEBUG_ENTRY;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// maybe the device is gone
|
|
//
|
|
if (logicalUnit->PdoState & PDOS_DEADMEAT) {
|
|
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
IdePortNotification(IdeRequestComplete,
|
|
deviceExtension + 1,
|
|
srb);
|
|
|
|
IdePortNotification(IdeNextRequest,
|
|
deviceExtension + 1);
|
|
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (resetRequest) {
|
|
|
|
RESET_CONTEXT resetContext;
|
|
|
|
resetContext.DeviceExtension = deviceExtension;
|
|
resetContext.PathId = 0;
|
|
resetContext.NewResetSequence = TRUE;
|
|
resetContext.ResetSrb = srb;
|
|
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
|
|
returnValue = IdeResetBusSynchronized (&resetContext);
|
|
|
|
} else {
|
|
|
|
returnValue = AtapiStartIo (deviceExtension->HwDeviceExtension,
|
|
srb);
|
|
}
|
|
|
|
//
|
|
// Check for miniport work requests.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
}
|
|
|
|
return returnValue;
|
|
|
|
} // end IdeStartIoSynchronized()
|
|
|
|
BOOLEAN
|
|
IdeTimeoutSynchronized (
|
|
PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles a port timeout. There are two reason these can occur
|
|
either because of a reset hold or a time out waiting for a read for next
|
|
request notification. If a reset hold completes, then any held request
|
|
must be started. If a timeout occurs, then the bus must be reset.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies the pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If a timeout error should be logged.
|
|
|
|
Notes:
|
|
|
|
The port driver spinlock must be held when this routine is called.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject = ServiceContext;
|
|
PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension;
|
|
ULONG i;
|
|
BOOLEAN enumProbing = FALSE;
|
|
BOOLEAN noErrorLog = FALSE;
|
|
|
|
DebugPrint((3, "IdeTimeoutSynchronized: Enter routine\n"));
|
|
|
|
//
|
|
// Make sure the timer is stopped.
|
|
//
|
|
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_STOPPED;
|
|
|
|
//
|
|
// Check for a reset hold. If one is in progress then clear it and check
|
|
// for a pending held request
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_RESET_HOLD) {
|
|
|
|
CLRMASK (deviceExtension->InterruptData.InterruptFlags, PD_RESET_HOLD);
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_HELD_REQUEST) {
|
|
|
|
//
|
|
// Clear the held request flag and restart the request.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->InterruptData.InterruptFlags, PD_HELD_REQUEST);
|
|
IdeStartIoSynchronized(ServiceContext);
|
|
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Miniport is hung and not accepting new requests. So reset the
|
|
// bus to clear things up.
|
|
//
|
|
|
|
if (deviceExtension->HwDeviceExtension->CurrentSrb) {
|
|
|
|
deviceExtension->HwDeviceExtension->TimeoutCount[
|
|
deviceExtension->HwDeviceExtension->CurrentSrb->TargetId
|
|
]++;
|
|
|
|
//
|
|
// Many harddrives fail to respond to the first DMA operation
|
|
// We then reset the device and subsequently everything works fine
|
|
// The hack is to mask this error from being logged in the system logs
|
|
//
|
|
|
|
if (deviceExtension->HwDeviceExtension->TimeoutCount[
|
|
deviceExtension->HwDeviceExtension->CurrentSrb->TargetId
|
|
] == 1) {
|
|
noErrorLog=TRUE;
|
|
}
|
|
|
|
enumProbing = TestForEnumProbing (deviceExtension->HwDeviceExtension->CurrentSrb);
|
|
}
|
|
|
|
if (!enumProbing) {
|
|
|
|
DebugPrint((0,
|
|
"IdeTimeoutSynchronized: DevObj 0x%x Next request timed out. Resetting bus..currentSrb=0x%x\n",
|
|
deviceObject,
|
|
deviceExtension->HwDeviceExtension->CurrentSrb));
|
|
}
|
|
|
|
ASSERT (deviceExtension->ResetSrb == 0);
|
|
deviceExtension->ResetSrb = NULL;
|
|
deviceExtension->ResetCallAgain = 0;
|
|
AtapiResetController (deviceExtension->HwDeviceExtension,
|
|
0,
|
|
&deviceExtension->ResetCallAgain);
|
|
|
|
//
|
|
// Set the reset hold flag and start the counter.
|
|
// if we are doing enumertion, don't set the flag
|
|
// we shouldn't set the flag if ResetCallAgain is not set
|
|
//
|
|
if (!enumProbing &&
|
|
(deviceExtension->ResetCallAgain)) {
|
|
|
|
ASSERT(deviceExtension->ResetCallAgain);
|
|
|
|
deviceExtension->InterruptData.InterruptFlags |= PD_RESET_HOLD;
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_RESET_HOLD_TIME;
|
|
|
|
} else {
|
|
|
|
ASSERT(deviceExtension->ResetCallAgain == 0);
|
|
|
|
}
|
|
|
|
//
|
|
// Check for miniport work requests.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
if (enumProbing || noErrorLog) {
|
|
|
|
return(FALSE);
|
|
} else {
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
} // end IdeTimeoutSynchronized()
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
IdeBuildAndSendIrp (
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
|
|
IN PVOID CompletionContext
|
|
)
|
|
{
|
|
|
|
LARGE_INTEGER largeInt;
|
|
NTSTATUS status = STATUS_PENDING;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
//
|
|
// why?
|
|
//
|
|
largeInt.QuadPart = (LONGLONG) 1;
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
//
|
|
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
|
|
PdoExtension->DeviceObject,
|
|
Srb->DataBuffer,
|
|
Srb->DataTransferLength,
|
|
&largeInt,
|
|
NULL);
|
|
|
|
if (irp == NULL) {
|
|
|
|
IdeLogNoMemoryError(PdoExtension->ParentDeviceExtension,
|
|
PdoExtension->TargetId,
|
|
NonPagedPool,
|
|
IoSizeOfIrp(PdoExtension->DeviceObject->StackSize),
|
|
IDEPORT_TAG_SEND_IRP
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto GetOut;
|
|
}
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
(PIO_COMPLETION_ROUTINE)CompletionRoutine,
|
|
CompletionContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
irpStack->Parameters.Scsi.Srb = Srb;
|
|
|
|
//
|
|
// put the irp in the original request field
|
|
//
|
|
Srb->OriginalRequest = irp;
|
|
|
|
(VOID)IoCallDriver(PdoExtension->DeviceObject, irp);
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
GetOut:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdeFreeIrpAndMdl(
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
ASSERT(Irp);
|
|
|
|
if (Irp->MdlAddress != NULL) {
|
|
MmUnlockPages(Irp->MdlAddress);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
|
|
Irp->MdlAddress = NULL;
|
|
}
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
IssueRequestSense(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
IN PSCSI_REQUEST_BLOCK FailingSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a REQUEST SENSE request and uses IoCallDriver to
|
|
renter the driver. The completion routine cleans up the data structures
|
|
and processes the logical unit queue according to the flags.
|
|
|
|
A pointer to failing SRB is stored at the end of the request sense
|
|
Srb, so that the completion routine can find it.
|
|
|
|
Arguments:
|
|
|
|
DeviceExension - Supplies a pointer to the pdo device extension
|
|
|
|
FailingSrb - Supplies a pointer to the request that the request sense
|
|
is being done for.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP irp;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
PVOID *pointer;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
KIRQL currentIrql;
|
|
NTSTATUS status;
|
|
|
|
#if DBG
|
|
PIO_STACK_LOCATION failingIrpStack;
|
|
PIRP failingIrp;
|
|
PLOGICAL_UNIT_EXTENSION failingLogicalUnit;
|
|
#endif
|
|
|
|
DebugPrint((3,"IssueRequestSense: Enter routine\n"));
|
|
|
|
//
|
|
// Build the asynchronous request
|
|
// to be sent to the port driver.
|
|
//
|
|
// Allocate Srb from non-paged pool
|
|
// plus room for a pointer to the failing IRP.
|
|
// Note this routine is in an error-handling
|
|
// path and is a shortterm allocation.
|
|
//
|
|
|
|
srb = ExAllocatePool(NonPagedPool,
|
|
sizeof(SCSI_REQUEST_BLOCK) + sizeof(PVOID));
|
|
|
|
if (srb == NULL) {
|
|
DebugPrint((1, "IssueRequest sense - pool allocation failed\n"));
|
|
goto Getout;
|
|
}
|
|
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
//
|
|
// Save the Failing SRB after the request sense Srb.
|
|
//
|
|
|
|
pointer = (PVOID *) (srb+1);
|
|
*pointer = FailingSrb;
|
|
|
|
//
|
|
// Build the REQUEST SENSE CDB.
|
|
//
|
|
|
|
srb->CdbLength = 6;
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
|
|
cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
|
|
cdb->CDB6INQUIRY.Reserved1 = 0;
|
|
cdb->CDB6INQUIRY.PageCode = 0;
|
|
cdb->CDB6INQUIRY.IReserved = 0;
|
|
cdb->CDB6INQUIRY.AllocationLength =
|
|
(UCHAR)FailingSrb->SenseInfoBufferLength;
|
|
cdb->CDB6INQUIRY.Control = 0;
|
|
|
|
|
|
|
|
//
|
|
// Set up SCSI bus address.
|
|
//
|
|
|
|
srb->TargetId = FailingSrb->TargetId;
|
|
srb->Lun = FailingSrb->Lun;
|
|
srb->PathId = FailingSrb->PathId;
|
|
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Set timeout value to 16 seconds.
|
|
//
|
|
|
|
srb->TimeOutValue = 0x10;
|
|
|
|
//
|
|
// Disable auto request sense.
|
|
//
|
|
|
|
srb->SenseInfoBufferLength = 0;
|
|
|
|
//
|
|
// Sense buffer is in stack.
|
|
//
|
|
|
|
srb->SenseInfoBuffer = NULL;
|
|
|
|
//
|
|
// Set read and bypass frozen queue bits in flags.
|
|
//
|
|
|
|
//
|
|
// Set SRB flags to indicate the logical unit queue should be by
|
|
// passed and that no queue processing should be done when the request
|
|
// completes. Also disable disconnect and synchronous data
|
|
// transfer if necessary.
|
|
//
|
|
|
|
srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE |
|
|
SRB_FLAGS_DISABLE_DISCONNECT;
|
|
|
|
if (FailingSrb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) {
|
|
srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
}
|
|
|
|
srb->DataBuffer = FailingSrb->SenseInfoBuffer;
|
|
|
|
//
|
|
// Set the transfer length.
|
|
//
|
|
|
|
srb->DataTransferLength = FailingSrb->SenseInfoBufferLength;
|
|
|
|
//
|
|
// Zero out status.
|
|
//
|
|
|
|
srb->ScsiStatus = srb->SrbStatus = 0;
|
|
|
|
srb->NextSrb = 0;
|
|
|
|
#if DBG
|
|
//
|
|
// This was added to catch a bug where the original request
|
|
// was pointing to a pnp irp
|
|
//
|
|
ASSERT(FailingSrb->OriginalRequest);
|
|
failingIrp = FailingSrb->OriginalRequest;
|
|
failingIrpStack = IoGetCurrentIrpStackLocation(failingIrp);
|
|
failingLogicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (failingIrpStack);
|
|
ASSERT(failingLogicalUnit);
|
|
#endif
|
|
|
|
status = IdeBuildAndSendIrp(PdoExtension,
|
|
srb,
|
|
IdePortInternalCompletion,
|
|
srb
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
ASSERT(status == STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
Getout:
|
|
if (srb) {
|
|
ExFreePool(srb);
|
|
}
|
|
|
|
irp = FailingSrb->OriginalRequest;
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
//
|
|
// Clear the request sense flag. Since IdeStartIoSync will never get called, this
|
|
// flag won't be cleared.
|
|
//
|
|
KeAcquireSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
CLRMASK (logicalUnit->LuFlags, PD_NEED_REQUEST_SENSE);
|
|
KeReleaseSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, currentIrql);
|
|
|
|
//
|
|
// unfreeze the queue if necessary
|
|
//
|
|
ASSERT(FailingSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN);
|
|
if ((FailingSrb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE) &&
|
|
(FailingSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN)) {
|
|
|
|
|
|
CLRMASK (logicalUnit->LuFlags, PD_QUEUE_FROZEN);
|
|
|
|
KeAcquireSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
GetNextLuRequest(logicalUnit->ParentDeviceExtension, logicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
|
|
CLRMASK (FailingSrb->SrbStatus, SRB_STATUS_QUEUE_FROZEN);
|
|
}
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
IDEPORT_GET_LUNEXT_IN_IRP(irpStack)->ParentDeviceExtension,
|
|
IDEPORT_GET_LUNEXT_IN_IRP(irpStack),
|
|
irp
|
|
);
|
|
|
|
//
|
|
// Complete the original request
|
|
//
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
|
|
return;
|
|
|
|
} // end IssueRequestSense()
|
|
|
|
|
|
NTSTATUS
|
|
IdePortInternalCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Device object
|
|
IRP
|
|
Context - pointer to SRB
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb = Context;
|
|
PSCSI_REQUEST_BLOCK failingSrb;
|
|
PIRP failingIrp;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSENSE_DATA senseBuffer;
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension;
|
|
KIRQL currentIrql;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
DebugPrint((3,"IdePortInternalCompletion: Enter routine\n"));
|
|
|
|
//
|
|
// If RESET_BUS or ABORT_COMMAND request
|
|
// then free pool and return.
|
|
//
|
|
|
|
if ((srb->Function == SRB_FUNCTION_ABORT_COMMAND) ||
|
|
(srb->Function == SRB_FUNCTION_RESET_BUS)) {
|
|
|
|
//
|
|
// Deallocate internal SRB and IRP.
|
|
//
|
|
|
|
ExFreePool(srb);
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Request sense completed. If successful or data over/underrun
|
|
// get the failing SRB and indicate that the sense information
|
|
// is valid. The class driver will check for underrun and determine
|
|
// if there is enough sense information to be useful.
|
|
//
|
|
|
|
//
|
|
// Get a pointer to failing Irp and Srb.
|
|
//
|
|
|
|
failingSrb = *((PVOID *) (srb+1));
|
|
failingIrp = failingSrb->OriginalRequest;
|
|
irpStack = IoGetCurrentIrpStackLocation(failingIrp);
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
|
|
if ((SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) ||
|
|
(SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)) {
|
|
|
|
//
|
|
// Report sense buffer is valid.
|
|
//
|
|
|
|
failingSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
|
|
|
|
//
|
|
// Copy bytes transferred to failing SRB
|
|
// request sense length field to communicate
|
|
// to the class drivers the number of valid
|
|
// sense bytes.
|
|
//
|
|
|
|
failingSrb->SenseInfoBufferLength = (UCHAR) srb->DataTransferLength;
|
|
|
|
#if 0
|
|
//
|
|
// enable for debugging only
|
|
// if sense buffer is smaller than 13 bytes, then the debugprint
|
|
// below could bugchecl the system
|
|
//
|
|
|
|
//
|
|
// Print the sense buffer for debugging purposes.
|
|
//
|
|
senseBuffer = failingSrb->SenseInfoBuffer;
|
|
DebugPrint((DBG_ATAPI_DEVICES, "CDB=%x, SenseKey=%x, ASC=%x, ASQ=%x\n",
|
|
failingSrb->Cdb[0],
|
|
senseBuffer->SenseKey, senseBuffer->AdditionalSenseCode,
|
|
senseBuffer->AdditionalSenseCodeQualifier));
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Clear the request sense flag. If we fail due to fault injection
|
|
// IdeStartIo won't get called and this flag never gets cleared.
|
|
//
|
|
KeAcquireSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
CLRMASK (logicalUnit->LuFlags, PD_NEED_REQUEST_SENSE);
|
|
KeReleaseSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, currentIrql);
|
|
|
|
//
|
|
// unfreeze the queue if necessary
|
|
//
|
|
ASSERT(failingSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN);
|
|
if ((failingSrb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE) &&
|
|
(failingSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN)) {
|
|
|
|
|
|
CLRMASK (logicalUnit->LuFlags, PD_QUEUE_FROZEN);
|
|
|
|
KeAcquireSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
GetNextLuRequest(logicalUnit->ParentDeviceExtension, logicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
|
|
CLRMASK (failingSrb->SrbStatus, SRB_STATUS_QUEUE_FROZEN);
|
|
}
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
IDEPORT_GET_LUNEXT_IN_IRP(irpStack)->ParentDeviceExtension,
|
|
IDEPORT_GET_LUNEXT_IN_IRP(irpStack),
|
|
failingIrp
|
|
);
|
|
|
|
//
|
|
// Complete the failing request.
|
|
//
|
|
|
|
|
|
IoCompleteRequest(failingIrp, IO_DISK_INCREMENT);
|
|
|
|
//
|
|
// Deallocate internal SRB, MDL and IRP.
|
|
//
|
|
|
|
ExFreePool(srb);
|
|
|
|
IdeFreeIrpAndMdl(Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // IdePortInternalCompletion()
|
|
|
|
|
|
BOOLEAN
|
|
IdeGetInterruptState(
|
|
IN PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine saves the InterruptFlags, MapTransferParameters and
|
|
CompletedRequests fields and clears the InterruptFlags.
|
|
|
|
This routine also removes the request from the logical unit queue if it is
|
|
tag. Finally the request time is updated.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies a pointer to the interrupt context which contains
|
|
pointers to the interrupt data and where to save it.
|
|
|
|
Return Value:
|
|
|
|
Returns TURE if there is new work and FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Called via KeSynchronizeExecution with the port device extension spinlock
|
|
held.
|
|
|
|
--*/
|
|
{
|
|
PINTERRUPT_CONTEXT interruptContext = ServiceContext;
|
|
ULONG limit = 0;
|
|
PFDO_EXTENSION deviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PSRB_DATA srbData;
|
|
PSRB_DATA nextSrbData;
|
|
BOOLEAN isTimed;
|
|
|
|
deviceExtension = interruptContext->DeviceExtension;
|
|
|
|
//
|
|
// Check for pending work.
|
|
//
|
|
|
|
if (!(deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Move the interrupt state to save area.
|
|
//
|
|
|
|
*interruptContext->SavedInterruptData = deviceExtension->InterruptData;
|
|
|
|
//
|
|
// Clear the interrupt state.
|
|
//
|
|
|
|
deviceExtension->InterruptData.InterruptFlags &= PD_INTERRUPT_FLAG_MASK;
|
|
deviceExtension->InterruptData.CompletedRequests = NULL;
|
|
deviceExtension->InterruptData.ReadyLogicalUnit = NULL;
|
|
deviceExtension->InterruptData.CompletedAbort = NULL;
|
|
deviceExtension->InterruptData.PdoExtensionResetBus = NULL;
|
|
|
|
srbData = interruptContext->SavedInterruptData->CompletedRequests;
|
|
|
|
while (srbData != NULL) {
|
|
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
ASSERT(limit++ < 100);
|
|
|
|
//
|
|
// Get a pointer to the SRB and the logical unit extension.
|
|
//
|
|
|
|
ASSERT(srbData->CurrentSrb != NULL);
|
|
srb = srbData->CurrentSrb;
|
|
|
|
irp = srb->OriginalRequest;
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
//
|
|
// If the request did not succeed, then check for the special cases.
|
|
//
|
|
|
|
if (srb->SrbStatus != SRB_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// If this request failed and a REQUEST SENSE command needs to
|
|
// be done, then set a flag to indicate this and prevent other
|
|
// commands from being started.
|
|
//
|
|
|
|
if (NEED_REQUEST_SENSE(srb)) {
|
|
|
|
if (logicalUnit->LuFlags & PD_NEED_REQUEST_SENSE) {
|
|
|
|
//
|
|
// This implies that requests have completed with a
|
|
// status of check condition before a REQUEST SENSE
|
|
// command could be preformed. This should never occur.
|
|
// Convert the request to another code so that only one
|
|
// auto request sense is issued.
|
|
//
|
|
|
|
srb->ScsiStatus = 0;
|
|
srb->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Indicate that an auto request sense needs to be done.
|
|
//
|
|
|
|
logicalUnit->LuFlags |= PD_NEED_REQUEST_SENSE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logicalUnit->RequestTimeoutCounter = PD_TIMER_STOPPED;
|
|
srbData = srbData->CompletedRequests;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
VOID
|
|
IdePortAllocateAccessToken (
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
if (!fdoExtension->SyncAccessInterface.AllocateAccessToken) {
|
|
|
|
CallIdeStartIoSynchronized (
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
DeviceObject
|
|
);
|
|
|
|
} else {
|
|
|
|
(*fdoExtension->SyncAccessInterface.AllocateAccessToken) (
|
|
fdoExtension->SyncAccessInterface.Token,
|
|
CallIdeStartIoSynchronized,
|
|
DeviceObject
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
IO_ALLOCATION_ACTION
|
|
CallIdeStartIoSynchronized (
|
|
IN PVOID Reserved1,
|
|
IN PVOID Reserved2,
|
|
IN PVOID Reserved3,
|
|
IN PVOID DeviceObject
|
|
)
|
|
{
|
|
PFDO_EXTENSION deviceExtension = ((PDEVICE_OBJECT) DeviceObject)->DeviceExtension;
|
|
KIRQL currentIrql;
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
|
|
|
KeSynchronizeExecution (
|
|
deviceExtension->InterruptObject,
|
|
IdeStartIoSynchronized,
|
|
DeviceObject
|
|
);
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
|
|
|
return KeepObject;
|
|
}
|
|
|
|
|
|
VOID
|
|
LogErrorEntry(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PERROR_LOG_ENTRY LogEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates an I/O error log record, fills it in and writes it
|
|
to the I/O error log.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the port device extension.
|
|
|
|
LogEntry - Supplies a pointer to the scsi port log entry.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
|
|
DeviceExtension->DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET) + 4 * sizeof(ULONG)
|
|
);
|
|
|
|
if (errorLogEntry != NULL) {
|
|
|
|
//
|
|
// Translate the miniport error code into the NT I\O driver.
|
|
//
|
|
|
|
switch (LogEntry->ErrorCode) {
|
|
case SP_BUS_PARITY_ERROR:
|
|
errorLogEntry->ErrorCode = IO_ERR_PARITY;
|
|
break;
|
|
|
|
case SP_UNEXPECTED_DISCONNECT:
|
|
errorLogEntry->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
case SP_INVALID_RESELECTION:
|
|
errorLogEntry->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
case SP_BUS_TIME_OUT:
|
|
errorLogEntry->ErrorCode = IO_ERR_TIMEOUT;
|
|
break;
|
|
|
|
case SP_PROTOCOL_ERROR:
|
|
errorLogEntry->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
case SP_INTERNAL_ADAPTER_ERROR:
|
|
errorLogEntry->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
case SP_IRQ_NOT_RESPONDING:
|
|
errorLogEntry->ErrorCode = IO_ERR_INCORRECT_IRQL;
|
|
break;
|
|
|
|
case SP_BAD_FW_ERROR:
|
|
errorLogEntry->ErrorCode = IO_ERR_BAD_FIRMWARE;
|
|
break;
|
|
|
|
case SP_BAD_FW_WARNING:
|
|
errorLogEntry->ErrorCode = IO_WRN_BAD_FIRMWARE;
|
|
break;
|
|
|
|
default:
|
|
errorLogEntry->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
}
|
|
|
|
errorLogEntry->SequenceNumber = LogEntry->SequenceNumber;
|
|
errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI;
|
|
errorLogEntry->RetryCount = (UCHAR) LogEntry->ErrorLogRetryCount;
|
|
errorLogEntry->UniqueErrorValue = LogEntry->UniqueId;
|
|
errorLogEntry->FinalStatus = STATUS_SUCCESS;
|
|
errorLogEntry->DumpDataSize = 4 * sizeof(ULONG);
|
|
errorLogEntry->DumpData[0] = LogEntry->PathId;
|
|
errorLogEntry->DumpData[1] = LogEntry->TargetId;
|
|
errorLogEntry->DumpData[2] = LogEntry->Lun;
|
|
errorLogEntry->DumpData[3] = LogEntry->ErrorCode;
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
PCHAR errorCodeString;
|
|
|
|
switch (LogEntry->ErrorCode) {
|
|
case SP_BUS_PARITY_ERROR:
|
|
errorCodeString = "SCSI bus partity error";
|
|
break;
|
|
|
|
case SP_UNEXPECTED_DISCONNECT:
|
|
errorCodeString = "Unexpected disconnect";
|
|
break;
|
|
|
|
case SP_INVALID_RESELECTION:
|
|
errorCodeString = "Invalid reselection";
|
|
break;
|
|
|
|
case SP_BUS_TIME_OUT:
|
|
errorCodeString = "SCSI bus time out";
|
|
break;
|
|
|
|
case SP_PROTOCOL_ERROR:
|
|
errorCodeString = "SCSI protocol error";
|
|
break;
|
|
|
|
case SP_INTERNAL_ADAPTER_ERROR:
|
|
errorCodeString = "Internal adapter error";
|
|
break;
|
|
|
|
default:
|
|
errorCodeString = "Unknown error code";
|
|
break;
|
|
|
|
}
|
|
|
|
DebugPrint((DBG_ALWAYS,"LogErrorEntry: Logging SCSI error packet. ErrorCode = %s.\n",
|
|
errorCodeString
|
|
));
|
|
DebugPrint((DBG_ALWAYS,
|
|
"PathId = %2x, TargetId = %2x, Lun = %2x, UniqueId = %x.\n",
|
|
LogEntry->PathId,
|
|
LogEntry->TargetId,
|
|
LogEntry->Lun,
|
|
LogEntry->UniqueId
|
|
));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
GetNextLuPendingRequest(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
if (LogicalUnit->PendingRequest) {
|
|
|
|
GetNextLuRequest(
|
|
DeviceExtension,
|
|
LogicalUnit
|
|
);
|
|
|
|
} else {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef LOG_GET_NEXT_CALLER
|
|
|
|
VOID
|
|
GetNextLuRequest2(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN PUCHAR FileName,
|
|
IN ULONG LineNumber
|
|
)
|
|
#else
|
|
|
|
VOID
|
|
GetNextLuRequest(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
#endif
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get the next request for the specified logical unit. It does
|
|
the necessary initialization to the logical unit structure and submitts the
|
|
request to the device queue. The DeviceExtension SpinLock must be held
|
|
when this function called. It is released by this function.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the port device extension.
|
|
|
|
LogicalUnit - Supplies a pointer to the logical unit extension to get the
|
|
next request from.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKDEVICE_QUEUE_ENTRY packet;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
POWER_STATE powerState;
|
|
PIRP nextIrp;
|
|
BOOLEAN powerUpDevice = FALSE;
|
|
|
|
#ifdef LOG_GET_NEXT_CALLER
|
|
|
|
IdeLogGetNextLuCaller(DeviceExtension,
|
|
LogicalUnit,
|
|
FileName,
|
|
LineNumber
|
|
);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// If the active flag is not set, then the queue is not busy or there is
|
|
// a request being processed and the next request should not be started..
|
|
//
|
|
|
|
if ((!(LogicalUnit->LuFlags & PD_LOGICAL_UNIT_IS_ACTIVE) &&
|
|
(LogicalUnit->PendingRequest == NULL))
|
|
|| (LogicalUnit->SrbData.CurrentSrb)) {
|
|
|
|
DebugPrint ((2, "IdePort GetNextLuRequest: 0x%x 0x%x NOT PD_LOGICAL_UNIT_IS_ACTIVE\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
LogicalUnit->TargetId
|
|
));
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check for pending requests, queue full or busy requests. Pending
|
|
// requests occur when untagged request is started and there are active
|
|
// queued requests. Busy requests occur when the target returns a BUSY
|
|
// or QUEUE FULL status. Busy requests are started by the timer code.
|
|
// Also if the need request sense flag is set, it indicates that
|
|
// an error status was detected on the logical unit. No new requests
|
|
// should be started until this flag is cleared. This flag is cleared
|
|
// by an untagged command that by-passes the LU queue i.e.
|
|
//
|
|
// The busy flag and the need request sense flag have the effect of
|
|
// forcing the queue of outstanding requests to drain after an error or
|
|
// until a busy request gets started.
|
|
//
|
|
|
|
if (LogicalUnit->LuFlags & (PD_LOGICAL_UNIT_IS_BUSY
|
|
| PD_QUEUE_IS_FULL | PD_NEED_REQUEST_SENSE | PD_QUEUE_FROZEN) ||
|
|
(LogicalUnit->PdoState & (PDOS_REMOVED | PDOS_SURPRISE_REMOVED))) {
|
|
|
|
//
|
|
// If the request queue is now empty, then the pending request can
|
|
// be started.
|
|
//
|
|
|
|
DebugPrint((2, "IdePort: GetNextLuRequest: 0x%x 0x%x Ignoring a get next lu call.\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
LogicalUnit->TargetId
|
|
));
|
|
|
|
//
|
|
// Note the active flag is not cleared. So the next request
|
|
// will be processed when the other requests have completed.
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clear the active flag. If there is another request, the flag will be
|
|
// set again when the request is passed to the miniport.
|
|
//
|
|
CLRMASK (LogicalUnit->LuFlags, PD_LOGICAL_UNIT_IS_ACTIVE);
|
|
|
|
LogicalUnit->RetryCount = 0;
|
|
nextIrp = NULL;
|
|
|
|
if (LogicalUnit->PendingRequest) {
|
|
|
|
nextIrp = LogicalUnit->PendingRequest;
|
|
|
|
LogicalUnit->PendingRequest = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Remove the packet from the logical unit device queue.
|
|
//
|
|
packet = KeRemoveByKeyDeviceQueue(&LogicalUnit->DeviceObject->DeviceQueue,
|
|
LogicalUnit->CurrentKey);
|
|
|
|
if (packet != NULL) {
|
|
|
|
nextIrp = CONTAINING_RECORD(packet, IRP, Tail.Overlay.DeviceQueueEntry);
|
|
|
|
#if DBG
|
|
InterlockedDecrement (
|
|
&LogicalUnit->NumberOfIrpQueued
|
|
);
|
|
#endif // DBG
|
|
|
|
}
|
|
}
|
|
|
|
if (!nextIrp) {
|
|
|
|
DebugPrint ((2, "IdePort GetNextLuRequest: 0x%x 0x%x no irp to processing\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
LogicalUnit->TargetId
|
|
));
|
|
}
|
|
|
|
if (nextIrp) {
|
|
|
|
BOOLEAN pendingRequest;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = (PSCSI_REQUEST_BLOCK)irpStack->Parameters.Others.Argument1;
|
|
|
|
if (LogicalUnit->PdoState & PDOS_QUEUE_BLOCKED) {
|
|
|
|
DebugPrint ((2, "IdePort GetNextLuRequest: 0x%x 0x%x Lu must queue\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
LogicalUnit->TargetId
|
|
));
|
|
|
|
pendingRequest = TRUE;
|
|
|
|
if (!(LogicalUnit->PdoState & PDOS_MUST_QUEUE)) {
|
|
|
|
//
|
|
// device is powered down
|
|
// use a large time in case it spins up slowly
|
|
//
|
|
if (srb->TimeOutValue < DEFAULT_SPINUP_TIME) {
|
|
|
|
srb->TimeOutValue = DEFAULT_SPINUP_TIME;
|
|
}
|
|
|
|
//
|
|
// We are not powered up.
|
|
// issue an power up
|
|
//
|
|
powerUpDevice = TRUE;
|
|
|
|
DebugPrint ((2, "IdePort GetNextLuRequest: 0x%x 0x%x need to spin up device, requeue irp 0x%x\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
LogicalUnit->TargetId,
|
|
nextIrp));
|
|
}
|
|
|
|
} else {
|
|
|
|
pendingRequest = FALSE;
|
|
}
|
|
|
|
if (pendingRequest) {
|
|
|
|
ASSERT (LogicalUnit->PendingRequest == NULL);
|
|
LogicalUnit->PendingRequest = nextIrp;
|
|
|
|
nextIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (nextIrp) {
|
|
|
|
//
|
|
// Set the new current key.
|
|
//
|
|
LogicalUnit->CurrentKey = srb->QueueSortKey;
|
|
|
|
//
|
|
// Hack to work-around the starvation led to by numerous requests touching the same sector.
|
|
//
|
|
|
|
LogicalUnit->CurrentKey++;
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
DebugPrint ((2, "GetNextLuRequest: IoStartPacket 0x%x\n", nextIrp));
|
|
|
|
IoStartPacket(DeviceExtension->DeviceObject, nextIrp, (PULONG)NULL, NULL);
|
|
|
|
} else {
|
|
|
|
NTSTATUS status;
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
if (powerUpDevice) {
|
|
|
|
powerState.DeviceState = PowerDeviceD0;
|
|
status = PoRequestPowerIrp (
|
|
LogicalUnit->DeviceObject,
|
|
IRP_MN_SET_POWER,
|
|
powerState,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
ASSERT (NT_SUCCESS(status));
|
|
}
|
|
}
|
|
|
|
} // end GetNextLuRequest()
|
|
|
|
VOID
|
|
IdeLogTimeoutError(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp,
|
|
IN ULONG UniqueId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function logs an error when a request times out.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the port device extension.
|
|
|
|
Irp - Supplies a pointer to the request which timedout.
|
|
|
|
UniqueId - Supplies the UniqueId for this error.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
The port device extension spinlock should be held when this routine is
|
|
called.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSRB_DATA srbData;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
srb = (PSCSI_REQUEST_BLOCK)irpStack->Parameters.Others.Argument1;
|
|
srbData = IdeGetSrbData(DeviceExtension, srb);
|
|
|
|
if (!srbData) {
|
|
return;
|
|
}
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry(DeviceExtension->DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET) + 4 * sizeof(ULONG));
|
|
|
|
if (errorLogEntry != NULL) {
|
|
errorLogEntry->ErrorCode = IO_ERR_TIMEOUT;
|
|
errorLogEntry->SequenceNumber = srbData->SequenceNumber;
|
|
errorLogEntry->MajorFunctionCode = irpStack->MajorFunction;
|
|
errorLogEntry->RetryCount = (UCHAR) srbData->ErrorLogRetryCount;
|
|
errorLogEntry->UniqueErrorValue = UniqueId;
|
|
errorLogEntry->FinalStatus = STATUS_SUCCESS;
|
|
errorLogEntry->DumpDataSize = 4 * sizeof(ULONG);
|
|
errorLogEntry->DumpData[0] = srb->PathId;
|
|
errorLogEntry->DumpData[1] = srb->TargetId;
|
|
errorLogEntry->DumpData[2] = srb->Lun;
|
|
errorLogEntry->DumpData[3] = SP_REQUEST_TIMEOUT;
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IdeLogResetError(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN ULONG UniqueId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function logs an error when the bus is reset.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the port device extension.
|
|
|
|
Srb - Supplies a pointer to the request which timed-out.
|
|
|
|
UniqueId - Supplies the UniqueId for this error.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
The port device extension spinlock should be held when this routine is
|
|
called.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP irp;
|
|
PSRB_DATA srbData;
|
|
ULONG sequenceNumber = 0;
|
|
UCHAR function = 0,
|
|
pathId = 0,
|
|
targetId = 0,
|
|
lun = 0,
|
|
retryCount = 0;
|
|
|
|
if (Srb) {
|
|
|
|
irp = Srb->OriginalRequest;
|
|
|
|
if (irp) {
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
function = irpStack->MajorFunction;
|
|
}
|
|
|
|
srbData = IdeGetSrbData(DeviceExtension, Srb);
|
|
|
|
if (!srbData) {
|
|
return;
|
|
}
|
|
|
|
pathId = Srb->PathId;
|
|
targetId = Srb->TargetId;
|
|
lun = Srb->Lun;
|
|
retryCount = (UCHAR) srbData->ErrorLogRetryCount;
|
|
sequenceNumber = srbData->SequenceNumber;
|
|
|
|
|
|
}
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( DeviceExtension->DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET)
|
|
+ 4 * sizeof(ULONG) );
|
|
|
|
if (errorLogEntry != NULL) {
|
|
errorLogEntry->ErrorCode = IO_ERR_TIMEOUT;
|
|
errorLogEntry->SequenceNumber = sequenceNumber;
|
|
errorLogEntry->MajorFunctionCode = function;
|
|
errorLogEntry->RetryCount = retryCount;
|
|
errorLogEntry->UniqueErrorValue = UniqueId;
|
|
errorLogEntry->FinalStatus = STATUS_SUCCESS;
|
|
errorLogEntry->DumpDataSize = 4 * sizeof(ULONG);
|
|
errorLogEntry->DumpData[0] = pathId;
|
|
errorLogEntry->DumpData[1] = targetId;
|
|
errorLogEntry->DumpData[2] = lun;
|
|
errorLogEntry->DumpData[3] = SP_REQUEST_TIMEOUT;
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeTranslateSrbStatus(
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine translates an srb status into an ntstatus.
|
|
|
|
Arguments:
|
|
|
|
Srb - Supplies a pointer to the failing Srb.
|
|
|
|
Return Value:
|
|
|
|
An nt status approprate for the error.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (SRB_STATUS(Srb->SrbStatus)) {
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_NO_HBA:
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_BUS_RESET:
|
|
case SRB_STATUS_TIMEOUT:
|
|
return(STATUS_IO_TIMEOUT);
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
return(STATUS_DEVICE_NOT_CONNECTED);
|
|
case SRB_STATUS_BAD_FUNCTION:
|
|
case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
case SRB_STATUS_DATA_OVERRUN:
|
|
return(STATUS_BUFFER_OVERFLOW);
|
|
default:
|
|
return(STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
|
|
return(STATUS_IO_DEVICE_ERROR);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IdeResetBusSynchronized (
|
|
PVOID ServiceContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function resets the bus and sets up the port timer so the reset hold
|
|
flag is clean when necessary.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies a pointer to the reset context which includes a
|
|
pointer to the device extension and the pathid to be reset.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the reset succeeds.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESET_CONTEXT resetContext = ServiceContext;
|
|
PFDO_EXTENSION deviceExtension;
|
|
PSCSI_REQUEST_BLOCK resetSrbToComplete;
|
|
BOOLEAN goodReset;
|
|
|
|
resetSrbToComplete = NULL;
|
|
deviceExtension = resetContext->DeviceExtension;
|
|
|
|
//
|
|
// Should never get a reset srb while one is in progress
|
|
//
|
|
if (resetContext->ResetSrb && deviceExtension->ResetSrb) {
|
|
|
|
ASSERT (resetContext->ResetSrb == deviceExtension->ResetSrb);
|
|
}
|
|
|
|
if (resetContext->NewResetSequence) {
|
|
//
|
|
// a new reset sequence to kill the reset in progress if any
|
|
//
|
|
|
|
if (deviceExtension->ResetCallAgain) {
|
|
|
|
DebugPrint ((0, "ATAPI: WARNING: Resetting a reset\n"));
|
|
|
|
deviceExtension->ResetCallAgain = 0;
|
|
|
|
if (deviceExtension->ResetSrb) {
|
|
|
|
resetSrbToComplete = deviceExtension->ResetSrb;
|
|
resetSrbToComplete->SrbStatus = SRB_STATUS_ERROR;
|
|
|
|
deviceExtension->ResetSrb = NULL;
|
|
}
|
|
|
|
}
|
|
deviceExtension->ResetSrb = resetContext->ResetSrb;
|
|
}
|
|
|
|
goodReset = AtapiResetController (
|
|
deviceExtension->HwDeviceExtension,
|
|
resetContext->PathId,
|
|
&deviceExtension->ResetCallAgain);
|
|
|
|
//
|
|
// Set the reset hold flag and start the counter if the reset is not done
|
|
//
|
|
if ((goodReset) && (deviceExtension->ResetCallAgain)) {
|
|
|
|
deviceExtension->InterruptData.InterruptFlags |= PD_RESET_HOLD;
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_RESET_HOLD_TIME;
|
|
|
|
} else {
|
|
|
|
CLRMASK (deviceExtension->InterruptData.InterruptFlags, PD_RESET_HOLD);
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_STOPPED;
|
|
|
|
if (deviceExtension->ResetSrb) {
|
|
|
|
resetSrbToComplete = deviceExtension->ResetSrb;
|
|
deviceExtension->ResetSrb = NULL;
|
|
}
|
|
|
|
if (resetSrbToComplete) {
|
|
|
|
if (goodReset) {
|
|
|
|
resetSrbToComplete->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
resetSrbToComplete->SrbStatus = SRB_STATUS_ERROR;
|
|
}
|
|
}
|
|
|
|
if (goodReset) {
|
|
|
|
IdePortNotification(IdeResetDetected,
|
|
deviceExtension->HwDeviceExtension,
|
|
resetSrbToComplete);
|
|
}
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_HELD_REQUEST) {
|
|
|
|
//
|
|
// Clear the held request flag and restart the request.
|
|
//
|
|
|
|
CLRMASK (deviceExtension->InterruptData.InterruptFlags, PD_HELD_REQUEST);
|
|
IdeStartIoSynchronized(deviceExtension->DeviceObject);
|
|
}
|
|
}
|
|
|
|
if (resetSrbToComplete) {
|
|
|
|
IdePortNotification(IdeRequestComplete,
|
|
deviceExtension->HwDeviceExtension,
|
|
resetSrbToComplete);
|
|
|
|
IdePortNotification(IdeNextRequest,
|
|
deviceExtension->HwDeviceExtension,
|
|
NULL);
|
|
}
|
|
|
|
//
|
|
// Check for miniport work requests.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
//
|
|
// Queue a DPC.
|
|
//
|
|
IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
IdeProcessCompletedRequest(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSRB_DATA SrbData,
|
|
OUT PBOOLEAN CallStartIo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine processes a request which has completed. It completes any
|
|
pending transfers, releases the adapter objects and map registers when
|
|
necessary. It deallocates any resources allocated for the request.
|
|
It processes the return status, by requeueing busy request, requesting
|
|
sense information or logging an error.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension for the
|
|
adapter data.
|
|
|
|
SrbData - Supplies a pointer to the SRB data block to be completed.
|
|
|
|
CallStartIo - This value is set if the start I/O routine needs to be
|
|
called.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PIO_ERROR_LOG_PACKET errorLogPacket;
|
|
ULONG sequenceNumber;
|
|
LONG interlockResult;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PHW_DEVICE_EXTENSION hwDeviceExtension = DeviceExtension->HwDeviceExtension;
|
|
|
|
ASSERT(SrbData->CurrentSrb);
|
|
srb = SrbData->CurrentSrb;
|
|
irp = srb->OriginalRequest;
|
|
|
|
DebugPrint((2,"CompletedRequest: Irp 0x%8x Srb 0x%8x DataBuf 0x%8x Len 0x%8x\n", irp, srb, srb->DataBuffer, srb->DataTransferLength));
|
|
|
|
#ifdef IDE_MULTIPLE_IRP_COMPLETE_REQUESTS_CHECK
|
|
if (irp->CurrentLocation > (CCHAR) (irp->StackCount + 1)) {
|
|
KeBugCheckEx( MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR) irp, (ULONG_PTR) srb, 0, 0 );
|
|
}
|
|
#endif // IDE_MULTIPLE_IRP_COMPLETE_REQUESTS_CHECK
|
|
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
|
|
//
|
|
// Get logical unit extension for this request.
|
|
//
|
|
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
//
|
|
// If miniport needs mapped system addresses, the the
|
|
// data buffer address in the SRB must be restored to
|
|
// original unmapped virtual address. Ensure that this request requires
|
|
// a data transfer.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) {
|
|
if (!SRB_USES_DMA(srb)) {
|
|
if (irp->MdlAddress) {
|
|
|
|
//
|
|
// If an IRP is for a transfer larger than a miniport driver
|
|
// can handle, the request is broken up into multiple smaller
|
|
// requests. Each request uses the same MDL and the data
|
|
// buffer address field in the SRB may not be at the
|
|
// beginning of the memory described by the MDL.
|
|
//
|
|
|
|
srb->DataBuffer = (PCCHAR)MmGetMdlVirtualAddress(irp->MdlAddress) +
|
|
((PCCHAR)srb->DataBuffer - SrbData->SrbDataOffset);
|
|
|
|
//
|
|
// Since this driver driver did programmaged I/O then the buffer
|
|
// needs to flushed if this an data-in transfer.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {
|
|
|
|
KeFlushIoBuffers(irp->MdlAddress,
|
|
TRUE,
|
|
FALSE);
|
|
}
|
|
|
|
if (SrbData->Flags & SRB_DATA_RESERVED_PAGES) {
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
|
|
IdeUnmapReservedMapping(DeviceExtension, SrbData, irp->MdlAddress);
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef LOG_GET_NEXT_CALLER
|
|
IdeLogCompletedCommand(DeviceExtension, srb);
|
|
#endif
|
|
|
|
//
|
|
// Clear the current request.
|
|
//
|
|
|
|
SrbData->CurrentSrb = NULL;
|
|
|
|
//
|
|
// If the no diconnect flag was set for this SRB, then check to see
|
|
// if IoStartNextPacket must be called.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) {
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags strcuture.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// Set the disconnect running flag and check the busy flag.
|
|
//
|
|
|
|
DeviceExtension->Flags |= PD_DISCONNECT_RUNNING;
|
|
|
|
//
|
|
// The interrupt flags are checked unsynchonized. This works because
|
|
// the RESET_HOLD flag is cleared with the spinlock held and the
|
|
// counter is only set with the spinlock held. So the only case where
|
|
// there is a problem is is a reset occurs before this code get run,
|
|
// but this code runs before the timer is set for a reset hold;
|
|
// the timer will soon set for the new value.
|
|
//
|
|
|
|
if (!(DeviceExtension->InterruptData.InterruptFlags & PD_RESET_HOLD)) {
|
|
|
|
//
|
|
// The miniport is ready for the next request and there is not a
|
|
// pending reset hold, so clear the port timer.
|
|
//
|
|
|
|
DeviceExtension->PortTimeoutCounter = PD_TIMER_STOPPED;
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
if (!(DeviceExtension->Flags & PD_DEVICE_IS_BUSY) &&
|
|
!*CallStartIo &&
|
|
!(DeviceExtension->Flags & PD_PENDING_DEVICE_REQUEST)) {
|
|
|
|
//
|
|
// The busy flag is clear so the miniport has requested the
|
|
// next request. Call IoStartNextPacket.
|
|
//
|
|
|
|
IoStartNextPacket(DeviceExtension->DeviceObject, FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if scatter/gather list came from pool.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_SGLIST_FROM_POOL) {
|
|
|
|
CLRMASK (srb->SrbFlags, SRB_FLAGS_SGLIST_FROM_POOL);
|
|
}
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure,
|
|
// and the free of the srb extension.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// Move bytes transfered to IRP.
|
|
//
|
|
irp->IoStatus.Information = srb->DataTransferLength;
|
|
|
|
//
|
|
// Save the sequence number in case an error needs to be logged later.
|
|
//
|
|
sequenceNumber = SrbData->SequenceNumber;
|
|
SrbData->SequenceNumber = 0;
|
|
SrbData->ErrorLogRetryCount = 0;
|
|
|
|
#if DBG
|
|
SrbData = NULL;
|
|
#endif
|
|
|
|
if (DeviceExtension->Flags & PD_PENDING_DEVICE_REQUEST) {
|
|
|
|
//
|
|
// The start I/O routine needs to be called because it could not
|
|
// allocate an srb extension. Clear the pending flag and note
|
|
// that it needs to be called later.
|
|
//
|
|
|
|
CLRMASK (DeviceExtension->Flags, PD_PENDING_DEVICE_REQUEST);
|
|
*CallStartIo = TRUE;
|
|
}
|
|
|
|
//
|
|
// If success then start next packet.
|
|
// Not starting packet effectively
|
|
// freezes the queue.
|
|
//
|
|
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) {
|
|
|
|
ULONG srbFlags;
|
|
#if DBG
|
|
PVOID tag = irp;
|
|
#endif
|
|
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// save the srbFlags for later user
|
|
//
|
|
srbFlags = srb->SrbFlags;
|
|
|
|
|
|
if (srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) {
|
|
|
|
//
|
|
// must complete power irp before starting a new request
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
tag
|
|
);
|
|
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
irp = NULL;
|
|
|
|
|
|
//
|
|
// we had a device state transition...restart the lu queue
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock);
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the queue is being bypassed then keep the queue frozen.
|
|
// If there are outstanding requests as indicated by the timer
|
|
// being active then don't start the then next request.
|
|
//
|
|
if (!(srbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) &&
|
|
logicalUnit->RequestTimeoutCounter == PD_TIMER_STOPPED) {
|
|
|
|
//
|
|
// This is a normal request start the next packet.
|
|
//
|
|
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
}
|
|
}
|
|
|
|
DebugPrint((2,
|
|
"IdeProcessCompletedRequests: Iocompletion IRP %lx\n",
|
|
irp));
|
|
|
|
//
|
|
// Note that the retry count and sequence number are not cleared
|
|
// for completed packets which were generated by the port driver.
|
|
//
|
|
if (irp) {
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
tag
|
|
);
|
|
|
|
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Set IRP status. Class drivers will reset IRP status based
|
|
// on request sense if error.
|
|
//
|
|
|
|
irp->IoStatus.Status = IdeTranslateSrbStatus(srb);
|
|
|
|
DebugPrint((2, "IdeProcessCompletedRequests: Queue frozen TID %d\n",
|
|
srb->TargetId));
|
|
|
|
if ((srb->SrbStatus == SRB_STATUS_TIMEOUT) ||
|
|
(srb->SrbStatus == SRB_STATUS_BUS_RESET)) {
|
|
|
|
if (SRB_USES_DMA(srb)) {
|
|
|
|
ULONG errorCount;
|
|
|
|
//
|
|
// retry with PIO
|
|
//
|
|
DebugPrint ((DBG_ALWAYS, "ATAPI: retrying dma srb 0x%x with pio\n", srb));
|
|
|
|
MARK_SRB_AS_PIO_CANDIDATE(srb);
|
|
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
srb->ScsiStatus = 0;
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) {
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// iostart the fdo
|
|
//
|
|
IoStartPacket(DeviceExtension->DeviceObject, irp, (PULONG)NULL, NULL);
|
|
|
|
} else {
|
|
|
|
KeInsertByKeyDeviceQueue(&logicalUnit->DeviceObject->DeviceQueue,
|
|
&irp->Tail.Overlay.DeviceQueueEntry,
|
|
srb->QueueSortKey);
|
|
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
}
|
|
|
|
//
|
|
// spinlock is released.
|
|
//
|
|
|
|
//
|
|
// we got an error using DMA
|
|
//
|
|
errorCount = InterlockedIncrement(&logicalUnit->DmaTransferTimeoutCount);
|
|
|
|
if (errorCount == PDO_DMA_TIMEOUT_LIMIT) {
|
|
|
|
ERROR_LOG_ENTRY errorLogEntry;
|
|
ULONG i;
|
|
|
|
//
|
|
// Timeout errors need not be device specific. So no need to
|
|
// update the hall of shame
|
|
//
|
|
errorLogEntry.ErrorCode = SP_PROTOCOL_ERROR;
|
|
errorLogEntry.MajorFunctionCode = IRP_MJ_SCSI;
|
|
errorLogEntry.PathId = srb->PathId;
|
|
errorLogEntry.TargetId = srb->TargetId;
|
|
errorLogEntry.Lun = srb->Lun;
|
|
errorLogEntry.UniqueId = ERRLOGID_TOO_MANY_DMA_TIMEOUT;
|
|
errorLogEntry.ErrorLogRetryCount = errorCount;
|
|
errorLogEntry.SequenceNumber = 0;
|
|
|
|
LogErrorEntry(
|
|
DeviceExtension,
|
|
&errorLogEntry
|
|
);
|
|
|
|
//
|
|
// disable DMA
|
|
//
|
|
hwDeviceExtension->DeviceParameters[srb->TargetId].TransferModeMask |= DMA_SUPPORT;
|
|
|
|
DebugPrint ((DBG_ALWAYS,
|
|
"ATAPI ERROR: 0x%x target %d has too many DMA timeout, falling back to PIO\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
srb->TargetId
|
|
));
|
|
|
|
//
|
|
// rescan the bus to update transfer mode
|
|
//
|
|
#if defined (BUS_CHECK_ON_DMA_ERROR)
|
|
|
|
if (!(logicalUnit->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
IoInvalidateDeviceRelations (
|
|
DeviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
}
|
|
#endif // BUS_CHECK_ON_DMA_ERROR
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
if ((!TestForEnumProbing(srb)) &&
|
|
(srb->Function != SRB_FUNCTION_ATA_POWER_PASS_THROUGH) &&
|
|
(srb->Function != SRB_FUNCTION_ATA_PASS_THROUGH_EX) &&
|
|
(srb->Function != SRB_FUNCTION_ATA_PASS_THROUGH)) {
|
|
|
|
ULONG errorCount;
|
|
ULONG errorCountLimit;
|
|
|
|
//
|
|
// Check if were trying the flush the device cache
|
|
//
|
|
if ((srb->Function == SRB_FUNCTION_FLUSH) ||
|
|
(srb->Function == SRB_FUNCTION_SHUTDOWN) ||
|
|
(srb->Cdb[0] == SCSIOP_SYNCHRONIZE_CACHE)) {
|
|
|
|
errorCount = InterlockedIncrement(&logicalUnit->FlushCacheTimeoutCount);
|
|
|
|
DebugPrint((1,
|
|
"FlushCacheTimeout incremented to 0x%x\n",
|
|
errorCount
|
|
));
|
|
|
|
//
|
|
// Disable flush on IDE devices
|
|
//
|
|
if (errorCount >= PDO_FLUSH_TIMEOUT_LIMIT ) {
|
|
hwDeviceExtension->
|
|
DeviceParameters[srb->TargetId].IdeFlushCommand = IDE_COMMAND_NO_FLUSH;
|
|
#ifdef ENABLE_48BIT_LBA
|
|
hwDeviceExtension->
|
|
DeviceParameters[srb->TargetId].IdeFlushCommandExt = IDE_COMMAND_NO_FLUSH;
|
|
#endif
|
|
}
|
|
|
|
ASSERT (errorCount <= PDO_FLUSH_TIMEOUT_LIMIT);
|
|
|
|
//
|
|
// looks like the device doesn't support flush cache
|
|
//
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
errorCount = InterlockedIncrement(&logicalUnit->ConsecutiveTimeoutCount);
|
|
|
|
DebugPrint ((DBG_ALWAYS, "0x%x target %d has 0x%x timeout errors so far\n",
|
|
logicalUnit->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
logicalUnit->TargetId,
|
|
errorCount));
|
|
|
|
if ((errorCount == PDO_CONSECUTIVE_TIMEOUT_WARNING_LIMIT) &&
|
|
!(logicalUnit->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
|
|
//
|
|
// the device not looking good
|
|
// make sure it is still there
|
|
//
|
|
IoInvalidateDeviceRelations (
|
|
DeviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
}
|
|
|
|
if (logicalUnit->PagingPathCount) {
|
|
|
|
errorCountLimit = PDO_CONSECUTIVE_PAGING_TIMEOUT_LIMIT;
|
|
|
|
} else {
|
|
|
|
errorCountLimit = PDO_CONSECUTIVE_TIMEOUT_LIMIT;
|
|
}
|
|
|
|
if (errorCount >= errorCountLimit) {
|
|
|
|
DebugPrint ((DBG_ALWAYS, "0x%x target %d has too many timeout. it is a goner...\n",
|
|
logicalUnit->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
logicalUnit->TargetId));
|
|
|
|
|
|
//
|
|
// looks like the device is dead.
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&logicalUnit->PdoSpinLock);
|
|
|
|
SETMASK (logicalUnit->PdoState, PDOS_DEADMEAT);
|
|
|
|
IdeLogDeadMeatReason( logicalUnit->DeadmeatRecord.Reason,
|
|
tooManyTimeout
|
|
);
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&logicalUnit->PdoSpinLock);
|
|
|
|
if (!(logicalUnit->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
IoInvalidateDeviceRelations (
|
|
DeviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// reset error count
|
|
//
|
|
InterlockedExchange(&logicalUnit->ConsecutiveTimeoutCount, 0);
|
|
}
|
|
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_PARITY_ERROR) {
|
|
|
|
ULONG errorCount;
|
|
errorCount = InterlockedIncrement(&logicalUnit->CrcErrorCount);
|
|
if (errorCount == PDO_UDMA_CRC_ERROR_LIMIT) {
|
|
|
|
ERROR_LOG_ENTRY errorLogEntry;
|
|
ULONG xferMode;
|
|
|
|
errorLogEntry.ErrorCode = SP_BUS_PARITY_ERROR;
|
|
errorLogEntry.MajorFunctionCode = IRP_MJ_SCSI;
|
|
errorLogEntry.PathId = srb->PathId;
|
|
errorLogEntry.TargetId = srb->TargetId;
|
|
errorLogEntry.Lun = srb->Lun;
|
|
errorLogEntry.UniqueId = ERRLOGID_TOO_MANY_CRC_ERROR;
|
|
errorLogEntry.ErrorLogRetryCount = errorCount;
|
|
errorLogEntry.SequenceNumber = 0;
|
|
|
|
LogErrorEntry(
|
|
DeviceExtension,
|
|
&errorLogEntry
|
|
);
|
|
|
|
//
|
|
//Procure the selected transfer mode again.
|
|
//
|
|
GetHighestDMATransferMode(hwDeviceExtension->DeviceParameters[srb->TargetId].TransferModeSelected,
|
|
xferMode);
|
|
|
|
//
|
|
//Gradual degradation.
|
|
//
|
|
if (xferMode > UDMA0) {
|
|
|
|
hwDeviceExtension->DeviceParameters[srb->TargetId].TransferModeMask |= (1 << xferMode);
|
|
|
|
} else if (xferMode == UDMA0) {
|
|
|
|
// Don't use MWDMA and SWDMA
|
|
hwDeviceExtension->DeviceParameters[srb->TargetId].TransferModeMask |= DMA_SUPPORT;
|
|
|
|
}
|
|
|
|
DebugPrint ((DBG_ALWAYS,
|
|
"ATAPI ERROR: 0x%x target %d has too many crc error, degrading to a lower DMA mode\n",
|
|
DeviceExtension->IdeResource.TranslatedCommandBaseAddress,
|
|
srb->TargetId
|
|
));
|
|
|
|
//
|
|
// rescan the bus to update transfer mode
|
|
//
|
|
if (!(logicalUnit->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
IoInvalidateDeviceRelations (
|
|
DeviceExtension->AttacheePdo,
|
|
BusRelations
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ((srb->ScsiStatus == SCSISTAT_BUSY ||
|
|
srb->SrbStatus == SRB_STATUS_BUSY ||
|
|
srb->ScsiStatus == SCSISTAT_QUEUE_FULL) &&
|
|
!(srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)) {
|
|
|
|
//
|
|
// Perform busy processing if a busy type status was returned and this
|
|
// is not a by-pass request.
|
|
//
|
|
|
|
DebugPrint((1,
|
|
"SCSIPORT: Busy SRB status %x, SCSI status %x)\n",
|
|
srb->SrbStatus,
|
|
srb->ScsiStatus));
|
|
|
|
//
|
|
// If there is already a pending busy request or the queue is frozen
|
|
// then just requeue this request.
|
|
//
|
|
|
|
if (logicalUnit->LuFlags & (PD_LOGICAL_UNIT_IS_BUSY | PD_QUEUE_FROZEN)) {
|
|
|
|
DebugPrint((1,
|
|
"IdeProcessCompletedRequest: Requeuing busy request\n"));
|
|
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
srb->ScsiStatus = 0;
|
|
|
|
if (!KeInsertByKeyDeviceQueue(&logicalUnit->DeviceObject->DeviceQueue,
|
|
&irp->Tail.Overlay.DeviceQueueEntry,
|
|
srb->QueueSortKey)) {
|
|
|
|
//
|
|
// This should never occur since there is a busy request.
|
|
//
|
|
|
|
srb->SrbStatus = SRB_STATUS_ERROR;
|
|
srb->ScsiStatus = SCSISTAT_BUSY;
|
|
|
|
ASSERT(FALSE);
|
|
goto BusyError;
|
|
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
} else if (logicalUnit->RetryCount++ < BUSY_RETRY_COUNT) {
|
|
|
|
//
|
|
// If busy status is returned, then indicate that the logical
|
|
// unit is busy. The timeout code will restart the request
|
|
// when it fires. Reset the status to pending.
|
|
//
|
|
|
|
srb->SrbStatus = SRB_STATUS_PENDING;
|
|
srb->ScsiStatus = 0;
|
|
|
|
logicalUnit->LuFlags |= PD_LOGICAL_UNIT_IS_BUSY;
|
|
logicalUnit->BusyRequest = irp;
|
|
|
|
if (logicalUnit->RetryCount == (BUSY_RETRY_COUNT/2) ) {
|
|
|
|
RESET_CONTEXT resetContext;
|
|
|
|
DebugPrint ((0,
|
|
"ATAPI: PDO 0x%x 0x%x seems to be DEAD. try a reset to bring it back.\n",
|
|
logicalUnit, logicalUnit->ParentDeviceExtension->IdeResource.TranslatedCommandBaseAddress
|
|
));
|
|
|
|
resetContext.DeviceExtension = DeviceExtension;
|
|
resetContext.PathId = srb->PathId;
|
|
resetContext.NewResetSequence = TRUE;
|
|
resetContext.ResetSrb = NULL;
|
|
|
|
KeSynchronizeExecution(DeviceExtension->InterruptObject,
|
|
IdeResetBusSynchronized,
|
|
&resetContext);
|
|
|
|
#if DBG
|
|
IdeDebugHungControllerCounter = 0;
|
|
#endif // DBG
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
} else {
|
|
|
|
BusyError:
|
|
//
|
|
// Indicate the queue is frozen.
|
|
//
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE)) {
|
|
srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
|
|
logicalUnit->LuFlags |= PD_QUEUE_FROZEN;
|
|
}
|
|
|
|
//#if DBG
|
|
// if (logicalUnit->PdoState & PDOS_DEADMEAT) {
|
|
// DbgBreakPoint();
|
|
// }
|
|
//#endif
|
|
|
|
//
|
|
// Release the spinlock. Start the next request.
|
|
//
|
|
if (!(srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) &&
|
|
logicalUnit->RequestTimeoutCounter == PD_TIMER_STOPPED) {
|
|
|
|
//
|
|
// This is a normal request start the next packet.
|
|
//
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
}
|
|
|
|
if (!TestForEnumProbing(srb)) {
|
|
|
|
//
|
|
// Log an a timeout erorr if we are not probing during bus-renum.
|
|
//
|
|
|
|
errorLogPacket = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(DeviceExtension->DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET) + 4 * sizeof(ULONG));
|
|
|
|
if (errorLogPacket != NULL) {
|
|
errorLogPacket->ErrorCode = IO_ERR_NOT_READY;
|
|
errorLogPacket->SequenceNumber = sequenceNumber;
|
|
errorLogPacket->MajorFunctionCode =
|
|
IoGetCurrentIrpStackLocation(irp)->MajorFunction;
|
|
errorLogPacket->RetryCount = logicalUnit->RetryCount;
|
|
errorLogPacket->UniqueErrorValue = 259;
|
|
errorLogPacket->FinalStatus = STATUS_DEVICE_NOT_READY;
|
|
errorLogPacket->DumpDataSize = 5 * sizeof(ULONG);
|
|
errorLogPacket->DumpData[0] = srb->PathId;
|
|
errorLogPacket->DumpData[1] = srb->TargetId;
|
|
errorLogPacket->DumpData[2] = srb->Lun;
|
|
errorLogPacket->DumpData[3] = srb->ScsiStatus;
|
|
errorLogPacket->DumpData[4] = SP_REQUEST_TIMEOUT;
|
|
|
|
|
|
IoWriteErrorLogEntry(errorLogPacket);
|
|
}
|
|
}
|
|
|
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
irp
|
|
);
|
|
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the request sense data is valid, or none is needed and this request
|
|
// is not going to freeze the queue, then start the next request for this
|
|
// logical unit if it is idle.
|
|
//
|
|
|
|
if (!NEED_REQUEST_SENSE(srb) && srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE) {
|
|
|
|
if (logicalUnit->RequestTimeoutCounter == PD_TIMER_STOPPED) {
|
|
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
|
|
//
|
|
// The spinlock is released by GetNextLuRequest.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// NOTE: This will also freeze the queue. For a case where there
|
|
// is no request sense.
|
|
//
|
|
|
|
// if (srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE) {
|
|
// DebugPrint ((DBG_ALWAYS, "BAD BAD BAD: Freezing queue even with a no_queue_freeze request srb = 0x%x\n", srb));
|
|
// }
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE)) {
|
|
srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
|
|
logicalUnit->LuFlags |= PD_QUEUE_FROZEN;
|
|
}
|
|
|
|
//#if DBG
|
|
// if (logicalUnit->PdoState & PDOS_DEADMEAT) {
|
|
// DbgBreakPoint();
|
|
// }
|
|
//#endif
|
|
|
|
//
|
|
// Determine if a REQUEST SENSE command needs to be done.
|
|
// Check that a CHECK_CONDITION was received, an autosense has not
|
|
// been done already, and that autosense has been requested.
|
|
//
|
|
|
|
if (NEED_REQUEST_SENSE(srb)) {
|
|
|
|
srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN;
|
|
logicalUnit->LuFlags |= PD_QUEUE_FROZEN;
|
|
|
|
//
|
|
// If a request sense is going to be issued then any busy
|
|
// requests must be requeue so that the time out routine does
|
|
// not restart them while the request sense is being executed.
|
|
//
|
|
|
|
if (logicalUnit->LuFlags & PD_LOGICAL_UNIT_IS_BUSY) {
|
|
|
|
DebugPrint((1, "IdeProcessCompletedRequest: Requeueing busy request to allow request sense.\n"));
|
|
|
|
if (!KeInsertByKeyDeviceQueue(
|
|
&logicalUnit->DeviceObject->DeviceQueue,
|
|
&logicalUnit->BusyRequest->Tail.Overlay.DeviceQueueEntry,
|
|
srb->QueueSortKey)) {
|
|
|
|
//
|
|
// This should never occur since there is a busy request.
|
|
// Complete the current request without request sense
|
|
// informaiton.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
DebugPrint((3, "IdeProcessCompletedRequests: Iocompletion IRP %lx\n", irp ));
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
irp
|
|
);
|
|
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Clear the busy flag.
|
|
//
|
|
|
|
CLRMASK (logicalUnit->LuFlags, PD_LOGICAL_UNIT_IS_BUSY | PD_QUEUE_IS_FULL);
|
|
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
|
|
//
|
|
// Call IssueRequestSense and it will complete the request
|
|
// after the REQUEST SENSE completes.
|
|
//
|
|
|
|
IssueRequestSense(logicalUnit, srb);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock);
|
|
}
|
|
|
|
//
|
|
// Decrement the logUnitExtension reference count
|
|
//
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
irp
|
|
);
|
|
|
|
|
|
IoCompleteRequest(irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
PSRB_DATA
|
|
IdeGetSrbData(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the SRB data for the addressed unit.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension.
|
|
|
|
Srb - Supplies the scsi request block
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the SRB data. NULL is returned if the address is not
|
|
valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
|
|
irp = Srb->OriginalRequest;
|
|
if (irp == NULL) {
|
|
return NULL;
|
|
}
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
logicalUnit = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
|
|
if (logicalUnit == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &logicalUnit->SrbData;
|
|
}
|
|
|
|
VOID
|
|
IdeCompleteRequest(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSRB_DATA SrbData,
|
|
IN UCHAR SrbStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine completes the specified request.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension.
|
|
|
|
SrbData - Supplies a pointer to the SrbData for the request to be
|
|
completed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
//
|
|
// Make sure there is a current request.
|
|
//
|
|
|
|
ASSERT(SrbData->CurrentSrb);
|
|
srb = SrbData->CurrentSrb;
|
|
|
|
if (srb == NULL || !(srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update SRB status.
|
|
//
|
|
|
|
srb->SrbStatus = SrbStatus;
|
|
|
|
//
|
|
// Indicate no bytes transferred.
|
|
//
|
|
if (!SRB_USES_DMA(srb)) {
|
|
|
|
srb->DataTransferLength = 0;
|
|
|
|
} else {
|
|
|
|
// if we are doing DMA, preserve DataTransferLength.
|
|
// so retry will know how many bytes to transfer
|
|
}
|
|
|
|
//
|
|
// Call notification routine.
|
|
//
|
|
|
|
IdePortNotification(IdeRequestComplete,
|
|
(PVOID)(DeviceExtension + 1),
|
|
srb);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeSendMiniPortIoctl(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP RequestIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sends a miniport ioctl to the miniport driver.
|
|
It creates an srb which is processed normally by the port driver.
|
|
This call is synchronous.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer the SCSI adapter device extension.
|
|
|
|
RequestIrp - Supplies a pointe to the Irp which made the original request.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or failure of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSRB_IO_CONTROL srbControl;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
KEVENT event;
|
|
LARGE_INTEGER startingOffset;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
ULONG outputLength;
|
|
ULONG length;
|
|
ULONG target;
|
|
IDE_PATH_ID pathId;
|
|
|
|
PAGED_CODE();
|
|
startingOffset.QuadPart = (LONGLONG) 1;
|
|
|
|
|
|
DebugPrint((3,"IdeSendMiniPortIoctl: Enter routine\n"));
|
|
|
|
//
|
|
// Get a pointer to the control block.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(RequestIrp);
|
|
srbControl = RequestIrp->AssociatedIrp.SystemBuffer;
|
|
RequestIrp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// check for kernel mode
|
|
//
|
|
if (RequestIrp->RequestorMode != KernelMode) {
|
|
RequestIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Validiate the user buffer.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SRB_IO_CONTROL)) {
|
|
|
|
RequestIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (srbControl->HeaderLength != sizeof(SRB_IO_CONTROL)) {
|
|
RequestIrp->IoStatus.Status = STATUS_REVISION_MISMATCH;
|
|
return(STATUS_REVISION_MISMATCH);
|
|
}
|
|
|
|
length = srbControl->HeaderLength + srbControl->Length;
|
|
if ((length < srbControl->HeaderLength) ||
|
|
(length < srbControl->Length)) {
|
|
|
|
//
|
|
// total length overflows a ULONG
|
|
//
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length &&
|
|
irpStack->Parameters.DeviceIoControl.InputBufferLength < length ) {
|
|
|
|
RequestIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
return(STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Set the logical unit addressing to the first logical unit. This is
|
|
// merely used for addressing purposes.
|
|
//
|
|
pathId.l = 0;
|
|
while (logicalUnit = NextLogUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
&pathId,
|
|
FALSE,
|
|
RequestIrp
|
|
)) {
|
|
|
|
//
|
|
// Walk the logical unit list to the end, looking for a safe one.
|
|
// If it was created for a rescan, it might be freed before this request is
|
|
// complete.
|
|
//
|
|
|
|
if (!(logicalUnit->LuFlags & PD_RESCAN_ACTIVE)) {
|
|
|
|
//
|
|
// Found a good one!
|
|
//
|
|
break;
|
|
}
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
RequestIrp
|
|
);
|
|
}
|
|
|
|
if (logicalUnit == NULL) {
|
|
RequestIrp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
//
|
|
// Initialize the notification event.
|
|
//
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
// Note we do this synchronously for two reasons. If it was done
|
|
// asynchonously then the completion code would have to make a special
|
|
// check to deallocate the buffer. Second if a completion routine were
|
|
// used then an additional IRP stack location would be needed.
|
|
//
|
|
|
|
irp = IoBuildSynchronousFsdRequest(
|
|
IRP_MJ_SCSI,
|
|
logicalUnit->DeviceObject,
|
|
srbControl,
|
|
length,
|
|
&startingOffset,
|
|
&event,
|
|
&ioStatusBlock);
|
|
|
|
if (irp==NULL) {
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
logicalUnit->TargetId,
|
|
NonPagedPool,
|
|
IoSizeOfIrp(logicalUnit->DeviceObject->StackSize),
|
|
IDEPORT_TAG_MPIOCTL_IRP
|
|
);
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
RequestIrp
|
|
);
|
|
|
|
RequestIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return RequestIrp->IoStatus.Status;
|
|
}
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Set major and minor codes.
|
|
//
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
irpStack->Parameters.Others.Argument1 = &srb;
|
|
|
|
//
|
|
// Zero out the srb.
|
|
//
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
srb.PathId = logicalUnit->PathId;
|
|
srb.TargetId = logicalUnit->TargetId;
|
|
srb.Lun = logicalUnit->Lun;
|
|
|
|
srb.Function = SRB_FUNCTION_IO_CONTROL;
|
|
srb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_NO_QUEUE_FREEZE;
|
|
|
|
srb.OriginalRequest = irp;
|
|
|
|
//
|
|
// Set timeout to requested value.
|
|
//
|
|
|
|
srb.TimeOutValue = srbControl->Timeout;
|
|
|
|
//
|
|
// Set the data buffer.
|
|
//
|
|
|
|
srb.DataBuffer = srbControl;
|
|
srb.DataTransferLength = length;
|
|
|
|
//
|
|
// Flush the data buffer for output. This will insure that the data is
|
|
// written back to memory. Since the data-in flag is the the port driver
|
|
// will flush the data again for input which will ensure the data is not
|
|
// in the cache.
|
|
//
|
|
|
|
KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
|
|
|
|
//
|
|
// Call port driver to handle this request.
|
|
//
|
|
|
|
IoCallDriver(logicalUnit->DeviceObject, irp);
|
|
|
|
//
|
|
// Wait for request to complete.
|
|
//
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
//
|
|
// Set the information length to the smaller of the output buffer length
|
|
// and the length returned in the srb.
|
|
//
|
|
|
|
RequestIrp->IoStatus.Information = srb.DataTransferLength > outputLength ?
|
|
outputLength : srb.DataTransferLength;
|
|
|
|
RequestIrp->IoStatus.Status = ioStatusBlock.Status;
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
RequestIrp
|
|
);
|
|
|
|
return RequestIrp->IoStatus.Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeGetInquiryData(
|
|
IN PFDO_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;
|
|
PSCSI_ADAPTER_BUS_INFO adapterInfo;
|
|
PSCSI_BUS_DATA busData;
|
|
PSCSI_INQUIRY_DATA inquiryData;
|
|
ULONG inquiryDataSize;
|
|
ULONG length;
|
|
ULONG numberOfBuses;
|
|
ULONG numberOfLus;
|
|
ULONG j;
|
|
PLOGICAL_UNIT_EXTENSION logUnitExtension;
|
|
IDE_PATH_ID pathId;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((3,"IdeGetInquiryData: Enter routine\n"));
|
|
|
|
//
|
|
// Get a pointer to the control block.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
bufferStart = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
numberOfBuses = MAX_IDE_BUS;
|
|
|
|
// this number could be changing...
|
|
// but we would always fill in the right info for the numLus.
|
|
numberOfLus = DeviceExtension->NumberOfLogicalUnits;
|
|
|
|
//
|
|
// 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;
|
|
return(STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// zero out the buffer so that we don't return uninitialized
|
|
// memory
|
|
//
|
|
RtlZeroMemory(bufferStart, length);
|
|
|
|
//
|
|
// 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 (j = 0; j < numberOfBuses; j++) {
|
|
|
|
busData = &adapterInfo->BusData[j];
|
|
busData->NumberOfLogicalUnits = 0;
|
|
busData->InitiatorBusId = IDE_PSUEDO_INITIATOR_ID;
|
|
|
|
//
|
|
// Copy the data for the logical units.
|
|
//
|
|
busData->InquiryDataOffset = (ULONG)((PUCHAR) inquiryData - bufferStart);
|
|
|
|
pathId.l = 0;
|
|
pathId.b.Path = j;
|
|
while (logUnitExtension = NextLogUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
&pathId,
|
|
TRUE,
|
|
Irp
|
|
)) {
|
|
|
|
INQUIRYDATA InquiryData;
|
|
NTSTATUS status;
|
|
|
|
if (pathId.b.Path != j) {
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
logUnitExtension,
|
|
Irp
|
|
);
|
|
break;
|
|
}
|
|
|
|
inquiryData->PathId = logUnitExtension->PathId;
|
|
inquiryData->TargetId = logUnitExtension->TargetId;
|
|
inquiryData->Lun = logUnitExtension->Lun;
|
|
inquiryData->DeviceClaimed = (BOOLEAN) (logUnitExtension->PdoState & PDOS_DEVICE_CLIAMED);
|
|
inquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE;
|
|
inquiryData->NextInquiryDataOffset = (ULONG)((PUCHAR) inquiryData +
|
|
inquiryDataSize - bufferStart);
|
|
|
|
status = IssueInquirySafe(logUnitExtension->ParentDeviceExtension, logUnitExtension, &InquiryData, FALSE);
|
|
|
|
if (NT_SUCCESS(status) || (status == STATUS_DATA_OVERRUN)) {
|
|
|
|
RtlCopyMemory(
|
|
inquiryData->InquiryData,
|
|
&InquiryData,
|
|
INQUIRYDATABUFFERSIZE
|
|
);
|
|
}
|
|
|
|
inquiryData = (PSCSI_INQUIRY_DATA) ((PCHAR) inquiryData + inquiryDataSize);
|
|
|
|
UnrefLogicalUnitExtensionWithTag (
|
|
DeviceExtension,
|
|
logUnitExtension,
|
|
Irp
|
|
);
|
|
|
|
busData->NumberOfLogicalUnits++;
|
|
|
|
if (busData->NumberOfLogicalUnits >= (UCHAR) numberOfLus) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fix up the last entry of the list.
|
|
//
|
|
|
|
if (busData->NumberOfLogicalUnits == 0) {
|
|
|
|
busData->InquiryDataOffset = 0;
|
|
|
|
} else {
|
|
|
|
((PSCSI_INQUIRY_DATA) ((PCHAR) inquiryData - inquiryDataSize))->
|
|
NextInquiryDataOffset = 0;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeSendScsiPassThrough (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP RequestIrp,
|
|
IN BOOLEAN Direct
|
|
)
|
|
/*++
|
|
|
|
Routine Description;
|
|
|
|
Validates the scsi pass through structure and invokes
|
|
IdeSendValidScsiPassThru to service the request.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension : Fdo Extension.
|
|
RequestIrp : The irp containing the scsi pass through request
|
|
|
|
Return Value:
|
|
|
|
The irp's io status
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status;
|
|
UCHAR pathId;
|
|
UCHAR targetId;
|
|
UCHAR lun;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = PortGetPassThroughAddress (RequestIrp,
|
|
&pathId,
|
|
&targetId,
|
|
&lun
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
//
|
|
// If this request came through a normal device control rather than from
|
|
// class driver then the device must exist and be unclaimed. Class drivers
|
|
// will set the minor function code for the device control. It is always
|
|
// zero for a user request.
|
|
//
|
|
logicalUnit = RefLogicalUnitExtensionWithTag(DeviceExtension,
|
|
pathId,
|
|
targetId,
|
|
lun,
|
|
FALSE,
|
|
RequestIrp
|
|
);
|
|
|
|
if (logicalUnit) {
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(RequestIrp);
|
|
|
|
if (irpStack->MinorFunction == 0) {
|
|
|
|
if (logicalUnit->PdoState & PDOS_DEVICE_CLIAMED) {
|
|
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
RequestIrp
|
|
);
|
|
|
|
logicalUnit = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (logicalUnit != NULL) {
|
|
|
|
status = PortSendPassThrough( logicalUnit->DeviceObject,
|
|
RequestIrp,
|
|
Direct,
|
|
0,
|
|
&DeviceExtension->Capabilities
|
|
);
|
|
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
logicalUnit,
|
|
RequestIrp
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
SyncAtaPassThroughCompletionRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
PSYNC_ATA_PASSTHROUGH_CONTEXT context = Context;
|
|
|
|
context->Status = Status;
|
|
|
|
KeSetEvent (&context->Event, 0, FALSE);
|
|
|
|
}
|
|
|
|
//
|
|
// <= DISPATCH_LEVEL
|
|
//
|
|
NTSTATUS
|
|
IssueAsyncAtaPassThroughSafe (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogUnitExtension,
|
|
IN OUT PATA_PASS_THROUGH AtaPassThroughData,
|
|
IN BOOLEAN DataIn,
|
|
IN ASYNC_PASS_THROUGH_COMPLETION Completion,
|
|
IN PVOID CallerContext,
|
|
IN BOOLEAN PowerRelated,
|
|
IN ULONG TimeOut,
|
|
IN BOOLEAN MustSucceed
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
KIRQL currentIrql;
|
|
NTSTATUS status;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PSENSE_DATA senseInfoBuffer;
|
|
ULONG totalBufferSize;
|
|
|
|
PATA_PASSTHROUGH_CONTEXT context;
|
|
PENUMERATION_STRUCT enumStruct;
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
senseInfoBuffer = NULL;
|
|
srb = NULL;
|
|
irp = NULL;
|
|
|
|
if (MustSucceed) {
|
|
|
|
enumStruct = DeviceExtension->PreAllocEnumStruct;
|
|
|
|
if (enumStruct == NULL) {
|
|
ASSERT (DeviceExtension->PreAllocEnumStruct);
|
|
|
|
//
|
|
// Fall back to the usual course of action
|
|
//
|
|
MustSucceed=FALSE;
|
|
} else {
|
|
|
|
context = enumStruct->Context;
|
|
|
|
ASSERT (context);
|
|
|
|
senseInfoBuffer = enumStruct->SenseInfoBuffer;
|
|
|
|
ASSERT (senseInfoBuffer);
|
|
|
|
srb = enumStruct->Srb;
|
|
|
|
ASSERT (srb);
|
|
|
|
totalBufferSize = FIELD_OFFSET(ATA_PASS_THROUGH, DataBuffer) + AtaPassThroughData->DataBufferSize;
|
|
|
|
irp = enumStruct->Irp1;
|
|
|
|
ASSERT (irp);
|
|
|
|
//
|
|
// this irp has always a stack size of 1. Use the same
|
|
// stack size when initializing the irp.
|
|
//
|
|
IoInitializeIrp(irp,
|
|
IoSizeOfIrp(PREALLOC_STACK_LOCATIONS),
|
|
PREALLOC_STACK_LOCATIONS);
|
|
|
|
irp->MdlAddress = enumStruct->MdlAddress;
|
|
|
|
ASSERT (enumStruct->DataBufferSize >= totalBufferSize);
|
|
RtlCopyMemory(enumStruct->DataBuffer, AtaPassThroughData, totalBufferSize);
|
|
}
|
|
}
|
|
|
|
if (!MustSucceed) {
|
|
|
|
context = ExAllocatePool(NonPagedPool, sizeof (ATA_PASSTHROUGH_CONTEXT));
|
|
|
|
if (context == NULL) {
|
|
DebugPrint((1,"IssueAsyncAtaPassThrough: Can't allocate context buffer\n"));
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
LogUnitExtension->TargetId,
|
|
NonPagedPool,
|
|
sizeof(ATA_PASSTHROUGH_CONTEXT),
|
|
(IDEPORT_TAG_ATAPASS_CONTEXT+AtaPassThroughData->IdeReg.bCommandReg)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
senseInfoBuffer = ExAllocatePool( NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
|
|
|
|
if (senseInfoBuffer == NULL) {
|
|
DebugPrint((1,"IssueAsyncAtaPassThrough: Can't allocate request sense buffer\n"));
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
LogUnitExtension->TargetId,
|
|
NonPagedPoolCacheAligned,
|
|
SENSE_BUFFER_SIZE,
|
|
(IDEPORT_TAG_ATAPASS_SENSE+AtaPassThroughData->IdeReg.bCommandReg)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
srb = ExAllocatePool (NonPagedPool, sizeof (SCSI_REQUEST_BLOCK));
|
|
if (srb == NULL) {
|
|
DebugPrint((1,"IssueAsyncAtaPassThrough: Can't SRB\n"));
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
LogUnitExtension->TargetId,
|
|
NonPagedPool,
|
|
sizeof(SCSI_REQUEST_BLOCK),
|
|
(IDEPORT_TAG_ATAPASS_SRB+AtaPassThroughData->IdeReg.bCommandReg)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
totalBufferSize = FIELD_OFFSET(ATA_PASS_THROUGH, DataBuffer) + AtaPassThroughData->DataBufferSize;
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
//
|
|
irp = IoAllocateIrp (
|
|
(CCHAR) (LogUnitExtension->DeviceObject->StackSize),
|
|
FALSE
|
|
);
|
|
if (irp == NULL) {
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
LogUnitExtension->TargetId,
|
|
NonPagedPool,
|
|
IoSizeOfIrp(LogUnitExtension->DeviceObject->StackSize),
|
|
(IDEPORT_TAG_ATAPASS_IRP+AtaPassThroughData->IdeReg.bCommandReg)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
irp->MdlAddress = IoAllocateMdl( AtaPassThroughData,
|
|
totalBufferSize,
|
|
FALSE,
|
|
FALSE,
|
|
(PIRP) NULL );
|
|
if (irp->MdlAddress == NULL) {
|
|
|
|
IdeLogNoMemoryError(DeviceExtension,
|
|
LogUnitExtension->TargetId,
|
|
NonPagedPool,
|
|
totalBufferSize,
|
|
(IDEPORT_TAG_ATAPASS_MDL+AtaPassThroughData->IdeReg.bCommandReg)
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto GetOut;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool(irp->MdlAddress);
|
|
|
|
}
|
|
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
srb->PathId = LogUnitExtension->PathId;
|
|
srb->TargetId = LogUnitExtension->TargetId;
|
|
srb->Lun = LogUnitExtension->Lun;
|
|
|
|
if (PowerRelated) {
|
|
|
|
srb->Function = SRB_FUNCTION_ATA_POWER_PASS_THROUGH;
|
|
srb->QueueSortKey = MAXULONG;
|
|
} else {
|
|
|
|
srb->Function = SRB_FUNCTION_ATA_PASS_THROUGH;
|
|
srb->QueueSortKey = 0;
|
|
}
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Set flags to disable synchronous negociation.
|
|
//
|
|
srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
srb->SrbFlags |= DataIn ? 0 : SRB_FLAGS_DATA_OUT;
|
|
|
|
if (AtaPassThroughData->IdeReg.bReserved & ATA_PTFLAGS_URGENT) {
|
|
|
|
srb->SrbFlags |= SRB_FLAGS_BYPASS_FROZEN_QUEUE;
|
|
}
|
|
|
|
srb->SrbStatus = srb->ScsiStatus = 0;
|
|
|
|
srb->NextSrb = 0;
|
|
|
|
srb->OriginalRequest = irp;
|
|
|
|
//
|
|
// Set timeout to 15 seconds.
|
|
//
|
|
srb->TimeOutValue = TimeOut;
|
|
|
|
srb->CdbLength = 6;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
srb->SenseInfoBuffer = senseInfoBuffer;
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
srb->DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);
|
|
srb->DataTransferLength = totalBufferSize;
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AtaPassThroughCompletionRoutine,
|
|
context,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
|
|
context->DeviceObject = LogUnitExtension->DeviceObject;
|
|
context->CallerCompletion = Completion;
|
|
context->CallerContext = CallerContext;
|
|
context->SenseInfoBuffer = senseInfoBuffer;
|
|
context->Srb = srb;
|
|
context->MustSucceed = MustSucceed? 1 : 0;
|
|
context->DataBuffer = AtaPassThroughData;
|
|
|
|
//
|
|
// send the pass through irp
|
|
//
|
|
status = IoCallDriver(LogUnitExtension->DeviceObject, irp);
|
|
|
|
//
|
|
// always return STATUS_PENDING when we actually send out the irp
|
|
//
|
|
return STATUS_PENDING;
|
|
|
|
GetOut:
|
|
|
|
ASSERT (!MustSucceed);
|
|
|
|
if (context) {
|
|
|
|
ExFreePool (context);
|
|
}
|
|
|
|
if (senseInfoBuffer) {
|
|
|
|
ExFreePool (senseInfoBuffer);
|
|
}
|
|
|
|
if (srb) {
|
|
|
|
ExFreePool (srb);
|
|
}
|
|
|
|
if (irp && irp->MdlAddress) {
|
|
|
|
IoFreeMdl (irp->MdlAddress);
|
|
}
|
|
|
|
if (irp) {
|
|
|
|
IoFreeIrp( irp );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // IssueAtaPassThrough
|
|
|
|
NTSTATUS
|
|
IssueSyncAtaPassThroughSafe (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PLOGICAL_UNIT_EXTENSION LogUnitExtension,
|
|
IN OUT PATA_PASS_THROUGH AtaPassThroughData,
|
|
IN BOOLEAN DataIn,
|
|
IN BOOLEAN PowerRelated,
|
|
IN ULONG TimeOut,
|
|
IN BOOLEAN MustSucceed
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
SYNC_ATA_PASSTHROUGH_CONTEXT context;
|
|
ULONG retryCount=10;
|
|
ULONG locked;
|
|
|
|
|
|
status=STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (MustSucceed) {
|
|
|
|
//Lock
|
|
ASSERT(InterlockedCompareExchange(&(DeviceExtension->EnumStructLock), 1, 0) == 0);
|
|
|
|
}
|
|
|
|
|
|
while ((status == STATUS_UNSUCCESSFUL || status == STATUS_INSUFFICIENT_RESOURCES) && retryCount--) {
|
|
|
|
//
|
|
// Initialize the notification event.
|
|
//
|
|
|
|
KeInitializeEvent(&context.Event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
status = IssueAsyncAtaPassThroughSafe (
|
|
DeviceExtension,
|
|
LogUnitExtension,
|
|
AtaPassThroughData,
|
|
DataIn,
|
|
SyncAtaPassThroughCompletionRoutine,
|
|
&context,
|
|
PowerRelated,
|
|
TimeOut,
|
|
MustSucceed
|
|
);
|
|
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&context.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
status=context.Status;
|
|
}
|
|
|
|
if (status == STATUS_UNSUCCESSFUL) {
|
|
DebugPrint((1, "Retrying flushed request\n"));
|
|
}
|
|
}
|
|
|
|
if (MustSucceed) {
|
|
//Unlock
|
|
ASSERT(InterlockedCompareExchange(&(DeviceExtension->EnumStructLock), 0, 1) == 1);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
return context.Status;
|
|
|
|
} else {
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
AtaPassThroughCompletionRoutine(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
{
|
|
PATA_PASSTHROUGH_CONTEXT context = Context;
|
|
PATA_PASS_THROUGH ataPassThroughData;
|
|
|
|
|
|
|
|
DebugPrint((1, "AtaPassThroughCompletionRoutine: Irp = 0x%x status=%x\n",
|
|
Irp, Irp->IoStatus.Status));
|
|
|
|
if (context->Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
KIRQL currentIrql;
|
|
|
|
DebugPrint((1, "AtaPassThroughCompletionRoutine: Unfreeze Queue TID %d\n",
|
|
context->Srb->TargetId));
|
|
|
|
logicalUnit = context->DeviceObject->DeviceExtension;
|
|
|
|
ASSERT (logicalUnit);
|
|
CLRMASK (logicalUnit->LuFlags, PD_QUEUE_FROZEN);
|
|
|
|
KeAcquireSpinLock(&logicalUnit->ParentDeviceExtension->SpinLock, ¤tIrql);
|
|
GetNextLuRequest(logicalUnit->ParentDeviceExtension, logicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
}
|
|
|
|
ataPassThroughData = (PATA_PASS_THROUGH) context->Srb->DataBuffer;
|
|
if (ataPassThroughData->IdeReg.bReserved & ATA_PTFLAGS_OK_TO_FAIL) {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (context->MustSucceed) {
|
|
RtlCopyMemory(context->DataBuffer,
|
|
context->Srb->DataBuffer, context->Srb->DataTransferLength);
|
|
DebugPrint((1, "AtaCompletionSafe: Device =%x, Status= %x, SrbStatus=%x\n",
|
|
context->Srb->TargetId, Irp->IoStatus.Status, context->Srb->SrbStatus));
|
|
}
|
|
|
|
if (context->CallerCompletion) {
|
|
|
|
context->CallerCompletion (context->DeviceObject, context->CallerContext, Irp->IoStatus.Status);
|
|
}
|
|
|
|
if (context->MustSucceed) {
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
ExFreePool (context->SenseInfoBuffer);
|
|
ExFreePool (context->Srb);
|
|
ExFreePool (context);
|
|
|
|
if (Irp->MdlAddress) {
|
|
|
|
IoFreeMdl (Irp->MdlAddress);
|
|
}
|
|
|
|
IoFreeIrp (Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeClaimLogicalUnit(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function finds the specified device in the logical unit information
|
|
and either updates the device object point or claims the device. If the
|
|
device is already claimed, then the request fails. If the request succeeds,
|
|
then the current device object is returned in the data buffer pointer
|
|
of the SRB.
|
|
|
|
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 the status of the operation. Either success, no device or busy.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL currentIrql;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PDEVICE_OBJECT saveDevice;
|
|
PPDO_EXTENSION pdoExtension;
|
|
PVOID sectionHandle;
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get SRB address from current IRP stack.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
srb = (PSCSI_REQUEST_BLOCK) irpStack->Parameters.Others.Argument1;
|
|
|
|
pdoExtension = IDEPORT_GET_LUNEXT_IN_IRP (irpStack);
|
|
ASSERT (pdoExtension);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
sectionHandle = MmLockPagableCodeSection(IdeClaimLogicalUnit);
|
|
#endif
|
|
|
|
//
|
|
// Lock the data.
|
|
//
|
|
KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql);
|
|
|
|
if (srb->Function == SRB_FUNCTION_RELEASE_DEVICE) {
|
|
|
|
CLRMASK (pdoExtension->PdoState, PDOS_DEVICE_CLIAMED | PDOS_LEGACY_ATTACHER);
|
|
|
|
KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql);
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Check for a claimed device.
|
|
//
|
|
|
|
if (pdoExtension->PdoState & PDOS_DEVICE_CLIAMED) {
|
|
|
|
KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql);
|
|
srb->SrbStatus = SRB_STATUS_BUSY;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
return(STATUS_DEVICE_BUSY);
|
|
}
|
|
|
|
//
|
|
// Save the current device object.
|
|
//
|
|
|
|
saveDevice = pdoExtension->AttacherDeviceObject;
|
|
|
|
//
|
|
// Update the lun information based on the operation type.
|
|
//
|
|
|
|
if (srb->Function == SRB_FUNCTION_CLAIM_DEVICE) {
|
|
|
|
pdoExtension->PdoState |= PDOS_DEVICE_CLIAMED;
|
|
}
|
|
|
|
if (srb->Function == SRB_FUNCTION_ATTACH_DEVICE) {
|
|
pdoExtension->AttacherDeviceObject = srb->DataBuffer;
|
|
}
|
|
|
|
srb->DataBuffer = saveDevice;
|
|
|
|
if (irpStack->DeviceObject == pdoExtension->ParentDeviceExtension->DeviceObject) {
|
|
|
|
//
|
|
// The original irp is sent to the parent. The attacher must
|
|
// be legacy class driver. We can never do pnp remove safely.
|
|
//
|
|
pdoExtension->PdoState |= PDOS_LEGACY_ATTACHER;
|
|
}
|
|
|
|
KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql);
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
MmUnlockPagableImageSection(sectionHandle);
|
|
#endif
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
IdeRemoveDevice(
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function finds the specified device in the logical unit information
|
|
and deletes it. This is done in preparation for a failing device to be
|
|
physically removed from a SCSI bus. An assumption is that the system
|
|
utility controlling the device removal has locked the volumes so there
|
|
is no outstanding IO to this device.
|
|
|
|
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 the status of the operation. Either success or no device.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL currentIrql;
|
|
PPDO_EXTENSION pdoExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
// ISSUE:2000/02/11 : need to test this
|
|
|
|
//
|
|
// Get SRB address from current IRP stack.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
|
|
srb = (PSCSI_REQUEST_BLOCK) irpStack->Parameters.Others.Argument1;
|
|
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
|
|
pdoExtension = RefLogicalUnitExtensionWithTag(
|
|
DeviceExtension,
|
|
srb->PathId,
|
|
srb->TargetId,
|
|
srb->Lun,
|
|
FALSE,
|
|
IdeRemoveDevice
|
|
);
|
|
if (pdoExtension) {
|
|
|
|
DebugPrint((1, "IdeRemove device removing Pdo %x\n", pdoExtension));
|
|
status = FreePdoWithTag (pdoExtension, TRUE, TRUE, IdeRemoveDevice);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
IdeMiniPortTimerDpc(
|
|
IN struct _KDPC *Dpc,
|
|
IN PVOID DeviceObject,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the miniport when its requested timer fires.
|
|
It interlocks either with the port spinlock and the interrupt object.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Unsed.
|
|
|
|
DeviceObject - Supplies a pointer to the device object for this adapter.
|
|
|
|
SystemArgument1 - Unused.
|
|
|
|
SystemArgument2 - Unused.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDO_EXTENSION deviceExtension = ((PDEVICE_OBJECT) DeviceObject)->DeviceExtension;
|
|
|
|
//
|
|
// Acquire the port spinlock.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Make sure the timer routine is still desired.
|
|
//
|
|
|
|
if (deviceExtension->HwTimerRequest != NULL) {
|
|
|
|
KeSynchronizeExecution (
|
|
deviceExtension->InterruptObject,
|
|
(PKSYNCHRONIZE_ROUTINE) deviceExtension->HwTimerRequest,
|
|
deviceExtension->HwDeviceExtension
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Check for miniport work requests. Note this is an unsynchonized
|
|
// test on a bit that can be set by the interrupt routine; however,
|
|
// the worst that can happen is that the completion DPC checks for work
|
|
// twice.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
//
|
|
// Call the completion DPC directly.
|
|
//
|
|
|
|
IdePortCompletionDpc( NULL,
|
|
deviceExtension->DeviceObject,
|
|
NULL,
|
|
NULL);
|
|
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
IdePortFlushLogicalUnit (
|
|
PFDO_EXTENSION FdoExtension,
|
|
PLOGICAL_UNIT_EXTENSION LogUnitExtension,
|
|
BOOLEAN Forced
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PKDEVICE_QUEUE_ENTRY packet;
|
|
KIRQL currentIrql;
|
|
PIRP nextIrp;
|
|
PIRP listIrp;
|
|
PIRP powerRelatedIrp;
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure and the saved
|
|
// interrupt context.
|
|
//
|
|
|
|
KeAcquireSpinLock(&FdoExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// Make sure the queue is frozen.
|
|
//
|
|
|
|
if ((!(LogUnitExtension->LuFlags & PD_QUEUE_FROZEN)) && (!Forced)) {
|
|
|
|
DebugPrint((1,"IdePortFlushLogicalUnit: Request to flush an unfrozen queue!\n"));
|
|
|
|
KeReleaseSpinLock(&FdoExtension->SpinLock, currentIrql);
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else {
|
|
|
|
listIrp = NULL;
|
|
powerRelatedIrp = NULL;
|
|
|
|
//
|
|
// The queue may not be busy so we have to use the IfBusy variant.
|
|
// Use a zero key to pull items from the head of it (if any are there)
|
|
//
|
|
while ((packet =
|
|
KeRemoveByKeyDeviceQueueIfBusy(
|
|
&(LogUnitExtension->DeviceObject->DeviceQueue),
|
|
0))
|
|
!= NULL) {
|
|
|
|
nextIrp = CONTAINING_RECORD(packet, IRP, Tail.Overlay.DeviceQueueEntry);
|
|
|
|
//
|
|
// Get the srb.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
if (srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) {
|
|
|
|
ASSERT (!powerRelatedIrp);
|
|
powerRelatedIrp = nextIrp;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set the status code.
|
|
//
|
|
|
|
srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// Link the requests. They will be completed after the
|
|
// spinlock is released.
|
|
//
|
|
|
|
nextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)
|
|
listIrp;
|
|
listIrp = nextIrp;
|
|
}
|
|
|
|
|
|
//
|
|
// clear the pending reuqest blocked by busy device
|
|
//
|
|
if ((LogUnitExtension->LuFlags & PD_LOGICAL_UNIT_IS_BUSY) &&
|
|
(LogUnitExtension->BusyRequest)) {
|
|
|
|
nextIrp = LogUnitExtension->BusyRequest;
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
LogUnitExtension->BusyRequest = NULL;
|
|
CLRMASK (LogUnitExtension->LuFlags, PD_LOGICAL_UNIT_IS_BUSY);
|
|
|
|
if (srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) {
|
|
|
|
ASSERT (!powerRelatedIrp);
|
|
powerRelatedIrp = nextIrp;
|
|
|
|
} else {
|
|
|
|
srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
nextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)
|
|
listIrp;
|
|
listIrp = nextIrp;
|
|
}
|
|
}
|
|
|
|
if (LogUnitExtension->PendingRequest) {
|
|
|
|
nextIrp = LogUnitExtension->PendingRequest;
|
|
LogUnitExtension->PendingRequest = NULL;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
srb = irpStack->Parameters.Scsi.Srb;
|
|
|
|
if (srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) {
|
|
|
|
ASSERT (!powerRelatedIrp);
|
|
powerRelatedIrp = nextIrp;
|
|
|
|
} else {
|
|
|
|
srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
nextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)
|
|
listIrp;
|
|
listIrp = nextIrp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the queue as unfrozen. Since all the requests have
|
|
// been removed and the device queue is no longer busy, it
|
|
// is effectively unfrozen.
|
|
//
|
|
|
|
CLRMASK (LogUnitExtension->LuFlags, PD_QUEUE_FROZEN);
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLock(&FdoExtension->SpinLock, currentIrql);
|
|
|
|
if (powerRelatedIrp) {
|
|
|
|
PDEVICE_OBJECT deviceObject = LogUnitExtension->DeviceObject;
|
|
|
|
DebugPrint ((DBG_POWER, "Resending power related pass through reuqest 0x%x\n", powerRelatedIrp));
|
|
|
|
UnrefPdoWithTag(
|
|
LogUnitExtension,
|
|
powerRelatedIrp
|
|
);
|
|
|
|
IdePortDispatch(
|
|
deviceObject,
|
|
powerRelatedIrp
|
|
);
|
|
}
|
|
|
|
//
|
|
// Complete the flushed requests.
|
|
//
|
|
|
|
while (listIrp != NULL) {
|
|
|
|
nextIrp = listIrp;
|
|
listIrp = (PIRP) nextIrp->Tail.Overlay.ListEntry.Flink;
|
|
|
|
UnrefLogicalUnitExtensionWithTag(
|
|
FdoExtension,
|
|
LogUnitExtension,
|
|
nextIrp
|
|
);
|
|
|
|
IoCompleteRequest(nextIrp, 0);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PVOID
|
|
IdeMapLockedPagesWithReservedMapping (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSRB_DATA SrbData,
|
|
IN PMDL Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to map the physical pages represented by the supplied
|
|
MDL using the adapter's reserved page range.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Points to the FDO extension
|
|
|
|
SrbData - Points to SrbData structure for this request
|
|
|
|
Mdl - Points to an MDL that describes the physical range we
|
|
are tring to map.
|
|
|
|
Return Value:
|
|
|
|
Kernel VA of the mapped pages if mapped successfully.
|
|
|
|
NULL if the reserved page range is too small or if the pages are
|
|
not successfully mapped.
|
|
|
|
-1 if the reserved pages are already in use.
|
|
|
|
Notes:
|
|
|
|
This routine is called with the spinlock held.
|
|
|
|
--*/
|
|
{
|
|
ULONG_PTR numberOfPages;
|
|
PVOID startingVa;
|
|
PVOID systemAddress;
|
|
|
|
//
|
|
// Check if the reserve pages are already in use
|
|
//
|
|
if (DeviceExtension->Flags & PD_RESERVED_PAGES_IN_USE) {
|
|
|
|
DebugPrint((1,
|
|
"Reserve pages in use...\n"
|
|
));
|
|
|
|
return (PVOID)-1;
|
|
}
|
|
|
|
startingVa = (PVOID)((PCHAR)Mdl->StartVa + Mdl->ByteOffset);
|
|
numberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(startingVa, Mdl->ByteCount);
|
|
|
|
if (numberOfPages > IDE_NUM_RESERVED_PAGES) {
|
|
|
|
systemAddress = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The reserved range is large enough to map all the pages. Go ahead
|
|
// and try to map them. Since we are specifying MmCached as cache
|
|
// type and we've ensured that we have enough reserved pages to
|
|
// cover the request, this should never fail.
|
|
//
|
|
systemAddress = MmMapLockedPagesWithReservedMapping (DeviceExtension->ReservedPages,
|
|
'PedI',
|
|
Mdl,
|
|
MmCached );
|
|
|
|
if (systemAddress == NULL) {
|
|
|
|
DebugPrint((1,
|
|
"mapping failed....\n"
|
|
));
|
|
|
|
ASSERT(systemAddress);
|
|
|
|
} else {
|
|
|
|
DebugPrint((1,
|
|
"mapping....\n"
|
|
));
|
|
|
|
//
|
|
// We need this flag to verify if the reserved pages are already
|
|
// in use. The per request srbData flag is not available to make
|
|
// this check
|
|
//
|
|
ASSERT(!(DeviceExtension->Flags & PD_RESERVED_PAGES_IN_USE));
|
|
SETMASK(DeviceExtension->Flags, PD_RESERVED_PAGES_IN_USE);
|
|
|
|
|
|
//
|
|
// we need this flag to unmap the pages. The flag in the
|
|
// device extension cannot be relied upon as it might indicate
|
|
// the flags for the next request
|
|
//
|
|
ASSERT(!(SrbData->Flags & SRB_DATA_RESERVED_PAGES));
|
|
SETMASK(SrbData->Flags, SRB_DATA_RESERVED_PAGES);
|
|
|
|
}
|
|
}
|
|
|
|
return systemAddress;
|
|
|
|
}
|
|
|
|
VOID
|
|
IdeUnmapReservedMapping (
|
|
IN PFDO_EXTENSION DeviceExtension,
|
|
IN PSRB_DATA SrbData,
|
|
IN PMDL Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Unmap the physical pages represented by the Mdl
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension: The Fdo extension
|
|
|
|
Mdl: Mdl for the request
|
|
|
|
Return Value:
|
|
|
|
No return value
|
|
|
|
Notes:
|
|
|
|
This routine is called with the spinlock held
|
|
|
|
--*/
|
|
{
|
|
DebugPrint((1,
|
|
"Unmapping....\n"
|
|
));
|
|
|
|
ASSERT(DeviceExtension->Flags & PD_RESERVED_PAGES_IN_USE);
|
|
CLRMASK(DeviceExtension->Flags, PD_RESERVED_PAGES_IN_USE);
|
|
|
|
ASSERT(SrbData->Flags & SRB_DATA_RESERVED_PAGES);
|
|
CLRMASK(SrbData->Flags, SRB_DATA_RESERVED_PAGES);
|
|
|
|
MmUnmapReservedMapping (
|
|
DeviceExtension->ReservedPages,
|
|
'PedI',
|
|
Mdl
|
|
);
|
|
}
|
|
|
|
#ifdef LOG_GET_NEXT_CALLER
|
|
|
|
VOID
|
|
IdeLogCompletedCommand(
|
|
PFDO_EXTENSION FdoExtension,
|
|
PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
ULONG index = FdoExtension->CompletedCommandIndex;
|
|
|
|
RtlCopyMemory(&(FdoExtension->CompletedCommandQueue[index].Srb),
|
|
Srb,
|
|
sizeof(SCSI_REQUEST_BLOCK)
|
|
);
|
|
|
|
FdoExtension->CompletedCommandIndex =
|
|
(FdoExtension->CompletedCommandIndex + 1) % GET_NEXT_LOG_LENGTH;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
VOID
|
|
IdeLogGetNextLuCaller (
|
|
PFDO_EXTENSION FdoExtension,
|
|
PPDO_EXTENSION PdoExtension,
|
|
PUCHAR FileName,
|
|
ULONG LineNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Temporary routine to log the last few GetNextLuRequest caller. This
|
|
routine was added to catch a bug where we fail to process further
|
|
requests on an LU
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG index = FdoExtension->GetNextLuIndex;
|
|
|
|
FdoExtension->GetNextLuCallerLineNumber[index] = LineNumber;
|
|
|
|
strncpy (FdoExtension->GetNextLuCallerFileName[index], FileName, 255);
|
|
|
|
FdoExtension->GetNextLuCallerFlags[index] = PdoExtension->LuFlags;
|
|
|
|
FdoExtension->GetNextLuIndex =
|
|
(FdoExtension->GetNextLuIndex + 1) % GET_NEXT_LOG_LENGTH;
|
|
|
|
return;
|
|
}
|
|
|
|
#endif
|