/*++ Copyright (c) 1990 Microsoft Corporation Module Name: scsiboot.c Abstract: This is the NT SCSI port driver. Author: Mike Glass Jeff Havens Environment: kernel mode only Notes: This module is linked into the kernel. Revision History: --*/ #if !defined(DECSTATION) #include "stdarg.h" #include "stdio.h" #ifdef MIPS #include "..\fw\mips\fwp.h" #elif defined(ALPHA) #include "..\fw\alpha\fwp.h" #else #include "boot.h" #endif #include "scsi.h" #include "scsiboot.h" ULONG ScsiPortCount; ULONG ScsiDebug = 0; PDEVICE_OBJECT ScsiPortDeviceObject[10]; PINQUIRYDATA InquiryDataBuffer; FULL_SCSI_REQUEST_BLOCK PrimarySrb; FULL_SCSI_REQUEST_BLOCK RequestSenseSrb; FULL_SCSI_REQUEST_BLOCK AbortSrb; // // Function declarations // VOID IopAllocateCommonBuffer( IN PVOID NonCachedExtension, IN ULONG NonCachedExtensionSize, OUT PPHYSICAL_ADDRESS LogicalAddress ); ARC_STATUS ScsiPortDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID ScsiPortExecute( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID ScsiPortStartIo ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); BOOLEAN ScsiPortInterrupt( IN PKINTERRUPT InterruptObject, IN PDEVICE_OBJECT DeviceObject ); VOID ScsiPortCompletionDpc( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); VOID ScsiPortTickHandler( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ); IO_ALLOCATION_ACTION ScsiPortAllocationRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context ); ARC_STATUS IssueInquiry( IN PDEVICE_EXTENSION deviceExtension, IN PLUNINFO LunInfo ); VOID IssueRequestSense( IN PDEVICE_EXTENSION deviceExtension, IN PSCSI_REQUEST_BLOCK FailingSrb ); VOID ScsiPortInternalCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ); PSCSI_BUS_SCAN_DATA ScsiBusScan( IN PDEVICE_EXTENSION DeviceExtension, IN UCHAR ScsiBus, IN UCHAR InitiatorBusId ); PLOGICAL_UNIT_EXTENSION CreateLogicalUnitExtension( IN PDEVICE_EXTENSION DeviceExtension ); BOOLEAN SpStartIoSynchronized ( PVOID ServiceContext ); VOID IssueAbortRequest( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP FailingIrp ); IO_ALLOCATION_ACTION SpBuildScatterGather( IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp, IN PVOID MapRegisterBase, IN PVOID Context ); BOOLEAN SpGetInterruptState( IN PVOID ServiceContext ); VOID SpTimerDpc( IN PKDPC Dpc, IN PVOID Context, IN PVOID SystemContext1, IN PVOID SystemContext2 ); PLOGICAL_UNIT_EXTENSION GetLogicalUnitExtension( PDEVICE_EXTENSION DeviceExtension, UCHAR TargetId ); NTSTATUS SpInitializeConfiguration( IN PDEVICE_EXTENSION DeviceExtension, IN PHW_INITIALIZATION_DATA HwInitData, OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN BOOLEAN InitialCall ); NTSTATUS SpGetCommonBuffer( PDEVICE_EXTENSION DeviceExtension, ULONG NonCachedExtensionSize ); #ifdef i386 ULONG HalpGetCmosData( IN ULONG SourceLocation, IN ULONG SourceAddress, IN PVOID ReturnBuffer, IN ULONG ByteCount ); #endif // // Routines start // ULONG ScsiPortInitialize( IN PVOID Argument1, IN PVOID Argument2, IN struct _HW_INITIALIZATION_DATA *HwInitializationData, IN PVOID HwContext ) /*++ Routine Description: This routine initializes the port driver. Arguments: Argument1 - Pointer to driver object created by system HwInitializationData - Miniport initialization structure HwContext - Value passed to miniport driver's config routine Return Value: The function value is the final status from the initialization operation. --*/ { PDEVICE_EXTENSION deviceExtension; PDEVICE_OBJECT DeviceObject; PORT_CONFIGURATION_INFORMATION configInfo; KEVENT allocateAdapterEvent; ULONG ExtensionAllocationSize; ULONG j; UCHAR scsiBus; PULONG scsiPortNumber; ULONG numberOfPageBreaks; PIO_SCSI_CAPABILITIES capabilities; BOOLEAN callAgain; DEVICE_DESCRIPTION deviceDescription; ARC_CODES status; BOOLEAN foundOne = FALSE; UNREFERENCED_PARAMETER(Argument1); UNREFERENCED_PARAMETER(Argument2); if (HwInitializationData->HwInitializationDataSize != sizeof(HW_INITIALIZATION_DATA)) { DebugPrint((0,"ScsiPortInitialize: Miniport driver wrong version\n")); return EBADF; } // // Check that each required entry is not NULL. // if ((!HwInitializationData->HwInitialize) || (!HwInitializationData->HwFindAdapter) || (!HwInitializationData->HwResetBus)) { DebugPrint((0, "ScsiPortInitialize: Miniport driver missing required entry\n")); return EBADF; } CallAgain: // // Get the configuration information // scsiPortNumber = &ScsiPortCount; // // Determine size of extensions. // ExtensionAllocationSize = DEVICE_EXTENSION_SIZE + HwInitializationData->DeviceExtensionSize + sizeof(DEVICE_OBJECT); DeviceObject = ExAllocatePool(NonPagedPool, ExtensionAllocationSize); if (DeviceObject == NULL) { return ENOMEM; } RtlZeroMemory(DeviceObject, ExtensionAllocationSize); // // Set up device extension pointers // deviceExtension = DeviceObject->DeviceExtension = (PVOID) (DeviceObject + 1); deviceExtension->DeviceObject = DeviceObject; // // Save the dependent driver routines in the device extension. // deviceExtension->HwInitialize = HwInitializationData->HwInitialize; deviceExtension->HwStartIo = HwInitializationData->HwStartIo; deviceExtension->HwInterrupt = HwInitializationData->HwInterrupt; deviceExtension->HwReset = HwInitializationData->HwResetBus; deviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted; deviceExtension->HwLogicalUnitExtensionSize = HwInitializationData->SpecificLuExtensionSize; deviceExtension->HwDeviceExtension = (PVOID)(deviceExtension + 1); // // Set indicater as to whether adapter needs kernel mapped buffers. // deviceExtension->MapBuffers = HwInitializationData->MapBuffers; // // Mark this object as supporting direct I/O so that I/O system // will supply mdls in irps. // DeviceObject->Flags |= DO_DIRECT_IO; // // Check if miniport driver requires any noncached memory. // SRB extensions will come from zoned memory. A page is // allocated as it is the smallest unit of noncached memory // allocation. // deviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize; // // Get the miniport configuration information. // capabilities = &deviceExtension->Capabilities; capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); callAgain = FALSE; if (!NT_SUCCESS(SpInitializeConfiguration( deviceExtension, HwInitializationData, &configInfo, TRUE ))) { DebugPrint((2, "ScsiPortInitialize: no Configuration info found\n")); return(ENODEV); } configInfo.NumberOfAccessRanges = HwInitializationData->NumberOfAccessRanges; configInfo.AccessRanges = ExAllocatePool( NonPagedPool, sizeof(ACCESS_RANGE) * HwInitializationData->NumberOfAccessRanges ); if (configInfo.AccessRanges == NULL) { return ENOMEM; } if (HwInitializationData->HwFindAdapter( deviceExtension->HwDeviceExtension, // DeviceExtension HwContext, // HwContext NULL, // BusInformation NULL, // ArgumentString &configInfo, // ConfigurationInformation &callAgain // Again ) != SP_RETURN_FOUND) { return foundOne ? ESUCCESS : EIO; } ScsiDebugPrint(1,"ScsiPortInitialize: SCSI adapter IRQ is %d\n", configInfo.BusInterruptLevel); ScsiDebugPrint(1,"ScsiPortInitialize: SCSI adapter ID is %d\n", configInfo.InitiatorBusId[0]); deviceExtension->NumberOfBuses = configInfo.NumberOfBuses; // // Free the pointer to the bus data at map register base. This was // allocated by ScsiPortGetBusData. // if (deviceExtension->MapRegisterBase != NULL) { ExFreePool(deviceExtension->MapRegisterBase); } // // Allocate memory for the non cached extension if it has not already been // allocated. // if (deviceExtension->SrbExtensionSize != 0 && deviceExtension->SrbExtensionZonePool == NULL) { status = SpGetCommonBuffer(deviceExtension, 0); if (status != ESUCCESS) { return(status); } } // // Get the adapter object for this card. // if ((configInfo.Master || configInfo.DmaChannel != 0xFFFFFFFF)) { deviceDescription.Version = DEVICE_DESCRIPTION_VERSION; deviceDescription.DmaChannel = configInfo.DmaChannel; deviceDescription.InterfaceType = configInfo.AdapterInterfaceType; deviceDescription.BusNumber = configInfo.SystemIoBusNumber; deviceDescription.DmaWidth = configInfo.DmaWidth; deviceDescription.DmaSpeed = configInfo.DmaSpeed; deviceDescription.DmaPort = configInfo.DmaPort; deviceDescription.MaximumLength = configInfo.MaximumTransferLength; deviceDescription.ScatterGather = configInfo.ScatterGather; deviceDescription.Master = configInfo.Master; deviceDescription.AutoInitialize = FALSE; deviceDescription.DemandMode = FALSE; // BugBug: Make the 0x11000 a define when there is one. if (configInfo.MaximumTransferLength > 0x11000) { deviceDescription.MaximumLength = 0x11000; } else { deviceDescription.MaximumLength = configInfo.MaximumTransferLength; } deviceExtension->DmaAdapterObject = HalGetAdapter( &deviceDescription, &numberOfPageBreaks ); // // Set maximum number of page breaks. // if (numberOfPageBreaks > configInfo.NumberOfPhysicalBreaks) { capabilities->MaximumPhysicalPages = configInfo.NumberOfPhysicalBreaks; } else { capabilities->MaximumPhysicalPages = numberOfPageBreaks; } } capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); capabilities->MaximumTransferLength = configInfo.MaximumTransferLength; ScsiDebugPrint(1, "Maximum physical page breaks = %d. Maximum transfer length = %x\n", capabilities->MaximumPhysicalPages, capabilities->MaximumTransferLength); if (HwInitializationData->ReceiveEvent) { capabilities->SupportedAsynchronousEvents |= SRBEV_SCSI_ASYNC_NOTIFICATION; } capabilities->TaggedQueuing = HwInitializationData->TaggedQueuing; capabilities->AdapterScansDown = configInfo.AdapterScansDown; // // Make sure maximum nuber of pages is set to a reasonable value. // This occurs for mini-ports with no Dma adapter. // if (capabilities->MaximumPhysicalPages == 0) { capabilities->MaximumPhysicalPages = ROUND_TO_PAGES(capabilities->MaximumTransferLength) + 1; // // Honor any limit requested by the mini-port. // if (configInfo.NumberOfPhysicalBreaks < capabilities->MaximumPhysicalPages) { capabilities->MaximumPhysicalPages = configInfo.NumberOfPhysicalBreaks; } } if (deviceExtension->DmaAdapterObject != NULL && !HwInitializationData->NeedPhysicalAddresses) { // // Allocate the adapter object. For the port driver the adapter object // and map registers are permentently allocated and used shared between // all logical units. The adapter is allocated by initializing an event, // calling IoAllocateAdapterChannel and waiting on the event. When the // adapter and map registers are available, ScsiPortAllocationRoutine is // called which set the event. In reality, all this takes zero time since // the stuff is available immediately. // // Allocate the AdapterObject. The number of registers is equal to the // maximum transfer length supported by the adapter + 1. This insures // that there will always be a sufficient number of registers. // /* TODO: Fix this for the case when there is no maximum transfer length. */ IoAllocateAdapterChannel( deviceExtension->DmaAdapterObject, DeviceObject, // configInfo.NumberOfPhysicalBreaks, capabilities->MaximumPhysicalPages, ScsiPortAllocationRoutine, &allocateAdapterEvent ); // // Wait for adapter object. // ASSERT(deviceExtension->MapRegisterBase); deviceExtension->MasterWithAdapter = FALSE; } else if (deviceExtension->DmaAdapterObject != NULL) { // // This SCSI adapter is a master with an adapter so a scatter/gather // list needs to be allocated for each transfer. // deviceExtension->MasterWithAdapter = TRUE; } else { deviceExtension->MasterWithAdapter = FALSE; } // end if (deviceExtension->DmaAdapterObject != NULL) // // Call the hardware dependent driver to do its initialization. // if (!KeSynchronizeExecution( deviceExtension->InterruptObject, deviceExtension->HwInitialize, deviceExtension->HwDeviceExtension )) { ScsiDebugPrint(1,"ScsiPortInitialize: initialization failed\n"); return ENODEV; } // // Allocate properly aligned INQUIRY buffer. // InquiryDataBuffer = ExAllocatePool(NonPagedPool, INQUIRYDATABUFFERSIZE); if (InquiryDataBuffer == NULL) { return(ENOMEM); } // // Reset the scsi bus. // if (!deviceExtension->HwReset( deviceExtension->HwDeviceExtension, 0)){ ScsiDebugPrint(1,"Reset SCSI bus failed\n"); } // // Call the interupt handler for a few microseconds to clear any reset // interrupts. // for (j = 0; j < 1000 * 100; j++) { FwStallExecution(10); if (deviceExtension->HwInterrupt != NULL) { deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension); } } // // Wait 2 seconds for the devices to recover after the reset. // //FwStallExecution(2 * 1000 * 1000); FwStallExecution(2 * 1000 * 100); // // Find devices on each SCSI bus. // // // Allocate buffer for SCSI bus scan information. // deviceExtension->ScsiInfo = ExAllocatePool(NonPagedPool, deviceExtension->NumberOfBuses * sizeof(PSCSI_BUS_SCAN_DATA) + 4); if (deviceExtension->ScsiInfo) { deviceExtension->ScsiInfo->NumberOfBuses = deviceExtension->NumberOfBuses; // // Find devices on each SCSI bus. // for (scsiBus = 0; scsiBus < deviceExtension->NumberOfBuses; scsiBus++) { deviceExtension->ScsiInfo->BusScanData[scsiBus] = ScsiBusScan(deviceExtension, scsiBus, configInfo.InitiatorBusId[scsiBus]); } } // // Save the device object for use by the driver. // ScsiPortDeviceObject[*scsiPortNumber] = DeviceObject; // // Bump SCSI host bus adapters count. // (*scsiPortNumber)++; foundOne = TRUE; // // If the adapter wants to be called again with the same configuration data // then start over from the begining again. // if (callAgain) { goto CallAgain; } return ESUCCESS; } // end ScsiPortInitialize() IO_ALLOCATION_ACTION ScsiPortAllocationRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context ) /*++ Routine Description: This function is called by IoAllocateAdapterChannel when sufficent resources are available to the driver. This routine saves the MapRegisterBase in the device object and set the event pointed to by the context parameter. Arguments: DeviceObject - Pointer to the device object to which the adapter is being allocated. Irp - Unused. MapRegisterBase - Supplied by the Io subsystem for use in IoMapTransfer. Context - Supplies a pointer to an event which is set to indicate the AdapterObject has been allocated. Return Value: KeepObject - Indicates the adapter and mapregisters should remain allocated after return. --*/ { ((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->MapRegisterBase = MapRegisterBase; UNREFERENCED_PARAMETER(Irp); UNREFERENCED_PARAMETER(Context); return(KeepObject); } IO_ALLOCATION_ACTION SpBuildScatterGather( IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp, IN PVOID MapRegisterBase, IN PVOID Context ) /*++ Routine Description: This function is called by the I/O system when an adapter object and map registers have been allocated. This routine then builds a scatter/gather list for use by the mini-port driver. Next it sets the timeout and the current Irp for the logical unit. Finally it calls the mini-port StartIo routine. Once that routines complete, this routine will return requesting that the adapter be freed and but the registers remain allocated. The registers will be freed the request completes. Arguments: DeviceObject - Supplies a pointer to the port driver device object. Irp - Supplies a pointer to the current Irp. MapRegisterBase - Supplies a context pointer to be used with calls the adapter object routines. Context - Supplies a pointer to the logical unit structure. Return Value: Returns DeallocateObjectKeepRegisters so that the adapter object can be used by other logical units. --*/ { BOOLEAN writeToDevice; PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); PLOGICAL_UNIT_EXTENSION logicalUnit; PSCSI_REQUEST_BLOCK srb; PSRB_SCATTER_GATHER scatterList; ULONG totalLength; logicalUnit = Context; srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; scatterList = logicalUnit->ScatterGather; totalLength = 0; // // Save the MapRegisterBase for later use to deallocate the map registers. // logicalUnit->MapRegisterBase = MapRegisterBase; // // Build the scatter/gather list by looping throught the transfer calling // I/O map transfer. // writeToDevice = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE; while (totalLength < srb->DataTransferLength) { // // Request that the rest of the transfer be mapped. // scatterList->Length = srb->DataTransferLength - totalLength; // // Since we are a master call I/O map transfer with a NULL adapter. // scatterList->PhysicalAddress = IoMapTransfer( NULL, Irp->MdlAddress, MapRegisterBase, (PCCHAR) srb->DataBuffer + totalLength, &scatterList->Length, writeToDevice ).LowPart; totalLength += scatterList->Length; scatterList++; } // // Set request timeout value from Srb SCSI extension in Irp. // logicalUnit->RequestTimeoutCounter = srb->TimeOutValue; // // Set current request for this logical unit. // logicalUnit->CurrentRequest = Irp; /* TODO: Check the return value. */ KeSynchronizeExecution( ((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->InterruptObject, SpStartIoSynchronized, DeviceObject ); return(DeallocateObjectKeepRegisters); } VOID ScsiPortExecute( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine calls the start I/O routine an waits for the request to complete. During the wait for complete the interrupt routine is called, also the timer routines are called at the appropriate times. After the request completes a check is made to determine if an request sense needs to be issued. Arguments: DeviceObject - Supplies pointer to Adapter device object. Irp - Supplies a pointer to an IRP. Return Value: Nothing. --*/ { ULONG milliSecondTime; ULONG secondTime; ULONG completionDelay; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; PVOID logicalUnit; deviceExtension = DeviceObject->DeviceExtension; logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId); if (logicalUnit == NULL) { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; return; } // // Mark IRP as pending. // Irp->PendingReturned = TRUE; // // Start the request. // ScsiPortStartIo( DeviceObject, Irp); // // The completion delay controls how long interrupts are serviced after // a request has been completed. This allows interrupts which occur after // a completion to be serviced. // completionDelay = COMPLETION_DELAY; // // Wait for the IRP to complete. // while (Irp->PendingReturned && completionDelay) { // // Wait 1 second then call the scsi port timer routine. // for (secondTime = 0; secondTime < 1000/ 500; secondTime++) { for (milliSecondTime = 0; milliSecondTime < (250 * 1000 / PD_INTERLOOP_STALL); milliSecondTime++) { ScsiPortInterrupt(NULL, DeviceObject); if (!Irp->PendingReturned) { if (completionDelay-- == 0) { goto done; } } if (deviceExtension->Flags & PD_ENABLE_CALL_REQUEST) { // // Call the mini-port requested routine. // deviceExtension->Flags &= ~PD_ENABLE_CALL_REQUEST; deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension); if (deviceExtension->Flags & PD_DISABLE_CALL_REQUEST) { deviceExtension->Flags &= ~(PD_DISABLE_INTERRUPTS | PD_DISABLE_CALL_REQUEST); deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension); } } FwStallExecution(PD_INTERLOOP_STALL); // // Check the miniport timer. // if (deviceExtension->TimerValue != 0) { deviceExtension->TimerValue--; if (deviceExtension->TimerValue == 0) { // // The timer timed out so called requested timer routine. // deviceExtension->HwTimerRequest(deviceExtension->HwDeviceExtension); } } } } ScsiPortTickHandler(DeviceObject, NULL); } done: if (!NT_SUCCESS(Irp->IoStatus.Status)) { PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; // // 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 (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION && !(srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && srb->SenseInfoBuffer) { // // Call IssueRequestSense and it will complete the request after // the REQUEST SENSE completes. // IssueRequestSense(deviceExtension, Srb); } } } VOID ScsiPortStartIo ( 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 = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PLOGICAL_UNIT_EXTENSION logicalUnit; PFULL_SCSI_REQUEST_BLOCK FullSrb; NTSTATUS status; ScsiDebugPrint(3,"ScsiPortStartIo: Enter routine\n"); FullSrb = CONTAINING_RECORD(Srb, FULL_SCSI_REQUEST_BLOCK, Srb); if (deviceExtension->SrbExtensionZonePool && (Srb->SrbExtension == NULL || deviceExtension->SrbExtensionSize > FullSrb->SrbExtensionSize)) { // // Allocate SRB extension from zone. // Srb->SrbExtension = deviceExtension->SrbExtensionPointer; (PCCHAR) deviceExtension->SrbExtensionPointer += deviceExtension->SrbExtensionSize; FullSrb->SrbExtensionSize = deviceExtension->SrbExtensionSize; if ((ULONG) deviceExtension->SrbExtensionPointer > (ULONG) deviceExtension->NonCachedExtension) { ScsiDebugPrint(0, "NtLdr: ScsiPortStartIo: Srb extension overflow. Too many srb extension allocated.\n"); } ScsiDebugPrint(3,"ExInterlockedAllocateFromZone: %lx\n", Srb->SrbExtension); ScsiDebugPrint(3,"Srb %lx\n",Srb); } // // Get logical unit extension. // logicalUnit = GetLogicalUnitExtension(deviceExtension, Srb->TargetId); // // Flush the data buffer if necessary. // if (Srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)) { if (Srb->DataTransferLength > deviceExtension->Capabilities.MaximumTransferLength) { ScsiDebugPrint(1, "Scsiboot: ScsiPortStartIo Length Exceeds limit %x, %x\n Hit any key\n", Srb->DataTransferLength, deviceExtension->Capabilities.MaximumTransferLength ); } KeFlushIoBuffers( Irp->MdlAddress, Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE, TRUE ); // // Determine if this adapter needs map registers // if (deviceExtension->MasterWithAdapter) { // // Calculate the number of map registers needed for this transfer. // logicalUnit->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES( Srb->DataBuffer, Srb->DataTransferLength ); // // Allocate the adapter channel with sufficient map registers // for the transfer. // status = IoAllocateAdapterChannel( deviceExtension->DmaAdapterObject, // AdapterObject DeviceObject, // DeviceObject. logicalUnit->NumberOfMapRegisters, // NumberOfMapRegisters SpBuildScatterGather, // ExecutionRoutine logicalUnit // Context ); if (!NT_SUCCESS(status)) { ; } // // The execution routine called by IoAllocateChannel will do the // rest of the work so just return. // return; } } // // Set request timeout value from Srb SCSI extension in Irp. // logicalUnit->RequestTimeoutCounter = Srb->TimeOutValue; // // Set current request for this logical unit. // logicalUnit->CurrentRequest = Irp; /* TODO: Check the return value. */ KeSynchronizeExecution( deviceExtension->InterruptObject, SpStartIoSynchronized, DeviceObject ); return; } // end ScsiPortStartIO() BOOLEAN SpStartIoSynchronized ( PVOID ServiceContext ) /*++ Routine Description: This routine calls the dependent driver start io routine. Arguments: ServiceContext - Supplies the pointer to the device object. Return Value: Returns the value returned by the dependent start I/O routine. --*/ { PDEVICE_OBJECT DeviceObject = ServiceContext; PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpstack; PSCSI_REQUEST_BLOCK Srb; ScsiDebugPrint(3, "ScsiPortStartIoSynchronized: Enter routine\n"); irpstack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp); Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; ScsiDebugPrint(3, "SpPortStartIoSynchronized: SRB %lx\n", Srb); ScsiDebugPrint(3, "SpPortStartIoSynchronized: IRP %lx\n", DeviceObject->CurrentIrp); // // Disable all synchronous transfers. // Srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT | SRB_FLAGS_DISABLE_AUTOSENSE); return deviceExtension->HwStartIo( deviceExtension->HwDeviceExtension, Srb ); } // end SpStartIoSynchronized() BOOLEAN ScsiPortInterrupt( IN PKINTERRUPT Interrupt, IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Arguments: Interrupt Device Object Return Value: Returns TRUE if interrupt expected. --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; UNREFERENCED_PARAMETER(Interrupt); if (deviceExtension->HwInterrupt != NULL) { if (deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension)) { return TRUE; } else { return FALSE; } } return(FALSE); } // end ScsiPortInterrupt() VOID ScsiPortCompletionDpc( 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. --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpstack; PSCSI_REQUEST_BLOCK Srb; PLOGICAL_UNIT_EXTENSION luExtension; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(Context); ScsiDebugPrint(3, "ScsiPortCompletionDpc Entered\n"); // // Acquire the spinlock to protect the flags structure and the saved // interrupt context. // KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql); // // Check for a flush DMA adapter object request. // if (deviceExtension->InterruptFlags & PD_FLUSH_ADAPTER_BUFFERS) { // // Call IoFlushAdapterBuffers using the parameters saved from the last // IoMapTransfer call. // IoFlushAdapterBuffers( deviceExtension->DmaAdapterObject, ((PIRP)deviceExtension->FlushAdapterParameters.Srb->OriginalRequest) ->MdlAddress, deviceExtension->MapRegisterBase, deviceExtension->FlushAdapterParameters.LogicalAddress, deviceExtension->FlushAdapterParameters.Length, (BOOLEAN)(deviceExtension->FlushAdapterParameters.Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) ); } // // Check for an IoMapTransfer DMA request. // if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) { // // Call IoMapTransfer using the parameters saved from the // interrupt level. // IoMapTransfer( deviceExtension->DmaAdapterObject, ((PIRP)deviceExtension->MapTransferParameters.Srb->OriginalRequest) ->MdlAddress, deviceExtension->MapRegisterBase, deviceExtension->MapTransferParameters.LogicalAddress, &deviceExtension->MapTransferParameters.Length, (BOOLEAN)(deviceExtension->MapTransferParameters.Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) ); // // Save the paramters for IoFlushAdapterBuffers. // deviceExtension->FlushAdapterParameters = deviceExtension->MapTransferParameters; // // If necessary notify the mini-port driver that the DMA has been // started. // if (deviceExtension->HwDmaStarted) { KeSynchronizeExecution( &deviceExtension->InterruptObject, (PKSYNCHRONIZE_ROUTINE) deviceExtension->HwDmaStarted, deviceExtension->HwDeviceExtension ); } } // // Process any completed requests. // while (deviceExtension->CompletedRequests != NULL) { Irp = deviceExtension->CompletedRequests; irpstack = IoGetCurrentIrpStackLocation(Irp); Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; luExtension = GetLogicalUnitExtension(deviceExtension, Srb->TargetId); ScsiDebugPrint(3, "ScsiPortCompletionDpc: SRB %lx\n", Srb); ScsiDebugPrint(3, "ScsiPortCompletionDpc: IRP %lx\n", Irp); // // Remove the request from the linked-list. // deviceExtension->CompletedRequests = irpstack->Parameters.Others.Argument3; // // Reset request timeout counter. // luExtension->RequestTimeoutCounter = -1; // // Flush the adapter buffers if necessary. // if (luExtension->MapRegisterBase) { // // Since we are a master call I/O flush adapter buffers with a NULL // adapter. // IoFlushAdapterBuffers( NULL, Irp->MdlAddress, luExtension->MapRegisterBase, Srb->DataBuffer, Srb->DataTransferLength, (BOOLEAN) (Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) ); // // Free the map registers. // IoFreeMapRegisters( deviceExtension->DmaAdapterObject, luExtension->MapRegisterBase, luExtension->NumberOfMapRegisters ); // // Clear the MapRegisterBase. // luExtension->MapRegisterBase = NULL; } // // Set IRP status. Class drivers will reset IRP status based // on request sense if error. // if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) { Irp->IoStatus.Status = STATUS_SUCCESS; } else { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } // // Move bytes transfered to IRP. // Irp->IoStatus.Information = Srb->DataTransferLength; // // If success then start next packet. // Not starting packet effectively // freezes the queue. // if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) { ScsiDebugPrint( 2, "ScsiPortCompletionDpc: Iocompletion IRP %lx\n", Irp ); IoCompleteRequest(Irp, 2); } else { if ( Srb->ScsiStatus == SCSISTAT_BUSY && (luExtension->RetryCount++ < 2)) { // // 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; luExtension->CurrentRequest = Irp; luExtension->Flags |= PD_LOGICAL_UNIT_IS_BUSY; } else { ScsiDebugPrint( 3, "ScsiPortCompletionDpc: Iocompletion IRP %lx\n", Irp ); IoCompleteRequest(Irp, 2); } } } // // Release the spinlock. // KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql); return; } // end ScsiPortCompletionDpc() ARC_STATUS IssueInquiry( IN PDEVICE_EXTENSION DeviceExtension, IN PLUNINFO LunInfo ) /*++ Routine Description: Build IRP, SRB and CDB for SCSI INQUIRY command. Arguments: DeviceExtension - address of adapter's device object extension. LunInfo - address of buffer for INQUIRY information. Return Value: ARC_STATUS --*/ { PIRP irp; PIO_STACK_LOCATION irpstack; PCDB cdb; PSCSI_REQUEST_BLOCK srb; ARC_STATUS status; ULONG retryCount = 0; ScsiDebugPrint(3,"IssueInquiry: Enter routine\n"); if (InquiryDataBuffer == NULL) { return ENOMEM; } inquiryRetry: // // Build IRP for this request. // irp = InitializeIrp( &PrimarySrb, IRP_MJ_SCSI, DeviceExtension->DeviceObject, (PVOID)InquiryDataBuffer, INQUIRYDATABUFFERSIZE ); irpstack = IoGetNextIrpStackLocation(irp); // // Set major and minor codes. // irpstack->MajorFunction = IRP_MJ_SCSI; // // Fill in SRB fields. // irpstack->Parameters.Others.Argument1 = &PrimarySrb; srb = &PrimarySrb.Srb; srb->Length = sizeof(SCSI_REQUEST_BLOCK); srb->PathId = LunInfo->PathId; srb->TargetId = LunInfo->TargetId; srb->Lun = LunInfo->Lun; srb->Function = SRB_FUNCTION_EXECUTE_SCSI; srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT; srb->SrbStatus = srb->ScsiStatus = 0; srb->OriginalRequest = irp; srb->NextSrb = 0; // // Set timeout to 5 seconds. // srb->TimeOutValue = 5; srb->CdbLength = 6; srb->SenseInfoBufferLength = 0; srb->SenseInfoBuffer = 0; srb->DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress); srb->DataTransferLength = INQUIRYDATABUFFERSIZE; cdb = (PCDB)srb->Cdb; // // Set CDB operation code. // cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; // // Set CDB LUN. // cdb->CDB6INQUIRY.LogicalUnitNumber = LunInfo->Lun; cdb->CDB6INQUIRY.Reserved1 = 0; // // Set allocation length to inquiry data buffer size. // cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE; // // Zero reserve field and // Set EVPD Page Code to zero. // Set Control field to zero. // (See SCSI-II Specification.) // cdb->CDB6INQUIRY.PageCode = 0; cdb->CDB6INQUIRY.IReserved = 0; cdb->CDB6INQUIRY.Control = 0; // // Call port driver to handle this request. // (VOID)IoCallDriver(DeviceExtension->DeviceObject, irp); if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { ScsiDebugPrint(2,"IssueInquiry: Inquiry failed SRB status %x\n", srb->SrbStatus); // // NOTE: if INQUIRY fails with a data underrun, // indicate success and let the class drivers // determine whether the inquiry information // is useful. // if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { // // Copy INQUIRY buffer to LUNINFO. // ScsiDebugPrint(1,"IssueInquiry: Data underrun at TID %d\n", LunInfo->TargetId); RtlMoveMemory(LunInfo->InquiryData, InquiryDataBuffer, INQUIRYDATABUFFERSIZE); status = STATUS_SUCCESS; } else if ((SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT) && (retryCount++ < 2)) { // // If the selection did not time out then retry the request. // ScsiDebugPrint(2,"IssueInquiry: Retry %d\n", retryCount); goto inquiryRetry; } else { status = EIO; } } else { // // Copy INQUIRY buffer to LUNINFO. // RtlMoveMemory(LunInfo->InquiryData, InquiryDataBuffer, INQUIRYDATABUFFERSIZE); status = STATUS_SUCCESS; } return status; } // end IssueInquiry() PSCSI_BUS_SCAN_DATA ScsiBusScan( IN PDEVICE_EXTENSION DeviceExtension, IN UCHAR ScsiBus, IN UCHAR InitiatorBusId ) /*++ Routine Description: Arguments: DeviceExtension ScsiBus Return Value: SCSI configuration information --*/ { PSCSI_BUS_SCAN_DATA busScanData; PLUNINFO lunInfo; UCHAR target; UCHAR device = 0; PLOGICAL_UNIT_EXTENSION nextLogicalUnitExtension; ScsiDebugPrint(3,"ScsiBusScan: Enter routine\n"); busScanData = ExAllocatePool(NonPagedPool, sizeof(SCSI_BUS_SCAN_DATA)); if (busScanData == NULL) { // // Insufficient system resources to complete bus scan. // return NULL; } RtlZeroMemory(busScanData,sizeof(SCSI_BUS_SCAN_DATA)); busScanData->Length = sizeof(SCSI_CONFIGURATION_INFO); // // Create first LUNINFO. // lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO)); if (lunInfo == NULL) { // // Insufficient system resources to complete bus scan. // return NULL; } RtlZeroMemory(lunInfo, sizeof(LUNINFO)); // // Create first logical unit extension. // nextLogicalUnitExtension = CreateLogicalUnitExtension(DeviceExtension); if (nextLogicalUnitExtension == NULL) { return(NULL); } // // Link logical unit extension on list. // nextLogicalUnitExtension->NextLogicalUnit = DeviceExtension->LogicalUnitList; DeviceExtension->LogicalUnitList = nextLogicalUnitExtension; // // Issue inquiry command to each target id to find devices. // // NOTE: Does not handle multiple logical units per target id. // for (target = 8; target > 0; target--) { // // This takes a long time so print a dot so the system doesn't look // hung. // FwPrint("."); if (InitiatorBusId == target-1) { continue; } nextLogicalUnitExtension->PathId = lunInfo->PathId = ScsiBus; nextLogicalUnitExtension->TargetId = lunInfo->TargetId = target-1; nextLogicalUnitExtension->Lun = lunInfo->Lun = 0; // // Rezero hardware logigal unit extension if it's being recycled. // if (DeviceExtension->HwLogicalUnitExtensionSize) { if (nextLogicalUnitExtension->SpecificLuExtension) { RtlZeroMemory(nextLogicalUnitExtension->SpecificLuExtension, DeviceExtension->HwLogicalUnitExtensionSize); } } // // Issue inquiry command. // ScsiDebugPrint(3,"ScsiBusScan: LunInfo at %lx\n", lunInfo); ScsiDebugPrint(3, "ScsiBusScan: Device extension %lx\n", DeviceExtension); ScsiDebugPrint(2,"ScsiBusScan: Try TargetId %d LUN 0\n", target-1); if (IssueInquiry(DeviceExtension, lunInfo) == ESUCCESS) { ScsiDebugPrint(1,"ScsiBusScan: Found Device %d", device); ScsiDebugPrint(1," at Target Id %d", lunInfo->TargetId); ScsiDebugPrint(1," LUN %d\n", lunInfo->Lun); // // Link LUN information on list. // lunInfo->NextLunInfo = busScanData->LunInfoList; busScanData->LunInfoList = lunInfo; // // This buffer is used. Get another. // lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO)); if (lunInfo == NULL) { // // Insufficient system resources to complete bus scan. // return busScanData; } RtlZeroMemory(lunInfo, sizeof(LUNINFO)); // // Current logical unit extension claimed. // Create next logical unit. // nextLogicalUnitExtension = CreateLogicalUnitExtension(DeviceExtension); if (nextLogicalUnitExtension == NULL) { return busScanData; } // // Link logical unit extension on list. // nextLogicalUnitExtension->NextLogicalUnit = DeviceExtension->LogicalUnitList; DeviceExtension->LogicalUnitList = nextLogicalUnitExtension; device++; } } // end for (target ... // // Remove unused logicalunit extension from list. // DeviceExtension->LogicalUnitList = DeviceExtension->LogicalUnitList->NextLogicalUnit; ExFreePool(nextLogicalUnitExtension); ExFreePool(lunInfo); busScanData->NumberOfLogicalUnits = device; ScsiDebugPrint(1, "ScsiBusScan: Found %d devices on SCSI bus %d\n", device, ScsiBus); return busScanData; } // end ScsiBusScan() PLOGICAL_UNIT_EXTENSION CreateLogicalUnitExtension( IN PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: Create logical unit extension. Arguments: DeviceExtension PathId Return Value: Logical unit extension --*/ { PLOGICAL_UNIT_EXTENSION logicalUnitExtension; // // Create logical unit extension and link in chain. // logicalUnitExtension = ExAllocatePool(NonPagedPool, sizeof(LOGICAL_UNIT_EXTENSION)); if (logicalUnitExtension == NULL) { return(NULL); } // // Zero logical unit extension. // RtlZeroMemory(logicalUnitExtension, sizeof(LOGICAL_UNIT_EXTENSION)); // // Allocate miniport driver logical unit extension if necessary. // if (DeviceExtension->HwLogicalUnitExtensionSize) { logicalUnitExtension->SpecificLuExtension = ExAllocatePool(NonPagedPool, DeviceExtension->HwLogicalUnitExtensionSize); if (logicalUnitExtension->SpecificLuExtension == NULL) { return(NULL); } // // Zero hardware logical unit extension. // RtlZeroMemory(logicalUnitExtension->SpecificLuExtension, DeviceExtension->HwLogicalUnitExtensionSize); } // // Set timer counters in LogicalUnits to -1 to indicate no // outstanding requests. // logicalUnitExtension->RequestTimeoutCounter = -1; // // Clear the current request field. // logicalUnitExtension->CurrentRequest = NULL; return logicalUnitExtension; } // end CreateLogicalUnitExtension() // // Routines providing service to hardware dependent driver. // SCSI_PHYSICAL_ADDRESS ScsiPortGetPhysicalAddress( IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID VirtualAddress, OUT ULONG *Length ) /*++ Routine Description: Arguments: Return Value: --*/ { PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; PSRB_SCATTER_GATHER scatterList; PIRP irp; PMDL mdl; ULONG byteOffset; ULONG whichPage; PULONG pages; ULONG address; if (Srb == NULL) { if (deviceExtension->SrbExtensionZonePool) { address = (PUCHAR) VirtualAddress - (PUCHAR) deviceExtension->SrbExtensionZonePool + deviceExtension->PhysicalZoneBase; } else { address = MmGetPhysicalAddress(VirtualAddress).LowPart; } // // Return the requested length. // } else if (deviceExtension->MasterWithAdapter) { // // A scatter/gather list has already been allocated use it to determine // the physical address and length. Get the scatter/gather list. // scatterList = GetLogicalUnitExtension(deviceExtension, Srb->TargetId) ->ScatterGather; // // Calculate byte offset into the data buffer. // byteOffset = (PCHAR) VirtualAddress - (PCHAR) Srb->DataBuffer; // // Find the appropirate 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; return(ScsiPortConvertUlongToPhysicalAddress(scatterList->PhysicalAddress + byteOffset)); } else { // // Get IRP from SRB. // irp = Srb->OriginalRequest; // // Get MDL from IRP. // mdl = irp->MdlAddress; // // Calculate byte offset from // beginning of first physical page. // byteOffset = (PCHAR)VirtualAddress - (PCHAR)mdl->StartVa; // // Calculate which physical page. // whichPage = byteOffset >> PAGE_SHIFT; // // Calculate beginning of physical page array. // pages = (PULONG)(mdl + 1); // // Calculate physical address. // address = (pages[whichPage] << PAGE_SHIFT) + BYTE_OFFSET(VirtualAddress); // // Assume the buffer is contiguous. Just return the requested length. // } return ScsiPortConvertUlongToPhysicalAddress(address); } // end ScsiPortGetPhysicalAddress() PVOID ScsiPortGetVirtualAddress( IN PVOID HwDeviceExtension, IN SCSI_PHYSICAL_ADDRESS PhysicalAddress ) /*++ Routine Description: This routine is returns a virtual address associated with a physical address, if the physical address was obtained by a call to ScsiPortGetPhysicalAddress. Arguments: PhysicalAddress Return Value: Virtual address if physical page hashed. NULL if physical page not found in hash. --*/ { PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; PVOID address; address = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress) - deviceExtension->PhysicalZoneBase + (PUCHAR)deviceExtension->SrbExtensionZonePool; return address; } // end ScsiPortGetVirtualAddress() PVOID ScsiPortGetLogicalUnit( IN PVOID HwDeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun ) /*++ Routine Description: Walk port driver's logical unit extension list searching for entry. Arguments: HwDeviceExtension - The port driver's device extension follows the miniport's device extension and contains a pointer to the logical device extension list. PathId, TargetId and Lun - identify which logical unit on the SCSI buses. Return Value: If entry found return miniport driver's logical unit extension. Else, return NULL. --*/ { PDEVICE_EXTENSION deviceExtension; PLOGICAL_UNIT_EXTENSION logicalUnit; ScsiDebugPrint(3, "ScsiPortGetLogicalUnit: TargetId %d\n", TargetId); // // Get pointer to port driver device extension. // deviceExtension = (PDEVICE_EXTENSION)HwDeviceExtension -1; // // Get pointer to logical unit list. // logicalUnit = deviceExtension->LogicalUnitList; // // Walk list looking at target id for requested logical unit extension. // while (logicalUnit != NULL) { if ((logicalUnit->TargetId == TargetId) && (logicalUnit->PathId == PathId) && (logicalUnit->Lun == Lun)) { // // Logical unit extension found. // Return specific logical unit extension. // return logicalUnit->SpecificLuExtension; } // // Get next logical unit. // logicalUnit = logicalUnit->NextLogicalUnit; } // // Requested logical unit extension not found. // return NULL; } // end ScsiPortGetLogicalUnit() VOID ScsiPortNotification( IN SCSI_NOTIFICATION_TYPE NotificationType, IN PVOID HwDeviceExtension, ... ) /*++ Routine Description: Arguments: Return Value: --*/ { PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) HwDeviceExtension - 1; PIO_STACK_LOCATION irpstack; PLOGICAL_UNIT_EXTENSION logicalUnit; PSCSI_REQUEST_BLOCK srb; va_list(ap); va_start(ap, HwDeviceExtension); switch (NotificationType) { case NextLuRequest: case NextRequest: // // Start next packet on adapter's queue. // ScsiDebugPrint(3, "ScsiPortNotification: Start next request\n"); deviceExtension->InterruptFlags |= PD_READY_FOR_NEXT_REQUEST; break; case RequestComplete: ScsiDebugPrint(3, "ScsiPortNotification: Request complete\n"); srb = va_arg(ap, PSCSI_REQUEST_BLOCK); if (srb->SrbStatus == SRB_STATUS_ERROR) { } // // Link the completed request into a forward-linked list of IRPs. // irpstack = IoGetCurrentIrpStackLocation( ((PIRP) srb->OriginalRequest) ); irpstack->Parameters.Others.Argument3 = deviceExtension->CompletedRequests; deviceExtension->CompletedRequests = srb->OriginalRequest; // // Set logical unit current request to NULL // to prevent race condition. // logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId); logicalUnit->CurrentRequest = NULL; break; case ResetDetected: break; case CallDisableInterrupts: ASSERT(deviceExtension->Flags & PD_DISABLE_INTERRUPTS); // // The mini-port 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: ASSERT(!(deviceExtension->Flags & PD_DISABLE_INTERRUPTS)); // // The mini-port 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->Flags |= PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST; deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT); break; case RequestTimerCall: deviceExtension->HwTimerRequest = va_arg(ap, PHW_INTERRUPT); deviceExtension->TimerValue = (va_arg(ap, ULONG) + PD_INTERLOOP_STALL) / PD_INTERLOOP_STALL; break; } va_end(ap); // // Check to see if the last DPC has been processed yet. If so // queue another DPC. // ScsiPortCompletionDpc( NULL, // Dpc deviceExtension->DeviceObject, // DeviceObject NULL, // Irp NULL // Context ); } // end ScsiPortNotification() VOID ScsiPortFlushDma( IN PVOID HwDeviceExtension ) /*++ Routine Description: This routine checks to see if the perivious IoMapTransfer has been done started. If it has not, then the PD_MAP_TRANSER flag is cleared, and the routine returns; otherwise, this routine schedules a DPC which will call IoFlushAdapter buffers. Arguments: HwDeviceExtension - Supplies a the hardware device extension for the host bus adapter which will be doing the data transfer. Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension; deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) { // // The transfer has not been started so just clear the map transfer // flag and return. // deviceExtension->InterruptFlags &= ~PD_MAP_TRANSFER; return; } deviceExtension->InterruptFlags |= PD_FLUSH_ADAPTER_BUFFERS; // // Check to see if the last DPC has been processed yet. If so // queue another DPC. // ScsiPortCompletionDpc( NULL, // Dpc deviceExtension->DeviceObject, // DeviceObject NULL, // Irp NULL // Context ); return; } 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. --*/ { PDEVICE_EXTENSION deviceExtension; deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; // // Make sure this host bus adapter has an Dma adapter object. // if (deviceExtension->DmaAdapterObject == NULL) { // // No DMA adapter, no work. // return; } deviceExtension->MapTransferParameters.Srb = Srb; deviceExtension->MapTransferParameters.LogicalAddress = LogicalAddress; deviceExtension->MapTransferParameters.Length = Length; deviceExtension->InterruptFlags |= PD_MAP_TRANSFER; // // Check to see if the last DPC has been processed yet. If so // queue another DPC. // ScsiPortCompletionDpc( NULL, // Dpc deviceExtension->DeviceObject, // DeviceObject NULL, // Irp NULL // Context ); } // end ScsiPortIoMapTransfer() VOID IssueRequestSense( IN PDEVICE_EXTENSION deviceExtension, 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 device extension for this SCSI port. 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; ScsiDebugPrint(3,"IssueRequestSense: Enter routine\n"); // // Allocate Srb from non-paged pool // plus room for a pointer to the failing IRP. // Since this routine is in an error-handling // path and a shortterm allocation // NonPagedMustSucceed is requested. // Srb = &RequestSenseSrb.Srb; // // Allocate an IRP to issue the REQUEST SENSE request. // Irp = InitializeIrp( &RequestSenseSrb, IRP_MJ_READ, deviceExtension->DeviceObject, FailingSrb->SenseInfoBuffer, FailingSrb->SenseInfoBufferLength ); irpstack = IoGetNextIrpStackLocation(Irp); irpstack->MajorFunction = IRP_MJ_SCSI; // // 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; // // Save SRB address in next stack for port driver. // irpstack->Parameters.Others.Argument1 = (PVOID)Srb; // // Set up IRP Address. // Srb->OriginalRequest = Irp; Srb->NextSrb = 0; // // Set up SCSI bus address. // Srb->TargetId = FailingSrb->TargetId; Srb->Lun = FailingSrb->Lun; Srb->PathId = FailingSrb->PathId; Srb->Length = sizeof(SCSI_REQUEST_BLOCK); Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; // // Set timeout value to 2 seconds. // Srb->TimeOutValue = 2; // // 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 a speical flags to indicate the logical unit queue should be by // passed and that no queue processing should be done when the request // completes. // Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT; Srb->DataBuffer = FailingSrb->SenseInfoBuffer; // // Set the transfer length. // Srb->DataTransferLength = FailingSrb->SenseInfoBufferLength; // // Zero out status. // Srb->ScsiStatus = Srb->SrbStatus = 0; (VOID)IoCallDriver(deviceExtension->DeviceObject, Irp); ScsiPortInternalCompletion(deviceExtension->DeviceObject, Irp, Srb); return; } // end IssueRequestSense() VOID ScsiPortInternalCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) /*++ Routine Description: Arguments: Device object IRP Context - pointer to SRB Return Value: None. --*/ { PSCSI_REQUEST_BLOCK srb = Context; PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK failingSrb; PIRP failingIrp; UNREFERENCED_PARAMETER(DeviceObject); ScsiDebugPrint(3,"ScsiPortInternalCompletion: Enter routine\n"); // // 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. // if ((SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) || (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)) { // // Get a pointer to failing Irp and Srb. // failingSrb = *((PVOID *) (srb+1)); failingIrp = failingSrb->OriginalRequest; // // 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; } } // ScsiPortInternalCompletion() VOID ScsiPortTickHandler( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: Arguments: Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; PLOGICAL_UNIT_EXTENSION logicalUnit; UNREFERENCED_PARAMETER(Context); logicalUnit = deviceExtension->LogicalUnitList; // // NOTE: The use of Current request needs to be synchronized with the // clearing of current request. // while (logicalUnit != NULL) { // // Check for busy requests. // if (logicalUnit->Flags & PD_LOGICAL_UNIT_IS_BUSY) { ScsiDebugPrint(1,"ScsiPortTickHandler: Retrying busy status request\n"); // // Clear the busy flag and retry the request. // logicalUnit->Flags &= ~PD_LOGICAL_UNIT_IS_BUSY; ScsiPortStartIo(DeviceObject, logicalUnit->CurrentRequest); } else if (logicalUnit->RequestTimeoutCounter == 0) { // // Request timed out. // ScsiDebugPrint(1, "ScsiPortTickHandler: Request timed out\n"); // // Reset request timeout counter to unused state. // logicalUnit->RequestTimeoutCounter = -1; // // Build and send request to abort command. // IssueAbortRequest(deviceExtension, logicalUnit->CurrentRequest); } else if (logicalUnit->RequestTimeoutCounter != -1) { ScsiDebugPrint(1, "ScsiPortTickHandler: Timeout value %lx\n",logicalUnit->RequestTimeoutCounter); logicalUnit->RequestTimeoutCounter--; } logicalUnit = logicalUnit->NextLogicalUnit; } return; } // end ScsiPortTickHandler() VOID IssueAbortRequest( IN PDEVICE_EXTENSION DeviceExtension, IN PIRP FailingIrp ) /*++ Routine Description: A request timed out and to clear the request at the HBA an ABORT request is issued. But first, if the request that timed out was an ABORT command, then reset the adapter instead. Arguments: DeviceExension - Supplies a pointer to the device extension for this SCSI port. FailingIrp - Supplies a pointer to the request that is to be aborted. Return Value: None. --*/ { ULONG j; ScsiDebugPrint(3,"IssueAbortRequest: Enter routine\n"); // // A request to abort failed. // Need to reset the adapter. // ScsiDebugPrint(1,"IssueAbort: Request timed out resetting the bus.\n"); if (!DeviceExtension->HwReset( DeviceExtension->HwDeviceExtension, 0)){ ScsiDebugPrint(1,"Reset SCSI bus failed\n"); } // // Call the interupt handler for a few microseconds to clear any reset // interrupts. // for (j = 0; j < 1000 * 100; j++) { FwStallExecution(10); if (DeviceExtension->HwInterrupt != NULL) { DeviceExtension->HwInterrupt(DeviceExtension->HwDeviceExtension); } } // // Wait 2 seconds for the devices to recover after the reset. // //FwStallExecution(2 * 1000 * 1000); FwStallExecution(2 * 1000 * 100); return; } // end IssueAbortRequest() BOOLEAN SpGetInterruptState( IN PVOID ServiceContext ) /*++ Routine Description: This routine saves the InterruptFlags, MapTransferParameters and CompletedRequests fields and clears the InterruptFlags. Arguments: ServiceContext - Supplies a pointer to the device extension for this SCSI port. Return Value: Always returns TRUE. Notes: Called via KeSynchronizeExecution. --*/ { PDEVICE_EXTENSION deviceExtension = ServiceContext; // // Move the interrupt state to save area. // deviceExtension->InterruptFlags = deviceExtension->InterruptFlags; deviceExtension->CompletedRequests = deviceExtension->CompletedRequests; deviceExtension->MapTransferParameters = deviceExtension->MapTransferParameters; // // Clear the interrupt state. // deviceExtension->InterruptFlags = 0; deviceExtension->CompletedRequests = NULL; return(TRUE); } 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 allocates an error log entry, copies the supplied text to it, and requests that it be written to the error log file. Arguments: DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage. 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. --*/ { PCHAR errorCodeString; switch (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((0,"\n\nLogErrorEntry: Logging SCSI error packet. ErrorCode = %s.\n", errorCodeString )); DebugPrint((0, "PathId = %2x, TargetId = %2x, Lun = %2x, UniqueId = %x.\n\n", PathId, TargetId, Lun, UniqueId )); return; } // end ScsiPortLogError() VOID ScsiPortCompleteRequest( IN PVOID HwDeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN UCHAR SrbStatus ) /*++ Routine Description: Complete all active requests for the specified logical unit. Arguments: DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage. TargetId, Lun and PathId - specify device address on a SCSI bus. SrbStatus - Status to be returned in each completed SRB. Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; PSCSI_REQUEST_BLOCK Srb; PSCSI_REQUEST_BLOCK failingSrb; PLOGICAL_UNIT_EXTENSION luExtension; PIRP nextIrp; PIO_STACK_LOCATION irpstack; UNREFERENCED_PARAMETER(PathId); UNREFERENCED_PARAMETER(Lun); if (TargetId == (UCHAR)(-1)) { // // Complete requests for all units on this bus. // luExtension = deviceExtension->LogicalUnitList; while (luExtension != NULL) { ScsiDebugPrint(2, "ScsiPortCompleteRequest: Complete requests for targetid %d\n", luExtension->TargetId); // // Complete requests until queue is empty. // if ((nextIrp = luExtension->CurrentRequest) != NULL && !(luExtension->Flags & PD_LOGICAL_UNIT_IS_BUSY)) { ScsiDebugPrint(3,"ScsiPortCompleteRequest: Current request %lx\n", nextIrp); // // Get SRB address from current IRP stack. // irpstack = IoGetCurrentIrpStackLocation(nextIrp); Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; // // Just in case this is an abort request, // get pointer to failingSrb. // failingSrb = Srb->NextSrb; // // Update SRB status. // Srb->SrbStatus = SrbStatus; // // Indicate no bytes transferred. // Srb->DataTransferLength = 0; // // Set IRP status. // nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; // // Move bytes transferred to IRP. // nextIrp->IoStatus.Information = Srb->DataTransferLength; // // Call notification routine. // ScsiPortNotification(RequestComplete, (PVOID)HwDeviceExtension, Srb); if (failingSrb) { // // This was an abort request. The failing // SRB must also be completed. // failingSrb->SrbStatus = SrbStatus; failingSrb->DataTransferLength = 0; // // Get IRP from SRB. // nextIrp = failingSrb->OriginalRequest; // // Set IRP status. // nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; // // Move bytes transferred to IRP. // nextIrp->IoStatus.Information = failingSrb->DataTransferLength; // // Call notification routine. // ScsiPortNotification(RequestComplete, (PVOID)HwDeviceExtension, failingSrb); } } // end if luExtension = luExtension->NextLogicalUnit; } // end while } else { // // Complete all requests for this logical unit. // ScsiDebugPrint(2, "ScsiPortCompleteRequest: Complete requests for targetid %d\n", TargetId); luExtension = GetLogicalUnitExtension(deviceExtension, TargetId); // // Complete requests until queue is empty. // if ((nextIrp = luExtension->CurrentRequest) != NULL) { ScsiDebugPrint(3,"ScsiPortCompleteRequest: Current request %lx\n", nextIrp); // // Get SRB address from current IRP stack. // irpstack = IoGetCurrentIrpStackLocation(nextIrp); Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; // // Update SRB status. // Srb->SrbStatus = SrbStatus; // // Indicate no bytes transferred. // Srb->DataTransferLength = 0; // // Set IRP status. // nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; // // Move bytes transferred to IRP. // nextIrp->IoStatus.Information = Srb->DataTransferLength; // // Call notification routine. // ScsiPortNotification(RequestComplete, (PVOID)HwDeviceExtension, Srb); } // end while } // end if ... else return; } // end ScsiPortCompleteRequest() VOID ScsiPortMoveMemory( IN PVOID WriteBuffer, IN PVOID ReadBuffer, IN ULONG Length ) /*++ Routine Description: Copy from one buffer into another. Arguments: ReadBuffer - source WriteBuffer - destination Length - number of bytes to copy Return Value: None. --*/ { RtlMoveMemory(WriteBuffer, ReadBuffer, Length); } // end ScsiPortMoveMemory() VOID ScsiPortStallExecution( ULONG Delay ) /*++ Routine Description: Wait number of microseconds in tight processor loop. Arguments: Delay - number of microseconds to wait. Return Value: None. --*/ { FwStallExecution(Delay); } // end ScsiPortStallExecution() PLOGICAL_UNIT_EXTENSION GetLogicalUnitExtension( PDEVICE_EXTENSION deviceExtension, UCHAR TargetId ) /*++ Routine Description: Walk logical unit extension list looking for extension with matching target id. Arguments: deviceExtension TargetId Return Value: Requested logical unit extension if found, else NULL. --*/ { PLOGICAL_UNIT_EXTENSION logicalUnit = deviceExtension->LogicalUnitList; while (logicalUnit != NULL) { if (logicalUnit->TargetId == TargetId) { return logicalUnit; } logicalUnit = logicalUnit->NextLogicalUnit; } // // Logical unit extension not found. // return (PLOGICAL_UNIT_EXTENSION)NULL; } // end GetLogicalUnitExtension() VOID ScsiDebugPrint( ULONG DebugPrintLevel, PCCHAR DebugMessage, ... ) /*++ Routine Description: Debug print for all SCSI drivers Arguments: Debug print level between 0 and 3, with 3 being the most verbose. Return Value: None --*/ { va_list ap; va_start( ap, DebugMessage ); if (DebugPrintLevel <= ScsiDebug) { char buffer[128]; vsprintf(buffer, DebugMessage, ap); DbgPrint(buffer); DbgPrint("\r"); } va_end(ap); } UCHAR ScsiPortReadPortUchar( IN PUCHAR Port ) /*++ Routine Description: Read from the specificed port address. Arguments: Port - Supplies a pointer to the port address. Return Value: Returns the value read from the specified port address. --*/ { #ifdef MIPS return(READ_REGISTER_UCHAR(Port)); #else return(READ_PORT_UCHAR(Port)); #endif } USHORT ScsiPortReadPortUshort( IN PUSHORT Port ) /*++ Routine Description: Read from the specificed port address. Arguments: Port - Supplies a pointer to the port address. Return Value: Returns the value read from the specified port address. --*/ { #ifdef MIPS return(READ_REGISTER_USHORT(Port)); #else return(READ_PORT_USHORT(Port)); #endif } ULONG ScsiPortReadPortUlong( IN PULONG Port ) /*++ Routine Description: Read from the specificed port address. Arguments: Port - Supplies a pointer to the port address. Return Value: Returns the value read from the specified port address. --*/ { #ifdef MIPS return(READ_REGISTER_ULONG(Port)); #else return(READ_PORT_ULONG(Port)); #endif } UCHAR ScsiPortReadRegisterUchar( IN PUCHAR Register ) /*++ Routine Description: Read from the specificed register address. Arguments: Register - Supplies a pointer to the register address. Return Value: Returns the value read from the specified register address. --*/ { return(READ_REGISTER_UCHAR(Register)); } USHORT ScsiPortReadRegisterUshort( IN PUSHORT Register ) /*++ Routine Description: Read from the specificed register address. Arguments: Register - Supplies a pointer to the register address. Return Value: Returns the value read from the specified register address. --*/ { return(READ_REGISTER_USHORT(Register)); } ULONG ScsiPortReadRegisterUlong( IN PULONG Register ) /*++ Routine Description: Read from the specificed register address. Arguments: Register - Supplies a pointer to the register address. Return Value: Returns the value read from the specified register address. --*/ { return(READ_REGISTER_ULONG(Register)); } VOID ScsiPortReadRegisterBufferUchar( IN PUCHAR Register, IN PUCHAR Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned bytes from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_REGISTER_BUFFER_UCHAR(Register, Buffer, Count); } VOID ScsiPortReadRegisterBufferUshort( IN PUSHORT Register, IN PUSHORT Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned shorts from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_REGISTER_BUFFER_USHORT(Register, Buffer, Count); } VOID ScsiPortReadRegisterBufferUlong( IN PULONG Register, IN PULONG Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned longs from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_REGISTER_BUFFER_ULONG(Register, Buffer, Count); } VOID ScsiPortWritePortUchar( IN PUCHAR Port, IN UCHAR Value ) /*++ Routine Description: Write to the specificed port address. Arguments: Port - Supplies a pointer to the port address. Value - Supplies the value to be written. Return Value: None --*/ { #ifdef MIPS WRITE_REGISTER_UCHAR(Port, Value); #else WRITE_PORT_UCHAR(Port, Value); #endif } VOID ScsiPortWritePortUshort( IN PUSHORT Port, IN USHORT Value ) /*++ Routine Description: Write to the specificed port address. Arguments: Port - Supplies a pointer to the port address. Value - Supplies the value to be written. Return Value: None --*/ { #ifdef MIPS WRITE_REGISTER_USHORT(Port, Value); #else WRITE_PORT_USHORT(Port, Value); #endif } VOID ScsiPortWritePortUlong( IN PULONG Port, IN ULONG Value ) /*++ Routine Description: Write to the specificed port address. Arguments: Port - Supplies a pointer to the port address. Value - Supplies the value to be written. Return Value: None --*/ { #ifdef MIPS WRITE_REGISTER_ULONG(Port, Value); #else WRITE_PORT_ULONG(Port, Value); #endif } VOID ScsiPortWriteRegisterUchar( IN PUCHAR Register, IN UCHAR Value ) /*++ Routine Description: Write to the specificed register address. Arguments: Register - Supplies a pointer to the register address. Value - Supplies the value to be written. Return Value: None --*/ { WRITE_REGISTER_UCHAR(Register, Value); } VOID ScsiPortWriteRegisterUshort( IN PUSHORT Register, IN USHORT Value ) /*++ Routine Description: Write to the specificed register address. Arguments: Register - Supplies a pointer to the register address. Value - Supplies the value to be written. Return Value: None --*/ { WRITE_REGISTER_USHORT(Register, Value); } VOID ScsiPortWriteRegisterUlong( IN PULONG Register, IN ULONG Value ) /*++ Routine Description: Write to the specificed register address. Arguments: Register - Supplies a pointer to the register address. Value - Supplies the value to be written. Return Value: None --*/ { WRITE_REGISTER_ULONG(Register, Value); } VOID ScsiPortWriteRegisterBufferUchar( IN PUCHAR Register, IN PUCHAR Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned bytes from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_REGISTER_BUFFER_UCHAR(Register, Buffer, Count); } VOID ScsiPortWriteRegisterBufferUshort( IN PUSHORT Register, IN PUSHORT Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned shorts from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_REGISTER_BUFFER_USHORT(Register, Buffer, Count); } VOID ScsiPortWriteRegisterBufferUlong( IN PULONG Register, IN PULONG Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned longs from the specified register address. Arguments: Register - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_REGISTER_BUFFER_ULONG(Register, Buffer, Count); } SCSI_PHYSICAL_ADDRESS ScsiPortConvertUlongToPhysicalAddress( ULONG UlongAddress ) { SCSI_PHYSICAL_ADDRESS physicalAddress; physicalAddress.HighPart = 0; physicalAddress.LowPart = UlongAddress; return(physicalAddress); } #undef ScsiPortConvertPhysicalAddressToUlong ULONG ScsiPortConvertPhysicalAddressToUlong( SCSI_PHYSICAL_ADDRESS Address ) { return(Address.LowPart); } PIRP InitializeIrp( PFULL_SCSI_REQUEST_BLOCK FullSrb, CCHAR MajorFunction, PVOID DeviceObject, PVOID Buffer, ULONG Length ) /*++ Routine Description: This funcition builds an IRP for use by the SCSI port driver and builds a MDL list. Arguments: FullSrb - Supplies a pointer to the full srb structure which contains the Irp and Mdl. MajorFunction - Supplies the major function code to initialize the Irp entry. DeviceObject - Supplies the device Object pointer to initialize the Irp with. Buffer - Supplies the virual address of the buffer for which the Mdl should be built. Length - Supplies the size of buffer for which the Mdl should be built. Return Value: Returns a pointer to the initialized IRP. --*/ { PIRP irp; PMDL mdl; PULONG pageFrame; ULONG frameNumber; ULONG index; ULONG numberOfPages; irp = &FullSrb->Irp; mdl = &FullSrb->Mdl; irp->Tail.Overlay.CurrentStackLocation = &FullSrb->IrpStack[IRP_STACK_SIZE]; if (Buffer != NULL && Length != 0) { // // Build the memory descriptor list. // irp->MdlAddress = mdl; mdl->Next = NULL; mdl->Size = sizeof(MDL) + ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length) * sizeof(ULONG); mdl->StartVa = (PVOID)PAGE_ALIGN(Buffer); mdl->ByteCount = Length; mdl->ByteOffset = BYTE_OFFSET(Buffer); mdl->MappedSystemVa = Buffer; mdl->MdlFlags = MDL_MAPPED_TO_SYSTEM_VA; pageFrame = (PULONG)(mdl + 1); frameNumber = RtlLargeIntegerShiftRight( MmGetPhysicalAddress(mdl->StartVa), PAGE_SHIFT).LowPart; numberOfPages = (mdl->ByteCount + mdl->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT; for (index = 0; index < numberOfPages; index += 1) { *pageFrame++ = frameNumber++; } } else { irp->MdlAddress = NULL; } return(irp); } PVOID ScsiPortGetDeviceBase( IN PVOID HwDeviceExtension, IN INTERFACE_TYPE BusType, IN ULONG SystemIoBusNumber, SCSI_PHYSICAL_ADDRESS IoAddress, ULONG NumberOfBytes, BOOLEAN InMemorySpace ) /*++ Routine Description: This routine maps an IO address to system address space. Use ScsiPortFreeDeviceBase to unmap address. Arguments: HwDeviceExtension - used to find port device extension. BusType - what type of bus - eisa, mca, isa SystemIoBusNumber - which IO bus (for machines with multiple buses). IoAddress - base device address to be mapped. NumberOfBytes - number of bytes for which address is valid. Return Value: Mapped address --*/ { PHYSICAL_ADDRESS cardAddress; ULONG addressSpace = InMemorySpace; PVOID mappedAddress; HalTranslateBusAddress( BusType, // AdapterInterfaceType SystemIoBusNumber, // SystemIoBusNumber IoAddress, // Bus Address &addressSpace, // AddressSpace &cardAddress // Translated address ); // // Map the device base address into the virtual address space // if the address is in memory space. // if (!addressSpace) { mappedAddress = MmMapIoSpace(cardAddress, NumberOfBytes, FALSE); } else { mappedAddress = (PVOID)cardAddress.LowPart; } return mappedAddress; } // end ScsiPortGetDeviceBase() VOID ScsiPortFreeDeviceBase( IN PVOID HwDeviceExtension, IN PVOID MappedAddress ) /*++ Routine Description: This routine unmaps an IO address that has been previously mapped to system address space using ScsiPortGetDeviceBase(). Arguments: HwDeviceExtension - used to find port device extension. MappedAddress - address to unmap. NumberOfBytes - number of bytes mapped. InIoSpace - addresses in IO space don't get mapped. Return Value: None --*/ { UNREFERENCED_PARAMETER(HwDeviceExtension); UNREFERENCED_PARAMETER(MappedAddress); return; } // end ScsiPortFreeDeviceBase() ARC_STATUS GetAdapterCapabilities( IN PDEVICE_OBJECT PortDeviceObject, OUT PIO_SCSI_CAPABILITIES *PortCapabilities ) /*++ Routine Description: Arguments: Return Value: Status is returned. --*/ { *PortCapabilities = &((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension) ->Capabilities; return(ESUCCESS); } // end GetAdapterCapabilities() ARC_STATUS GetInquiryData( IN PDEVICE_OBJECT PortDeviceObject, OUT PSCSI_CONFIGURATION_INFO *ConfigInfo ) /*++ Routine Description: This routine sends a request to a port driver to return configuration information. Arguments: The address of the configuration information is returned in the formal parameter ConfigInfo. Return Value: Status is returned. --*/ { *ConfigInfo = ((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension) ->ScsiInfo; return(ESUCCESS); } // end GetInquiryData() NTSTATUS SpInitializeConfiguration( IN PDEVICE_EXTENSION DeviceExtension, IN PHW_INITIALIZATION_DATA HwInitData, OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN BOOLEAN InitialCall ) /*++ Routine Description: This routine initializes the port configuration information structure. Any necessary information is extracted from the registery. Arguments: DeviceExtension - Supplies the device extension. HwInitializationData - Supplies the initial miniport data. ConfigInfo - Supplies the configuration information to be initialized. InitialCall - Indicates that this is first call to this function. If InitialCall is FALSE, then the perivous configuration information is used to determine the new information. Return Value: Returns a status indicating the success or fail of the initializaiton. --*/ { #ifdef i386 extern ULONG MachineType; #endif ULONG j; // // If this is the initial call then zero the information and set // the structure to the uninitialized values. // if (InitialCall) { RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION)); ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION); ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType; ConfigInfo->InterruptMode = Latched; ConfigInfo->MaximumTransferLength = 0xffffffff; ConfigInfo->NumberOfPhysicalBreaks = 0xffffffff; ConfigInfo->DmaChannel = 0xffffffff; ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges; #ifdef MIPS { PCONFIGURATION_COMPONENT Component; PCM_SCSI_DEVICE_DATA ScsiDeviceData; UCHAR Buffer[sizeof(CM_PARTIAL_RESOURCE_LIST) + (sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 5) + sizeof(CM_SCSI_DEVICE_DATA)]; PCM_PARTIAL_RESOURCE_LIST Descriptor = (PCM_PARTIAL_RESOURCE_LIST)&Buffer; ULONG Count; ULONG ScsiHostId; UCHAR ScsiComponentName[8]; // // Build the string scsi(n) to get the Arc component info // for the current scsi // sprintf(ScsiComponentName,"scsi(%d)",ScsiPortCount); if (((Component = ArcGetComponent(ScsiComponentName)) != NULL) && (Component->Class == AdapterClass) && (Component->Type == ScsiAdapter) && (ArcGetConfigurationData((PVOID)Descriptor, Component) == ESUCCESS) && ((Count = Descriptor->Count) < 6)) { ScsiDeviceData = (PCM_SCSI_DEVICE_DATA)&Descriptor->PartialDescriptors[Count]; if (ScsiDeviceData->HostIdentifier > 7) { ScsiHostId = 7; } else { ScsiHostId = ScsiDeviceData->HostIdentifier; } } else { ScsiHostId = 7; } for (j = 0; j < 8; j++) { ConfigInfo->InitiatorBusId[j] = ScsiHostId; } } #else for (j = 0; j < 8; j++) { ConfigInfo->InitiatorBusId[j] = ~0; } #endif #if i386 switch (HwInitData->AdapterInterfaceType) { case Isa: if ((MachineType & 0xff) == MACHINE_TYPE_ISA) { return(STATUS_SUCCESS); } case Eisa: if ((MachineType & 0xff) == MACHINE_TYPE_EISA) { return(STATUS_SUCCESS); } else { return(STATUS_DEVICE_DOES_NOT_EXIST); } case MicroChannel: if ((MachineType & 0xff) == MACHINE_TYPE_MCA) { return(STATUS_SUCCESS); } else { return(STATUS_DEVICE_DOES_NOT_EXIST); } default: return(STATUS_DEVICE_DOES_NOT_EXIST); } #elif MIPS // // ****** TMP to use the DEMO board only. Eisa can be removed. // if ((HwInitData->AdapterInterfaceType != Internal) && (HwInitData->AdapterInterfaceType != Isa)) { return(STATUS_DEVICE_DOES_NOT_EXIST); } #endif return(STATUS_SUCCESS); } else { return(STATUS_DEVICE_DOES_NOT_EXIST); } } NTSTATUS SpGetCommonBuffer( PDEVICE_EXTENSION DeviceExtension, ULONG NonCachedExtensionSize ) /*++ Routine Description: This routine determines the required size of the common buffer. Allocates the common buffer and finally sets up the srb extension zone. This routine expects that the adapter object has already been allocated. Arguments: DeviceExtension - Supplies a pointer to the device extension. NonCachedExtensionSize - Supplies the size of the noncached device extension for the mini-port driver. Return Value: Returns the status of the allocate operation. --*/ { PVOID buffer; ULONG length; ULONG blockSize; // // Calculate the block size for the zone elements based on the Srb // Extension. // blockSize = DeviceExtension->SrbExtensionSize; // // Last three bits of blocksize must be zero. // Round blocksize up. // blockSize = (blockSize + 7) & ~7; length = NonCachedExtensionSize + blockSize * MINIMUM_SRB_EXTENSIONS; // // Round the length up to a page size, since HalGetCommonBuffer allocates // in pages anyway. // length = ROUND_TO_PAGES(length); // // Allocate one page for noncached deviceextension // and srbextension zoned pool. // buffer = MmAllocateNonCachedMemory(length); if (buffer == NULL) { ScsiDebugPrint(1, "ScsiPortInitialize: Could not allocate page of noncached pool\n"); return ENOMEM; } // // Determine length and starting address of zone. // If noncached device extension required then // subtract size from page leaving rest for zone. // DeviceExtension->NonCachedExtension = (PUCHAR)buffer + length - NonCachedExtensionSize; if (DeviceExtension->SrbExtensionSize) { // // Get block size. // blockSize = DeviceExtension->SrbExtensionSize; // // Record starting virtual address of zone. // DeviceExtension->SrbExtensionZonePool = buffer; DeviceExtension->SrbExtensionPointer = buffer; DeviceExtension->SrbExtensionSize = blockSize; // // Set the IO translation for the common buffer // and store the mapping in PhysicalZoneBase. // IopAllocateCommonBuffer(buffer, length, &DeviceExtension->PhysicalZoneBase ); } else { DeviceExtension->SrbExtensionZonePool = NULL; } return(ESUCCESS); } PVOID ScsiPortGetUncachedExtension( IN PVOID HwDeviceExtension, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN ULONG NumberOfBytes ) /*++ Routine Description: This function allocates a common buffer to be used as the uncached device extension for the mini-port driver. This function will also allocate any required SRB extensions. The DmaAdapter is allocated if it has not been allocated previously. Arguments: DeviceExtension - Supplies a pointer to the mini-ports device extension. ConfigInfo - Supplies a pointer to the partially initialized configuraiton information. This is used to get an DMA adapter object. NumberOfBytes - Supplies the size of the extension which needs to be allocated Return Value: A pointer to the uncached device extension or NULL if the extension could not be allocated or was previously allocated. --*/ { PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; NTSTATUS status; // // Make sure that an common buffer has not already been allocated. // if (deviceExtension->SrbExtensionZonePool != NULL) { return(NULL); } // // Allocate the common buffer. // status = SpGetCommonBuffer( deviceExtension, NumberOfBytes); if (status != ESUCCESS) { return(NULL); } return(deviceExtension->NonCachedExtension); } ULONG ScsiPortGetBusData( IN PVOID DeviceExtension, IN ULONG BusDataType, IN ULONG SystemIoBusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Length ) /*++ Routine Description: The function returns the bus data for an adapter slot or CMOS address. Arguments: BusDataType - Supplies the type of bus. BusNumber - Indicates which bus. Buffer - Supplies the space to store the data. Length - Supplies a count in bytes of the maximum amount to return. Return Value: Returns the amount of data stored into the buffer. --*/ { ULONG DataLength = 0; #ifdef i386 PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceExtension - 1; // // If the length is non-zero, the the requested data. // if (Length != 0) { return( HalGetBusData( BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Length )); } // // Free any previously allocated data. // if (deviceExtension->MapRegisterBase != NULL) { ExFreePool(deviceExtension->MapRegisterBase); } if (BusDataType == EisaConfiguration) { #if 0 // // Deteremine the length to allocate based on the number of functions // for the slot. // Length = HalGetBusData( BusDataType, SystemIoBusNumber, SlotNumber, &slotInformation, sizeof(CM_EISA_SLOT_INFORMATION)); if (Length < sizeof(CM_EISA_SLOT_INFORMATION)) { // // The data is messed up since this should never occur // ScsiDebugPrint(1, "ScsiPortGetBusData: Slot information not returned. Length = %d\n", Length); #if 0 ScsiDebugPrint(1, "ScsiPortGetBusData: Hit any key\n"); if (ScsiDebug >= 1) { while(!GET_KEY()); } #endif return(0); } // // Calculate the required length based on the number of functions. // Length = sizeof(CM_EISA_SLOT_INFORMATION) + (sizeof(CM_EISA_FUNCTION_INFORMATION) * slotInformation.NumberFunctions); #else // // Since the loader does not really support freeing data and the EISA // configuration data can be very large. Hal get bus data has be changed // to accept a length of zero for EIAS configuration data. // DataLength = HalGetBusData( BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Length ); ScsiDebugPrint(1, "ScsiPortGetBusData: Returning data. Length = %d\n", DataLength); #if 0 ScsiDebugPrint(1, "ScsiPortGetBusData: Hit any key\n"); if (ScsiDebug >= 1) { while(!GET_KEY()); } #endif return(DataLength); #endif } else { Length = PAGE_SIZE; } deviceExtension->MapRegisterBase = ExAllocatePool(NonPagedPool, Length); if (deviceExtension->MapRegisterBase == NULL) { ScsiDebugPrint(1, "ScsiPortGetBusData: Memory allocation failed. Length = %d\n", Length); ScsiDebugPrint(1, "ScsiPortGetBusData: Hit any key\n"); if (ScsiDebug >= 1) { while(!GET_KEY()); } return(0); } // // Return the pointer to the mini-port driver. // *((PVOID *)Buffer) = deviceExtension->MapRegisterBase; DataLength = HalGetBusData( BusDataType, SystemIoBusNumber, SlotNumber, deviceExtension->MapRegisterBase, Length ); ScsiDebugPrint(1, "ScsiPortGetBusData: Returning data. Length = %d\n", DataLength); #if 0 ScsiDebugPrint(1, "ScsiPortGetBusData: Hit any key\n"); if (ScsiDebug >= 1) { while(!GET_KEY()); } #endif #endif return(DataLength); } PSCSI_REQUEST_BLOCK ScsiPortGetSrb( IN PVOID HwDeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN LONG QueueTag ) /*++ Routine Description: This routine retrieves an active SRB for a particuliar logical unit. Arguments: HwDeviceExtension PathId, TargetId, Lun - identify logical unit on SCSI bus. QueueTag - -1 indicates request is not tagged. Return Value: SRB, if one exists. Otherwise, NULL. --*/ { PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; PLOGICAL_UNIT_EXTENSION luExtension; PIRP irp; PIO_STACK_LOCATION irpstack; luExtension = GetLogicalUnitExtension(deviceExtension, TargetId); if (luExtension == NULL) { return(NULL); } irp = luExtension->CurrentRequest; irpstack = IoGetCurrentIrpStackLocation(irp); return ((PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1); } // end ScsiPortGetSrb() VOID ScsiPortReadPortBufferUchar( IN PUCHAR Port, IN PUCHAR Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned bytes from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_PORT_BUFFER_UCHAR(Port, Buffer, Count); } VOID ScsiPortReadPortBufferUshort( IN PUSHORT Port, IN PUSHORT Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned shorts from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_PORT_BUFFER_USHORT(Port, Buffer, Count); } VOID ScsiPortReadPortBufferUlong( IN PULONG Port, IN PULONG Buffer, IN ULONG Count ) /*++ Routine Description: Read a buffer of unsigned longs from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { READ_PORT_BUFFER_ULONG(Port, Buffer, Count); } VOID ScsiPortWritePortBufferUchar( IN PUCHAR Port, IN PUCHAR Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned bytes from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_PORT_BUFFER_UCHAR(Port, Buffer, Count); } VOID ScsiPortWritePortBufferUshort( IN PUSHORT Port, IN PUSHORT Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned shorts from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_PORT_BUFFER_USHORT(Port, Buffer, Count); } VOID ScsiPortWritePortBufferUlong( IN PULONG Port, IN PULONG Buffer, IN ULONG Count ) /*++ Routine Description: Write a buffer of unsigned longs from the specified port address. Arguments: Port - Supplies a pointer to the port address. Buffer - Supplies a pointer to the data buffer area. Count - The count of items to move. Return Value: None --*/ { WRITE_PORT_BUFFER_ULONG(Port, Buffer, Count); } #endif /* DECSTATION */