mirror of https://github.com/lianthony/NT4.0
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.
1821 lines
34 KiB
1821 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
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
|
|
ULONG ScsiDebug = 0;
|
|
UCHAR ScsiBuffer[128];
|
|
#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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
DebugPrint((3, "ScsiPortGetLogicalUnit: TargetId %d\n",
|
|
TargetId));
|
|
|
|
//
|
|
// Get pointer to port driver device extension.
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)HwDeviceExtension - 1;
|
|
|
|
//
|
|
// Make sure the target id is valid.
|
|
//
|
|
|
|
if (TargetId >= deviceExtension->MaximumTargetIds) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Get pointer to logical unit list.
|
|
//
|
|
|
|
logicalUnit = deviceExtension->LogicalUnitList[(TargetId + Lun) % NUMBER_LOGICAL_UNIT_BINS];
|
|
|
|
//
|
|
// 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 + 1;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
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.
|
|
//
|
|
|
|
if (!(srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) {
|
|
|
|
va_end(ap);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clear the active flag.
|
|
//
|
|
|
|
srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE;
|
|
|
|
//
|
|
// Treat abort completions as a special case.
|
|
//
|
|
|
|
if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
|
|
|
|
logicalUnit = GetLogicalUnitExtension(deviceExtension,
|
|
srb->PathId,
|
|
srb->TargetId,
|
|
srb->Lun);
|
|
|
|
logicalUnit->CompletedAbort =
|
|
deviceExtension->InterruptData.CompletedAbort;
|
|
|
|
deviceExtension->InterruptData.CompletedAbort = logicalUnit;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the SRB data and link it into the completion list.
|
|
//
|
|
|
|
srbData = SpGetSrbData(deviceExtension,
|
|
srb->PathId,
|
|
srb->TargetId,
|
|
srb->Lun,
|
|
srb->QueueTag);
|
|
|
|
ASSERT(srbData->CurrentSrb != NULL && srbData->CompletedRequests == NULL);
|
|
if ((srb->SrbStatus == SRB_STATUS_SUCCESS) &&
|
|
((srb->Cdb[0] == SCSIOP_READ) ||
|
|
(srb->Cdb[0] == SCSIOP_WRITE))) {
|
|
ASSERT(srb->DataTransferLength);
|
|
}
|
|
|
|
srbData->CompletedRequests =
|
|
deviceExtension->InterruptData.CompletedRequests;
|
|
deviceExtension->InterruptData.CompletedRequests = srbData;
|
|
}
|
|
|
|
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 = GetLogicalUnitExtension(deviceExtension,
|
|
pathId,
|
|
targetId,
|
|
lun);
|
|
|
|
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->SrbData.CurrentSrb == NULL) {
|
|
|
|
//
|
|
// Add the logical unit to the chain of logical units that
|
|
// another request maybe processed for.
|
|
//
|
|
|
|
logicalUnit->ReadyLogicalUnit =
|
|
deviceExtension->InterruptData.ReadyLogicalUnit;
|
|
deviceExtension->InterruptData.ReadyLogicalUnit = logicalUnit;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CallDisableInterrupts:
|
|
|
|
ASSERT(deviceExtension->InterruptData.InterruptFlags & PD_DISABLE_INTERRUPTS);
|
|
|
|
//
|
|
// The miniport wants us to call the specified routine
|
|
// with interrupts disabled. This is done after the current
|
|
// HwRequestInterrutp routine completes. Indicate the call is
|
|
// needed and save the routine to be called.
|
|
//
|
|
|
|
deviceExtension->Flags |= PD_DISABLE_CALL_REQUEST;
|
|
|
|
deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
|
|
break;
|
|
|
|
case CallEnableInterrupts:
|
|
|
|
//
|
|
// The miniport wants us to call the specified routine
|
|
// with interrupts enabled this is done from the DPC.
|
|
// Disable calls to the interrupt routine, indicate the call is
|
|
// needed and save the routine to be called.
|
|
//
|
|
|
|
deviceExtension->InterruptData.InterruptFlags |=
|
|
PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST;
|
|
|
|
deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
|
|
break;
|
|
|
|
case RequestTimerCall:
|
|
|
|
//
|
|
// The driver wants to set the miniport timer.
|
|
// Save the timer parameters.
|
|
//
|
|
|
|
deviceExtension->InterruptData.InterruptFlags |=
|
|
PD_TIMER_CALL_REQUEST;
|
|
deviceExtension->InterruptData.HwTimerRequest =
|
|
va_arg(ap, PHW_INTERRUPT);
|
|
deviceExtension->InterruptData.MiniportTimerValue =
|
|
va_arg(ap, ULONG);
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION 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;
|
|
}
|
|
|
|
ASSERT((Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) != SRB_FLAGS_UNSPECIFIED_DIRECTION);
|
|
|
|
deviceExtension->InterruptData.MapTransferParameters.SrbData =
|
|
SpGetSrbData(deviceExtension,
|
|
Srb->PathId,
|
|
Srb->TargetId,
|
|
Srb->Lun,
|
|
Srb->QueueTag);
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PDEVICE_OBJECT DeviceObject = deviceExtension->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 DBG
|
|
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 = SpGetSrbData(
|
|
deviceExtension,
|
|
Srb->PathId,
|
|
Srb->TargetId,
|
|
Srb->Lun,
|
|
Srb->QueueTag
|
|
);
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSRB_DATA srbData;
|
|
PLIST_ENTRY entry;
|
|
ULONG limit = 0;
|
|
ULONG target;
|
|
ULONG bus;
|
|
|
|
for (bus = 0; bus < SCSI_MAXIMUM_BUSES; bus++) {
|
|
for (target = 0; target < deviceExtension->MaximumTargetIds; target++) {
|
|
|
|
logicalUnit = deviceExtension->LogicalUnitList[target % NUMBER_LOGICAL_UNIT_BINS];
|
|
|
|
while (logicalUnit != NULL) {
|
|
|
|
ASSERT(limit++ < 1000);
|
|
DebugPrint((2,
|
|
"ScsiPortCompleteRequest: Complete requests for targetid %d\n",
|
|
logicalUnit->TargetId));
|
|
|
|
|
|
if ((PathId == logicalUnit->PathId || PathId == SP_UNTAGGED) &&
|
|
(TargetId == logicalUnit->TargetId || TargetId == SP_UNTAGGED) &&
|
|
(Lun == logicalUnit->Lun || Lun == SP_UNTAGGED)) {
|
|
|
|
//
|
|
// Complete any pending abort reqeusts.
|
|
//
|
|
|
|
if (logicalUnit->AbortSrb != NULL) {
|
|
logicalUnit->AbortSrb->SrbStatus = SrbStatus;
|
|
|
|
ScsiPortNotification(
|
|
RequestComplete,
|
|
HwDeviceExtension,
|
|
logicalUnit->AbortSrb
|
|
);
|
|
}
|
|
|
|
SpCompleteRequest(deviceExtension, &logicalUnit->SrbData, SrbStatus);
|
|
|
|
//
|
|
// Complete each of the requests in the queue.
|
|
//
|
|
|
|
entry = logicalUnit->SrbData.RequestList.Flink;
|
|
while (entry != &logicalUnit->SrbData.RequestList) {
|
|
ASSERT(limit++ < 1000);
|
|
srbData = CONTAINING_RECORD(entry, SRB_DATA, RequestList);
|
|
SpCompleteRequest(deviceExtension, srbData, SrbStatus);
|
|
entry = srbData->RequestList.Flink;
|
|
}
|
|
|
|
}
|
|
|
|
logicalUnit = logicalUnit->NextLogicalUnit;
|
|
|
|
} // end while
|
|
}
|
|
}
|
|
|
|
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) WriteBuffer & LONG_ALIGN ||
|
|
(ULONG) 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 DBG
|
|
|
|
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) {
|
|
|
|
vsprintf(ScsiBuffer, DebugMessage, ap);
|
|
|
|
DbgPrint(ScsiBuffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
} // end ScsiDebugPrint()
|
|
|
|
#else
|
|
|
|
//
|
|
// ScsiDebugPrint stub
|
|
//
|
|
|
|
VOID
|
|
ScsiDebugPrint(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PSRB_DATA srbData;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
srbData = SpGetSrbData(deviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
(UCHAR)QueueTag);
|
|
|
|
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:
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
ULONG byteOffset;
|
|
PHYSICAL_ADDRESS address;
|
|
ULONG length;
|
|
|
|
if (Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress) {
|
|
|
|
byteOffset = (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 = SpGetSrbData(deviceExtension,
|
|
Srb->PathId,
|
|
Srb->TargetId,
|
|
Srb->Lun,
|
|
Srb->QueueTag);
|
|
|
|
scatterList = srbData->ScatterGather;
|
|
|
|
//
|
|
// Calculate byte offset into the data buffer.
|
|
//
|
|
|
|
byteOffset = (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 = (LONGLONG)(scatterList->PhysicalAddress + 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
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
//
|
|
// This is not implemented in NT.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|