VOID ScsiPortLogError( IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN ULONG ErrorCode, IN ULONG UniqueId ) /*++ Routine Description: This routine saves the error log information, and queues a DPC if necessary. Arguments: HwDeviceExtension - Supplies the HBA miniport driver's adapter data storage. Srb - Supplies an optional pointer to srb if there is one. TargetId, Lun and PathId - specify device address on a SCSI bus. ErrorCode - Supplies an error code indicating the type of error. UniqueId - Supplies a unique identifier for the error. Return Value: None. --*/ { PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension); PDEVICE_OBJECT DeviceObject = deviceExtension->CommonExtension.DeviceObject; PSRB_DATA srbData; PERROR_LOG_ENTRY errorLogEntry; // // If the error log entry is already full, then dump the error. // if (deviceExtension->InterruptData.InterruptFlags & PD_LOG_ERROR) { #if SCSIDBG_ENABLED DebugPrint((1,"ScsiPortLogError: Dumping scsi error log packet.\n")); DebugPrint((1, "PathId = %2x, TargetId = %2x, Lun = %2x, ErrorCode = %x, UniqueId = %x.", PathId, TargetId, Lun, ErrorCode, UniqueId )); #endif return; } // // Save the error log data in the log entry. // errorLogEntry = &deviceExtension->InterruptData.LogEntry; errorLogEntry->ErrorCode = ErrorCode; errorLogEntry->TargetId = TargetId; errorLogEntry->Lun = Lun; errorLogEntry->PathId = PathId; errorLogEntry->UniqueId = UniqueId; // // Get the sequence number from the SRB data. // if (Srb != NULL) { srbData = Srb->OriginalRequest; ASSERT_SRB_DATA(srbData); errorLogEntry->SequenceNumber = srbData->SequenceNumber; errorLogEntry->ErrorLogRetryCount = srbData->ErrorLogRetryCount++; } else { errorLogEntry->SequenceNumber = 0; errorLogEntry->ErrorLogRetryCount = 0; } // // Indicate that the error log entry is in use. // deviceExtension->InterruptData.InterruptFlags |= PD_LOG_ERROR; // // Request a DPC be queued after the interrupt completes. // deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED; return; } // end ScsiPortLogError()
VOID ScsiPortIoMapTransfer( IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID LogicalAddress, IN ULONG Length ) /*++ Routine Description: Saves the parameters for the call to IoMapTransfer and schedules the DPC if necessary. Arguments: HwDeviceExtension - Supplies a the hardware device extension for the host bus adapter which will be doing the data transfer. Srb - Supplies the particular request that data transfer is for. LogicalAddress - Supplies the logical address where the transfer should begin. Length - Supplies the maximum length in bytes of the transfer. Return Value: None. --*/ { PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension); PSRB_DATA srbData = Srb->OriginalRequest; ASSERT_SRB_DATA(srbData); // // If this is a 64-bit system then this call is illegal. Bugcheck. // if(Sp64BitPhysicalAddresses) { KeBugCheckEx(PORT_DRIVER_INTERNAL, 1, STATUS_NOT_SUPPORTED, (ULONG_PTR) HwDeviceExtension, (ULONG_PTR) deviceExtension->DeviceObject->DriverObject); } // // Make sure this host bus adapter has an Dma adapter object. // if (deviceExtension->DmaAdapterObject == NULL) { // // No DMA adapter, no work. // return; } ASSERT((Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) != SRB_FLAGS_UNSPECIFIED_DIRECTION); deviceExtension->InterruptData.MapTransferParameters.SrbData = srbData; deviceExtension->InterruptData.MapTransferParameters.LogicalAddress = LogicalAddress; deviceExtension->InterruptData.MapTransferParameters.Length = Length; deviceExtension->InterruptData.MapTransferParameters.SrbFlags = Srb->SrbFlags; deviceExtension->InterruptData.InterruptFlags |= PD_MAP_TRANSFER; // // Request a DPC be queued after the interrupt completes. // deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED; } // end ScsiPortIoMapTransfer()
VOID ScsiPortNotification( IN SCSI_NOTIFICATION_TYPE NotificationType, IN PVOID HwDeviceExtension, ... ) /*++ Routine Description: Arguments: Return Value: --*/ { PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension); PLOGICAL_UNIT_EXTENSION logicalUnit; PSRB_DATA srbData; PSCSI_REQUEST_BLOCK srb; UCHAR pathId; UCHAR targetId; UCHAR lun; va_list ap; va_start(ap, HwDeviceExtension); switch (NotificationType) { case NextRequest: // // Start next packet on adapter's queue. // deviceExtension->InterruptData.InterruptFlags |= PD_READY_FOR_NEXT_REQUEST; break; case RequestComplete: srb = va_arg(ap, PSCSI_REQUEST_BLOCK); ASSERT(srb->SrbStatus != SRB_STATUS_PENDING); ASSERT(srb->SrbStatus != SRB_STATUS_SUCCESS || srb->ScsiStatus == SCSISTAT_GOOD || srb->Function != SRB_FUNCTION_EXECUTE_SCSI); // // If this srb has already been completed then return, otherwise // clear the active flag. // if (srb->SrbFlags & SRB_FLAGS_IS_ACTIVE) { srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE; } else { va_end(ap); return; } // // Treat abort completions as a special case. // if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) { ASSERT(FALSE); logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->PathId, srb->TargetId, srb->Lun, FALSE, FALSE); logicalUnit->CompletedAbort = deviceExtension->InterruptData.CompletedAbort; deviceExtension->InterruptData.CompletedAbort = logicalUnit; } else { // // Validate the srb data. // srbData = srb->OriginalRequest; #if DBG ASSERT_SRB_DATA(srbData); ASSERT(srbData->CurrentSrb == srb); ASSERT(srbData->CurrentSrb != NULL && srbData->CompletedRequests == NULL); if ((srb->SrbStatus == SRB_STATUS_SUCCESS) && ((srb->Cdb[0] == SCSIOP_READ) || (srb->Cdb[0] == SCSIOP_WRITE))) { ASSERT(srb->DataTransferLength); } #endif if(((srb->SrbStatus == SRB_STATUS_SUCCESS) || (srb->SrbStatus == SRB_STATUS_DATA_OVERRUN)) && (TEST_FLAG(srb->SrbFlags, SRB_FLAGS_UNSPECIFIED_DIRECTION))) { ASSERT(srbData->OriginalDataTransferLength >= srb->DataTransferLength); } srbData->CompletedRequests = deviceExtension->InterruptData.CompletedRequests; deviceExtension->InterruptData.CompletedRequests = srbData; // // Cache away the last logical unit we touched in the miniport. // This is cleared when we come out of the miniport // synchronization but provides a shortcut for finding the // logical unit before going into the hash table. // deviceExtension->CachedLogicalUnit = srbData->LogicalUnit; } break; case ResetDetected: // // Notifiy the port driver that a reset has been reported. // deviceExtension->InterruptData.InterruptFlags |= PD_RESET_REPORTED | PD_RESET_HOLD; break; case NextLuRequest: // // The miniport driver is ready for the next request and // can accept a request for this logical unit. // pathId = va_arg(ap, UCHAR); targetId = va_arg(ap, UCHAR); lun = va_arg(ap, UCHAR); // // A next request is impiled by this notification so set the // ready for next reqeust flag. // deviceExtension->InterruptData.InterruptFlags |= PD_READY_FOR_NEXT_REQUEST; logicalUnit = deviceExtension->CachedLogicalUnit; if((logicalUnit == NULL) || (logicalUnit->TargetId != targetId) || (logicalUnit->PathId != pathId) || (logicalUnit->Lun != lun)) { logicalUnit = GetLogicalUnitExtension(deviceExtension, pathId, targetId, lun, FALSE, FALSE); } if (logicalUnit != NULL && logicalUnit->ReadyLogicalUnit != NULL) { // // Since our ReadyLogicalUnit link field is not NULL we must // have already been linked onto a ReadyLogicalUnit list. // There is nothing to do. // break; } // // Don't process this as request for the next logical unit, if // there is a untagged request for active for this logical unit. // The logical unit will be started when untagged request completes. // if (logicalUnit->CurrentUntaggedRequest == NULL) { // // Add the logical unit to the chain of logical units that // another request maybe processed for. // logicalUnit->ReadyLogicalUnit = deviceExtension->InterruptData.ReadyLogicalUnit; deviceExtension->InterruptData.ReadyLogicalUnit = logicalUnit; } break; case CallDisableInterrupts: ASSERT(deviceExtension->InterruptData.InterruptFlags & PD_DISABLE_INTERRUPTS); // // The miniport wants us to call the specified routine // with interrupts disabled. This is done after the current // HwRequestInterrutp routine completes. Indicate the call is // needed and save the routine to be called. // deviceExtension->Flags |= PD_DISABLE_CALL_REQUEST; deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT); break; case CallEnableInterrupts: // // The miniport wants us to call the specified routine // with interrupts enabled this is done from the DPC. // Disable calls to the interrupt routine, indicate the call is // needed and save the routine to be called. // deviceExtension->InterruptData.InterruptFlags |= PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST; deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT); break; case RequestTimerCall: // // The driver wants to set the miniport timer. // Save the timer parameters. // deviceExtension->InterruptData.InterruptFlags |= PD_TIMER_CALL_REQUEST; deviceExtension->InterruptData.HwTimerRequest = va_arg(ap, PHW_INTERRUPT); deviceExtension->InterruptData.MiniportTimerValue = va_arg(ap, ULONG); break; case WMIEvent: { // // The miniport wishes to post a WMI event for the adapter // or a specified SCSI target. // PWMI_MINIPORT_REQUEST_ITEM lastMiniPortRequest; PWMI_MINIPORT_REQUEST_ITEM wmiMiniPortRequest; PWNODE_EVENT_ITEM wnodeEventItem; PWNODE_EVENT_ITEM wnodeEventItemCopy; wnodeEventItem = va_arg(ap, PWNODE_EVENT_ITEM); pathId = va_arg(ap, UCHAR); if (pathId != 0xFF) { targetId = va_arg(ap, UCHAR); lun = va_arg(ap, UCHAR); } // // Validate the event first. Then attempt to obtain a free // WMI_MINIPORT_REQUEST_ITEM structure so that we may store // this request and process it at DPC level later. If none // are obtained or the event is bad, we ignore the request. // if ((wnodeEventItem == NULL) || (wnodeEventItem->WnodeHeader.BufferSize > WMI_MINIPORT_EVENT_ITEM_MAX_SIZE)) { va_end(ap); // size, no free WMI_MINIPORT_REQUEST_ITEMs left] return; } // // Remove the WMI_MINIPORT_REQUEST_ITEM from the free list. // wmiMiniPortRequest = SpWmiPopFreeRequestItem(deviceExtension); // // Log an error if a free request item could not be dequeued // (log only once in the lifetime of this adapter). // if (wmiMiniPortRequest == NULL) { if (!deviceExtension->WmiFreeMiniPortRequestsExhausted) { deviceExtension->WmiFreeMiniPortRequestsExhausted = TRUE; ScsiPortLogError(HwDeviceExtension, NULL, pathId, targetId, lun, SP_LOST_WMI_MINIPORT_REQUEST, 0); } va_end(ap); return; } // // Save information pertaining to this WMI request for later // processing. // deviceExtension->InterruptData.InterruptFlags |= PD_WMI_REQUEST; wmiMiniPortRequest->TypeOfRequest = (UCHAR)WMIEvent; wmiMiniPortRequest->PathId = pathId; wmiMiniPortRequest->TargetId = targetId; wmiMiniPortRequest->Lun = lun; RtlCopyMemory(wmiMiniPortRequest->WnodeEventItem, wnodeEventItem, wnodeEventItem->WnodeHeader.BufferSize); // // Queue the new WMI_MINIPORT_REQUEST_ITEM to the end of list in the // interrupt data structure. // wmiMiniPortRequest->NextRequest = NULL; lastMiniPortRequest = deviceExtension->InterruptData.WmiMiniPortRequests; if (lastMiniPortRequest) { while (lastMiniPortRequest->NextRequest) { lastMiniPortRequest = lastMiniPortRequest->NextRequest; } lastMiniPortRequest->NextRequest = wmiMiniPortRequest; } else { deviceExtension->InterruptData.WmiMiniPortRequests = wmiMiniPortRequest; } break; } case WMIReregister: { // // The miniport wishes to re-register the GUIDs for the adapter or // a specified SCSI target. // PWMI_MINIPORT_REQUEST_ITEM lastMiniPortRequest; PWMI_MINIPORT_REQUEST_ITEM wmiMiniPortRequest; pathId = va_arg(ap, UCHAR); if (pathId != 0xFF) { targetId = va_arg(ap, UCHAR); lun = va_arg(ap, UCHAR); } // // Attempt to obtain a free WMI_MINIPORT_REQUEST_ITEM structure // so that we may store this request and process it at DPC // level later. If none are obtained or the event is bad, we // ignore the request. // // Remove a WMI_MINPORT_REQUEST_ITEM from the free list. // wmiMiniPortRequest = SpWmiPopFreeRequestItem(deviceExtension); if (wmiMiniPortRequest == NULL) { // // Log an error if a free request item could not be dequeued // (log only once in the lifetime of this adapter). // if (!deviceExtension->WmiFreeMiniPortRequestsExhausted) { deviceExtension->WmiFreeMiniPortRequestsExhausted = TRUE; ScsiPortLogError(HwDeviceExtension, NULL, pathId, targetId, lun, SP_LOST_WMI_MINIPORT_REQUEST, 0); } va_end(ap); return; } // // Save information pertaining to this WMI request for later // processing. // deviceExtension->InterruptData.InterruptFlags |= PD_WMI_REQUEST; wmiMiniPortRequest->TypeOfRequest = (UCHAR)WMIReregister; wmiMiniPortRequest->PathId = pathId; wmiMiniPortRequest->TargetId = targetId; wmiMiniPortRequest->Lun = lun; // // Queue the new WMI_MINIPORT_REQUEST_ITEM to the end of list in the // interrupt data structure. // wmiMiniPortRequest->NextRequest = NULL; lastMiniPortRequest = deviceExtension->InterruptData.WmiMiniPortRequests; if (lastMiniPortRequest) { while (lastMiniPortRequest->NextRequest) { lastMiniPortRequest = lastMiniPortRequest->NextRequest; } lastMiniPortRequest->NextRequest = wmiMiniPortRequest; } else { deviceExtension->InterruptData.WmiMiniPortRequests = wmiMiniPortRequest; } break; } case BusChangeDetected: { SET_FLAG(deviceExtension->InterruptData.InterruptFlags, PD_BUS_CHANGE_DETECTED); break; } default: { ASSERT(0); break; } } va_end(ap); // // Request a DPC be queued after the interrupt completes. // deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED; } // end ScsiPortNotification()
SCSI_PHYSICAL_ADDRESS ScsiPortGetPhysicalAddress( IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID VirtualAddress, OUT ULONG *Length ) /*++ Routine Description: Convert virtual address to physical address for DMA. Arguments: Return Value: --*/ { PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension); ULONG byteOffset; PHYSICAL_ADDRESS address; ULONG length; if (Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress) { byteOffset = (ULONG)((PCCHAR) VirtualAddress - (PCCHAR) deviceExtension->SrbExtensionBuffer); ASSERT(byteOffset < deviceExtension->CommonBufferSize); length = deviceExtension->CommonBufferSize - byteOffset; address.QuadPart = deviceExtension->PhysicalCommonBuffer.QuadPart + byteOffset; } else if (deviceExtension->MasterWithAdapter) { #ifdef USE_DMA_MACROS PSCATTER_GATHER_ELEMENT scatterList; #else PSRB_SCATTER_GATHER scatterList; #endif PSRB_DATA srbData; // // A scatter/gather list has already been allocated use it to determine // the physical address and length. Get the scatter/gather list. // srbData = Srb->OriginalRequest; ASSERT_SRB_DATA(srbData); scatterList = #ifdef USE_DMA_MACROS (PSCATTER_GATHER_ELEMENT) #endif // USE_DMA_MACROS srbData->ScatterGatherList; // // Calculate byte offset into the data buffer. // byteOffset = (ULONG)((PCHAR) VirtualAddress - (PCHAR) Srb->DataBuffer); // // Find the appropriate entry in the scatter/gatter list. // while (byteOffset >= scatterList->Length) { byteOffset -= scatterList->Length; scatterList++; } // // Calculate the physical address and length to be returned. // length = scatterList->Length - byteOffset; #ifdef USE_DMA_MACROS address.QuadPart = scatterList->Address.QuadPart + byteOffset; #else address.QuadPart = scatterList->PhysicalAddress.QuadPart + byteOffset; #endif } else { *Length = 0; address.QuadPart = (LONGLONG)(SP_UNINITIALIZED_VALUE); } *Length = length; return address; } // end ScsiPortGetPhysicalAddress()
VOID FASTCALL SpCompleteRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN OPTIONAL PSRB_DATA SrbData, IN CCHAR PriorityBoost ) /*++ Routine Description: This routine is a wrapper around IoCompleteRequest. It is used primarily for debugging purposes. The routine will assert if the Irp being completed is still holding the release lock. Arguments: Return Value: none --*/ { PADAPTER_EXTENSION adapterExtension = DeviceObject->DeviceExtension; #if DBG PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PREMOVE_TRACKING_BLOCK *listEntry = &((PREMOVE_TRACKING_BLOCK) commonExtension->RemoveTrackingList); KIRQL oldIrql; KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql); while(*listEntry != NULL) { if((*listEntry)->Tag == Irp) { break; } listEntry = &((*listEntry)->NextBlock); } if(*listEntry != NULL) { DebugPrint((0, ">>>>SpCompleteRequest: Irp %#p completed while " "still holding the remove lock\n", Irp)); DebugPrint((0, ">>>>SpReleaseRemoveLock: Lock acquired in file " "%s on line %d\n", (*listEntry)->File, (*listEntry)->Line)); ASSERT(FALSE); } KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql); if(ARGUMENT_PRESENT(SrbData)) { PLOGICAL_UNIT_EXTENSION logicalUnit; ASSERT_SRB_DATA(SrbData); ASSERT(SrbData->ScatterGatherList == NULL); ASSERT_SRB_DATA(SrbData); ASSERT(SrbData->CurrentIrp == Irp); logicalUnit = SrbData->LogicalUnit; ASSERT(logicalUnit != NULL); ASSERT(logicalUnit->CurrentUntaggedRequest != SrbData); ASSERT_PDO(logicalUnit->CommonExtension.DeviceObject); ASSERT(SrbData->RemappedMdl == NULL); } #endif // // If the caller specified an SRB_DATA structure for the completion then // we will free it to the lookaside list, fix the OriginalIrp value in the // srb and release the queue-tag value assigned to the device. // if(ARGUMENT_PRESENT(SrbData)) { PLOGICAL_UNIT_EXTENSION logicalUnit; logicalUnit = SrbData->LogicalUnit; if((SrbData->CurrentSrb->Function == SRB_FUNCTION_LOCK_QUEUE) || (SrbData->CurrentSrb->Function == SRB_FUNCTION_UNLOCK_QUEUE)) { ASSERT(SrbData->CurrentSrb == logicalUnit->LockRequest); logicalUnit->LockRequest = NULL; } SrbData->CurrentSrb->OriginalRequest = SrbData->CurrentIrp; SrbData->CurrentIrp = NULL; SrbData->CurrentSrb = NULL; ASSERT(SrbData->FreeRoutine != NULL); ASSERT((SrbData->FreeRoutine == SpFreeSrbData) || (SrbData->FreeRoutine == SpFreeBypassSrbData)); SrbData->FreeRoutine(logicalUnit->AdapterExtension, SrbData); SpReleaseRemoveLock(logicalUnit->CommonExtension.DeviceObject, Irp); } IoCompleteRequest(Irp, PriorityBoost); return; }