Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2414 lines
56 KiB

/*++
Copyright (C) Microsoft Corporation, 1990 - 1999
Module Name:
port.c
Abstract:
This is the NT SCSI port driver.
Authors:
Mike Glass
Jeff Havens
Environment:
kernel mode only
Notes:
This module is a dll for the kernel.
Revision History:
--*/
#include "port.h"
#if DBG
static const char *__file__ = __FILE__;
#endif
#if SCSIDBG_ENABLED
ULONG ScsiDebug = 0;
ULONG ScsiPortCheckSrbDataHashTable = 1;
#endif
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PscS')
#endif
//
// Routines providing service to hardware dependent driver.
//
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.
--*/
{
PADAPTER_EXTENSION deviceExtension;
PLOGICAL_UNIT_EXTENSION logicalUnit;
DebugPrint((3, "ScsiPortGetLogicalUnit: TargetId %d\n",
TargetId));
//
// Get pointer to port driver device extension.
//
deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
//
// Get a pointer to the logical unit.
//
logicalUnit = GetLogicalUnitExtension(deviceExtension,
PathId,
TargetId,
Lun,
FALSE,
FALSE);
if(logicalUnit != NULL) {
return logicalUnit->HwLogicalUnitExtension;
}
return NULL;
} // end ScsiPortGetLogicalUnit()
BOOLEAN SpLunIoLogActive = TRUE;
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) &&
(IS_READ(srb) || IS_WRITE(srb))) {
ASSERT(srb->DataTransferLength);
}
#endif
//
// Append this request to the LUN's IO history log.
//
if (SpLunIoLogActive == TRUE &&
srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
PSP_LUN_IO_LOG ioLogEntry;
ULONG index;
PLOGICAL_UNIT_EXTENSION luExt = ((PSRB_DATA)(srb->OriginalRequest))->LogicalUnit;
index = luExt->IoLogIndex;
ioLogEntry = &luExt->IoLog[index];
ioLogEntry->TickCount = ((PSRB_DATA)(srb->OriginalRequest))->TickCount;
ioLogEntry->SrbStatus = srb->SrbStatus;
ioLogEntry->ScsiStatus = srb->ScsiStatus;
ioLogEntry->CdbLength = srb->CdbLength;
ioLogEntry->Tag = srb->QueueTag;
ioLogEntry->SenseDataLength = srb->SenseInfoBufferLength;
ioLogEntry->InternalStatus = srb->InternalStatus;
RtlMoveMemory(ioLogEntry->Cdb, srb->Cdb, srb->CdbLength);
if (ioLogEntry->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
RtlMoveMemory(ioLogEntry->SenseData,
srb->SenseInfoBuffer,
(srb->SenseInfoBufferLength <= 18) ?
srb->SenseInfoBufferLength : 18);
}
index++;
if (index == 10) {
index = 0;
}
luExt->IoLogIndex = index;
if (luExt->IoLogEntries < 10) {
luExt->IoLogEntries++;
}
}
if (srb->SrbStatus == SRB_STATUS_BUSY) {
DebugPrint((0, "ScsiPortNotification: lun is busy (srb %p)\n", srb));
}
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 != NULL && 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;
if (SpVerifierActive(deviceExtension)) {
deviceExtension->VerifierExtension->RealHwRequestInterrupt =
va_arg(ap, PHW_INTERRUPT);
deviceExtension->HwRequestInterrupt = SpHwRequestInterruptVrfy;
} else {
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;
if (SpVerifierActive(deviceExtension)) {
deviceExtension->VerifierExtension->RealHwRequestInterrupt =
va_arg(ap, PHW_INTERRUPT);
deviceExtension->HwRequestInterrupt = SpHwRequestInterruptVrfy;
} else {
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 is 0xFF, that means that the WmiEevent is from the
// adapter, no targetId or lun is neccesary
//
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;
//
// If pathId is 0xFF that means that pathId and targetId
// will be not be defined
//
if (pathId != 0xFF) {
ScsiPortLogError(HwDeviceExtension,
NULL,
pathId,
targetId,
lun,
SP_LOST_WMI_MINIPORT_REQUEST,
0);
} else {
ScsiPortLogError(HwDeviceExtension,
NULL,
pathId,
0,
0,
SP_LOST_WMI_MINIPORT_REQUEST,
0);
} // pathId != 0xFF
}
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;
//
// If pathId was 0xFF, then there is no defined value for
// targetId or lun
//
if (pathId != 0xFF) {
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 is 0xFF, that means that we're re-registering the
// adapter no targetId or lun is neccesary
//
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;
//
// If pathId is 0xFF that means that pathId and targetId
// will be not be defined
//
if (pathId != 0xFF) {
ScsiPortLogError(HwDeviceExtension,
NULL,
pathId,
targetId,
lun,
SP_LOST_WMI_MINIPORT_REQUEST,
0);
} else {
ScsiPortLogError(HwDeviceExtension,
NULL,
pathId,
0,
0,
SP_LOST_WMI_MINIPORT_REQUEST,
0);
} // pathId != 0xFF
}
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;
//
// If pathId was 0xFF, then there is no defined value for
// targetId or lun
//
if (pathId != 0xFF) {
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()
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.
--*/
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
if(Sp64BitPhysicalAddresses) {
KeBugCheckEx(PORT_DRIVER_INTERNAL,
0,
STATUS_NOT_SUPPORTED,
(ULONG_PTR) HwDeviceExtension,
(ULONG_PTR) deviceExtension->DeviceObject->DriverObject);
}
if (deviceExtension->InterruptData.InterruptFlags & PD_MAP_TRANSFER) {
//
// The transfer has not been started so just clear the map transfer
// flag and return.
//
deviceExtension->InterruptData.InterruptFlags &= ~PD_MAP_TRANSFER;
return;
}
deviceExtension->InterruptData.InterruptFlags |= PD_FLUSH_ADAPTER_BUFFERS;
//
// Request a DPC be queued after the interrupt completes.
//
deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED;
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.
--*/
{
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
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
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 miniport 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.
--*/
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
ULONG binNumber;
for (binNumber = 0; binNumber < NUMBER_LOGICAL_UNIT_BINS; binNumber++) {
PLOGICAL_UNIT_BIN bin = &deviceExtension->LogicalUnitList[binNumber];
PLOGICAL_UNIT_EXTENSION logicalUnit;
ULONG limit = 0;
logicalUnit = bin->List;
DebugPrint((2, "ScsiPortCompleteRequest: Completing requests in "
"bin %d [%#p]\n",
binNumber, bin));
for(logicalUnit = bin->List;
logicalUnit != NULL;
logicalUnit = logicalUnit->NextLogicalUnit) {
PLIST_ENTRY entry;
ASSERT(limit++ < 1000);
//
// See if this logical unit matches the pattern. Check for -1
// first since this seems to be the most popular way to complete
// requests.
//
if (((PathId == SP_UNTAGGED) || (PathId == logicalUnit->PathId)) &&
((TargetId == SP_UNTAGGED) ||
(TargetId == logicalUnit->TargetId)) &&
((Lun == SP_UNTAGGED) || (Lun == logicalUnit->Lun))) {
//
// Complete any pending abort reqeusts.
//
if (logicalUnit->AbortSrb != NULL) {
logicalUnit->AbortSrb->SrbStatus = SrbStatus;
ScsiPortNotification(
RequestComplete,
HwDeviceExtension,
logicalUnit->AbortSrb
);
}
if(logicalUnit->CurrentUntaggedRequest != NULL) {
SpCompleteSrb(deviceExtension,
logicalUnit->CurrentUntaggedRequest,
SrbStatus);
}
//
// Complete each of the requests in the queue.
//
entry = logicalUnit->RequestList.Flink;
while (entry != &logicalUnit->RequestList) {
PSRB_DATA srbData;
ASSERT(limit++ < 1000);
srbData = CONTAINING_RECORD(entry, SRB_DATA, RequestList);
SpCompleteSrb(deviceExtension, srbData, SrbStatus);
entry = srbData->RequestList.Flink;
}
}
}
}
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.
--*/
{
//
// See if the length, source and desitination are word aligned.
//
if (Length & LONG_ALIGN || (ULONG_PTR) WriteBuffer & LONG_ALIGN ||
(ULONG_PTR) ReadBuffer & LONG_ALIGN) {
PCHAR destination = WriteBuffer;
PCHAR source = ReadBuffer;
for (; Length > 0; Length--) {
*destination++ = *source++;
}
} else {
PLONG destination = WriteBuffer;
PLONG source = ReadBuffer;
Length /= sizeof(LONG);
for (; Length > 0; Length--) {
*destination++ = *source++;
}
}
} // end ScsiPortMoveMemory()
#if SCSIDBG_ENABLED
VOID
ScsiDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print for scsi miniports.
Arguments:
Debug print level between 0 and 3, with 3 being the most verbose.
Return Value:
None
Note:
Using the new debug API (systemwide API) DebugPrintEx
renders the variable scsidebug meaningless, since the
level of debug is now controlled int he debug filter
the mask for scsiprot is Kd_ScsiMiniPort_Mask
From the debugger do: ed Kd_ScsiPort_Mask X
where X is the desired value of debug spew.
DPFLTR_ERROR_LEVEL - Are always printed.
DPFLTR_WARNING_LEVEL - 0x01 (bit 1)
DPFLTR_TRACE_LEVEL - 0x02 (bit 2)
DPFLTR_INFO_LEVEL - 0x04 (bit 3)
If you'd like to get WARNING and TRACE, you muyst set bit 1 and 2 (etc)
The Mask is a 32-bit value
I can only see 1 bad thing about changing this function.
Before the _vsnprintf operations would only occurr if we KNEW
that the debug message was going to be printed. Now it
occurs before we deternime wether it will be printed. This changes
the timing of the checked build a bit.
--*/
{
va_list ap;
ULONG DebugLevel;
//
// This code should be removed soon
// Its place here is to remind people debugging scsiport
// that the methods that control debug spew have changed
//
// NOTE - Eventually we should be able to remove this.
//
if (ScsiDebug != 0) {
// This means that someone changed the value of ScsiDebug
// (ie. they want debug spew)
DbgPrintEx(DPFLTR_SCSIMINIPORT_ID, DPFLTR_ERROR_LEVEL,
"Debug messages in SCSI Miniports are no longer controlled by\n"
"scsiport!scsidebug. Please use the correct debug maski\n\n"
"Kd_ScsiPort_Mask -- controls debug msgs from ScsiPort\n"
"Kd_ScsiMiniPort_Mask -- controls debug msgs from SCSI-Miniports\n\n"
"\t0x01 - Error Level\t(bit 0)\n"
"\t0x02 - Warning Level\t(bit 1)\n"
"\t0x04 - Trace Level\t(bit 2)\n"
"\t0x08 - Info Level\t(bit 3)\n\n"
"To get multiple levels, OR the bit-values\n");
DbgBreakPoint();
ScsiDebug = 0;
}
va_start(ap, DebugMessage);
//
// Map the debugprintlevels of scsiport into the new
// debug print API
//
switch (DebugPrintLevel) {
case 0:
DebugLevel = DPFLTR_WARNING_LEVEL;
break;
case 1:
case 2:
DebugLevel = DPFLTR_TRACE_LEVEL;
break;
case 3:
DebugLevel = DPFLTR_INFO_LEVEL;
break;
default:
DebugLevel = DebugPrintLevel;
break;
}
vDbgPrintExWithPrefix("ScsiMiniport: ",
DPFLTR_SCSIMINIPORT_ID,
DebugLevel,
DebugMessage,
ap);
va_end(ap);
} // end ScsiDebugPrint()
VOID
ScsiDebugPrintInt(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print for Internal DebugPrints (SCSIPORT Internal).
Arguments:
Debug print level between 0 and 3, with 3 being the most verbose.
Return Value:
None
Note:
Using the new debug API (systemwide API) DebugPrintEx
renders the variable scsidebug meaningless, since the
level of debug is now controlled int he debug filter
the mask for scsiprot is Kd_ScsiPort_Mask
From the debugger do: ed Kd_ScsiPort_Mask X
where X is the desired value of debug spew.
DPFLTR_ERROR_LEVEL - Are always printed.
DPFLTR_WARNING_LEVEL - 0x01 (bit 1)
DPFLTR_TRACE_LEVEL - 0x02 (bit 2)
DPFLTR_INFO_LEVEL - 0x04 (bit 3)
The Mask is a 32-bit value
I can only see 1 bad thing about changing this function.
Before the _vsnprintf operations would only occurr if we KNEW
that the debug message was going to be printed. Now it
occurs before we deternime wether it will be printed. This changes
the timing of the checked build a bit.
--*/
{
va_list ap;
ULONG DebugLevel;
//
// This code should be removed soon
// Its place here is to remind people debugging scsiport
// that the methods that control debug spew have changed
//
// NOTE - Eventually we should be able to remove this.
//
if (ScsiDebug != 0) {
// This means that someone changed the value of ScsiDebug
// (ie. they want debug spew)
DbgPrintEx(DPFLTR_SCSIPORT_ID, DPFLTR_ERROR_LEVEL,
"Debug messages in SCSI Miniports are no longer controlled by\n"
"scsiport!scsidebug. Please use the correct debug maski\n\n"
"Kd_ScsiPort_Mask -- controls debug msgs from ScsiPort\n"
"Kd_ScsiMiniPort_Mask -- controls debug msgs from SCSI-Miniports\n\n"
"\t0x01 - Error Level\t(bit 0)\n"
"\t0x02 - Warning Level\t(bit 1)\n"
"\t0x04 - Trace Level\t(bit 2)\n"
"\t0x08 - Info Level\t(bit 3)\n\n"
"To get multiple levels, OR the bit-values\n");
DbgBreakPoint();
ScsiDebug = 0;
}
va_start(ap, DebugMessage);
//
// Map the debugprintlevels of scsiport into the new
// debug print API
//
switch (DebugPrintLevel) {
case 0:
DebugLevel = DPFLTR_ERROR_LEVEL;
break;
case 1:
case 2:
DebugLevel = DPFLTR_WARNING_LEVEL;
break;
case 3:
DebugLevel = DPFLTR_TRACE_LEVEL;
break;
default:
DebugLevel = DPFLTR_INFO_LEVEL;
break;
}
vDbgPrintExWithPrefix("ScsiPort: ",
DPFLTR_SCSIPORT_ID,
DebugLevel,
DebugMessage,
ap);
va_end(ap);
} // end ScsiDebugPrint()
#else
//
// ScsiDebugPrint stub
//
VOID
ScsiDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
}
VOID
ScsiDebugPrintInt(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
}
#endif
//
// The below I/O access routines are forwarded to the HAL or NTOSKRNL on
// Alpha and Intel platforms.
//
#if !defined(_ALPHA_) && !defined(_X86_)
UCHAR
ScsiPortReadPortUchar(
IN PUCHAR Port
)
/*++
Routine Description:
Read from the specified port address.
Arguments:
Port - Supplies a pointer to the port address.
Return Value:
Returns the value read from the specified port address.
--*/
{
return(READ_PORT_UCHAR(Port));
}
USHORT
ScsiPortReadPortUshort(
IN PUSHORT Port
)
/*++
Routine Description:
Read from the specified port address.
Arguments:
Port - Supplies a pointer to the port address.
Return Value:
Returns the value read from the specified port address.
--*/
{
return(READ_PORT_USHORT(Port));
}
ULONG
ScsiPortReadPortUlong(
IN PULONG Port
)
/*++
Routine Description:
Read from the specified port address.
Arguments:
Port - Supplies a pointer to the port address.
Return Value:
Returns the value read from the specified port address.
--*/
{
return(READ_PORT_ULONG(Port));
}
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);
}
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 specified 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 specified 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
--*/
{
WRITE_PORT_UCHAR(Port, Value);
}
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
--*/
{
WRITE_PORT_USHORT(Port, Value);
}
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
--*/
{
WRITE_PORT_ULONG(Port, Value);
}
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);
}
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
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);
}
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);
}
#endif // !defined(_ALPHA_) && !defined(_X86_)
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.
--*/
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PSRB_DATA srbData;
PSCSI_REQUEST_BLOCK srb;
UCHAR pathId;
UCHAR targetId;
UCHAR lun;
srbData = SpGetSrbData(deviceExtension,
PathId,
TargetId,
Lun,
(UCHAR)QueueTag,
FALSE);
if (srbData == NULL || srbData->CurrentSrb == NULL) {
return(NULL);
}
srb = srbData->CurrentSrb;
//
// If the srb is not active then return NULL;
//
if (!(srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) {
return(NULL);
}
return (srb);
} // end ScsiPortGetSrb()
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) {
PSRB_SCATTER_GATHER scatterList;
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 = 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;
address.QuadPart = scatterList->Address.QuadPart + byteOffset;
} else {
length = 0;
address.QuadPart = (LONGLONG)(SP_UNINITIALIZED_VALUE);
}
*Length = length;
return 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
--*/
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
PVOID address;
ULONG smallphysicalBase;
ULONG smallAddress;
smallAddress = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress);
smallphysicalBase = ScsiPortConvertPhysicalAddressToUlong(deviceExtension->PhysicalCommonBuffer);
//
// Check that the physical address is within the proper range.
//
if (smallAddress < smallphysicalBase ||
smallAddress >= smallphysicalBase + deviceExtension->CommonBufferSize) {
//
// This is a bugous physical address return back NULL.
//
return(NULL);
}
address = smallAddress - smallphysicalBase +
(PUCHAR) deviceExtension->SrbExtensionBuffer;
return address;
} // end ScsiPortGetVirtualAddress()
BOOLEAN
ScsiPortValidateRange(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace
)
/*++
Routine Description:
This routine should take an IO range and make sure that it is not already
in use by another adapter. This allows miniport drivers to probe IO where
an adapter could be, without worrying about messing up another card.
Arguments:
HwDeviceExtension - Used to find scsi managers internal structures
BusType - EISA, PCI, PC/MCIA, MCA, ISA, what?
SystemIoBusNumber - Which system bus?
IoAddress - Start of range
NumberOfBytes - Length of range
InIoSpace - Is range in IO space?
Return Value:
TRUE if range not claimed by another driver.
--*/
{
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
//
// This is not implemented in NT.
//
return TRUE;
}
SCSI_PHYSICAL_ADDRESS
ScsiPortConvertUlongToPhysicalAddress(
ULONG_PTR UlongAddress
)
{
SCSI_PHYSICAL_ADDRESS physicalAddress;
physicalAddress.QuadPart = UlongAddress;
return(physicalAddress);
}
//
// Leave these routines at the end of the file.
//
#undef ScsiPortConvertPhysicalAddressToUlong
ULONG
ScsiPortConvertPhysicalAddressToUlong(
SCSI_PHYSICAL_ADDRESS Address
)
/*++
Routine Description:
This routine converts a 64-bit physical address to a ULONG
Arguments:
Address - Supplies a 64-bit address to be converted.
Return Value:
Returns a 32-bit address.
--*/
{
return(Address.LowPart);
}
VOID
ScsiPortStallExecution(
ULONG Delay
)
/*++
Routine Description:
This routine stalls the process for the specified number of microseconds.
Arguments:
Delay - the number of microseconds to stall.
Return Value:
--*/
{
KeStallExecutionProcessor(Delay);
}
#if defined(_AMD64_)
VOID
ScsiPortQuerySystemTime (
OUT PLARGE_INTEGER CurrentTime
)
/*++
Routine Description:
This function returns the current system time.
Arguments:
CurrentTime - Supplies a pointer to a variable that will receive the
current system time.
Return Value:
None.
--*/
{
KeQuerySystemTime(CurrentTime);
return;
}
#endif