/*++ 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;kiCdbLength;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;kiCdbLength;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; jHwDeviceExtension->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; cCurrentIrp->MdlAddress) { s = (PUCHAR) deviceObject->CurrentIrp->MdlAddress; d = (PUCHAR) &deviceExtension->debugData[deviceExtension->nextEntry].mdl; deviceExtension->debugDataPtr[deviceExtension->nextEntry].mdl = (PMDL) d; for (c=0; cdebugData[deviceExtension->nextEntry].mdl; deviceExtension->debugDataPtr[deviceExtension->nextEntry].mdl = (PMDL) d; for (c=0; cdebugData[deviceExtension->nextEntry].srb; deviceExtension->debugDataPtr[deviceExtension->nextEntry].srb = (PSCSI_REQUEST_BLOCK) d; for (c=0; cDataBuffer) & 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