3820 lines
88 KiB
C
3820 lines
88 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1993-4 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
cpqarray.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This is the device driver for the Compaq Intelligent Disk Array.
|
|||
|
|
|||
|
Authors:
|
|||
|
|
|||
|
Mike Glass (mglass)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Compaq Information Manager (CIM) support was developed by Tom Bonola and
|
|||
|
Tom Woller, courtesy of Compaq Computer Corporation.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#ifndef BYTE
|
|||
|
#define BYTE unsigned char
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef WORD
|
|||
|
#define WORD unsigned short
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef DWORD
|
|||
|
#define DWORD unsigned long
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef INT
|
|||
|
#define INT int
|
|||
|
#endif
|
|||
|
|
|||
|
#ifndef STATIC
|
|||
|
#if DBG
|
|||
|
#define STATIC
|
|||
|
#else
|
|||
|
#define STATIC static
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#include "miniport.h"
|
|||
|
#include "scsi.h"
|
|||
|
#include <ntddscsi.h>
|
|||
|
#include <scsireg.h>
|
|||
|
#include <cpqsczmp.h> // Compaq SCSI M&P definitions
|
|||
|
#include "cpqarray.h"
|
|||
|
#include "pcibios.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Adapter storage area
|
|||
|
//
|
|||
|
|
|||
|
typedef struct _DEVICE_EXTENSION {
|
|||
|
|
|||
|
//
|
|||
|
// Requests needing restarts
|
|||
|
//
|
|||
|
|
|||
|
PCOMMAND_LIST RestartRequests;
|
|||
|
|
|||
|
//
|
|||
|
// IDA BMIC registers
|
|||
|
//
|
|||
|
|
|||
|
PIDA_CONTROLLER Bmic;
|
|||
|
PULONG CPFIFO;
|
|||
|
PULONG CCFIFO;
|
|||
|
PULONG InterruptMask;
|
|||
|
PULONG InterruptStatus;
|
|||
|
PULONG InterruptPending;
|
|||
|
HBA_CONFIGURATION HBAConfiguration; //Memory mapped base
|
|||
|
ULONG BaseIOAddress; // I/O space accessible, not used.
|
|||
|
PEISAPCI_CONTROLLER eisapci;
|
|||
|
ULONG PCIoff;
|
|||
|
|
|||
|
//
|
|||
|
// Noncached extension for identify commands
|
|||
|
//
|
|||
|
|
|||
|
PVOID IdentifyBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Number of logical drives
|
|||
|
//
|
|||
|
|
|||
|
ULONG NumberOfLogicalDrives;
|
|||
|
UCHAR SectorShift; //setup to 9
|
|||
|
ULONG EisaId;
|
|||
|
UCHAR IrqLevel;
|
|||
|
IDENTIFY_CONTROLLER IdentifyData; // permanent controller info storage
|
|||
|
|
|||
|
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
|
|||
|
|
|||
|
//
|
|||
|
// Drive storage area
|
|||
|
//
|
|||
|
|
|||
|
typedef struct _LOGICAL_UNIT_EXTENSION {
|
|||
|
|
|||
|
//
|
|||
|
// Drive indentify data.
|
|||
|
//
|
|||
|
|
|||
|
IDENTIFY_LOGICAL_DRIVE IdentifyData;
|
|||
|
SENSE_CONFIGURATION SenseData; // sense data for logical drive
|
|||
|
|
|||
|
} LOGICAL_UNIT_EXTENSION, *PLOGICAL_UNIT_EXTENSION;
|
|||
|
|
|||
|
#include "cpqsmngr.h"
|
|||
|
|
|||
|
VOID
|
|||
|
BuildFlushDisable(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
);
|
|||
|
|
|||
|
ULONG
|
|||
|
IdaProcessIoctl(
|
|||
|
IN PDEVICE_EXTENSION pIdaDeviceExtension,
|
|||
|
PVOID pIoctlBuffer,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
);
|
|||
|
ULONG
|
|||
|
BuildCIMList(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
);
|
|||
|
VOID
|
|||
|
IdaMoveMemory(
|
|||
|
OUT PUCHAR pDestination,
|
|||
|
IN PUCHAR pSource,
|
|||
|
IN ULONG ulLength
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaStrCmp(
|
|||
|
IN PUCHAR p1,
|
|||
|
IN PUCHAR p2
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
IdaEnableInts(IN PDEVICE_EXTENSION);
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
IdaDisableInts(IN PDEVICE_EXTENSION);
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SearchEisaBus(
|
|||
|
IN PVOID HwDeviceExtension,
|
|||
|
IN PVOID Context,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called from IdaFindAdapter if the system fails to
|
|||
|
pass in predetermined configuration data. It searches the EISA bus
|
|||
|
data looking for information about controllers that this driver
|
|||
|
supports.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
Context - Used to track how many EISA slots have been searched.
|
|||
|
ConfigInfo - System template for configuration information.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - If Compaq IDA controller found.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
ULONG length;
|
|||
|
ULONG eisaSlotNumber;
|
|||
|
PACCESS_RANGE accessRange;
|
|||
|
PCM_EISA_SLOT_INFORMATION slotInformation;
|
|||
|
PCM_EISA_FUNCTION_INFORMATION functionInformation;
|
|||
|
ULONG numberOfFunctions;
|
|||
|
|
|||
|
//
|
|||
|
// Get pointer to first configuration info structure access range.
|
|||
|
//
|
|||
|
|
|||
|
accessRange = &((*(ConfigInfo->AccessRanges))[0]);
|
|||
|
|
|||
|
for (eisaSlotNumber=*((PULONG)Context);
|
|||
|
eisaSlotNumber<16;
|
|||
|
eisaSlotNumber++) {
|
|||
|
|
|||
|
//
|
|||
|
// Get pointer to bus data for this EISA slot.
|
|||
|
//
|
|||
|
|
|||
|
length = ScsiPortGetBusData(HwDeviceExtension,
|
|||
|
EisaConfiguration,
|
|||
|
ConfigInfo->SystemIoBusNumber,
|
|||
|
eisaSlotNumber,
|
|||
|
&slotInformation,
|
|||
|
0);
|
|||
|
|
|||
|
if (!length) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for Compaq IDA board id.
|
|||
|
//
|
|||
|
|
|||
|
if ((slotInformation->CompressedId & 0x00FFFFFF) == 0x0040110E) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if all slots searched.
|
|||
|
//
|
|||
|
|
|||
|
if (eisaSlotNumber == 16) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up default port address.
|
|||
|
//
|
|||
|
|
|||
|
accessRange->RangeStart.LowPart =
|
|||
|
(eisaSlotNumber * 0x1000) + 0x0C80;
|
|||
|
accessRange->RangeLength = sizeof(IDA_CONTROLLER);
|
|||
|
|
|||
|
accessRange++;
|
|||
|
|
|||
|
ConfigInfo->SlotNumber = eisaSlotNumber;
|
|||
|
|
|||
|
//
|
|||
|
// Get the number of EISA configuration functions returned in bus data.
|
|||
|
//
|
|||
|
|
|||
|
numberOfFunctions = slotInformation->NumberFunctions;
|
|||
|
|
|||
|
//
|
|||
|
// Get first configuration record.
|
|||
|
//
|
|||
|
|
|||
|
functionInformation =
|
|||
|
(PCM_EISA_FUNCTION_INFORMATION)(slotInformation + 1);
|
|||
|
|
|||
|
//
|
|||
|
// Walk configuration records to find EISA IRQ.
|
|||
|
//
|
|||
|
|
|||
|
for (; 0 < numberOfFunctions; numberOfFunctions--, functionInformation++) {
|
|||
|
|
|||
|
//
|
|||
|
// Check for IRQ.
|
|||
|
//
|
|||
|
|
|||
|
if (functionInformation->FunctionFlags & EISA_HAS_IRQ_ENTRY) {
|
|||
|
|
|||
|
ConfigInfo->BusInterruptLevel =
|
|||
|
functionInformation->EisaIrq->ConfigurationByte.Interrupt;
|
|||
|
ConfigInfo->InterruptMode = LevelSensitive;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for IO ranges.
|
|||
|
//
|
|||
|
|
|||
|
if (functionInformation->FunctionFlags & EISA_HAS_PORT_RANGE) {
|
|||
|
|
|||
|
PEISA_PORT_CONFIGURATION eisaPort =
|
|||
|
functionInformation->EisaPort;
|
|||
|
|
|||
|
//
|
|||
|
// Search for emulation ranges.
|
|||
|
//
|
|||
|
|
|||
|
while (eisaPort->PortAddress) {
|
|||
|
|
|||
|
//
|
|||
|
// Check range to determine length.
|
|||
|
//
|
|||
|
|
|||
|
switch (eisaPort->PortAddress) {
|
|||
|
|
|||
|
case 0x000001f0:
|
|||
|
case 0x00000170:
|
|||
|
|
|||
|
accessRange->RangeStart.LowPart = eisaPort->PortAddress;
|
|||
|
accessRange->RangeLength = 0x0000000F;
|
|||
|
break;
|
|||
|
|
|||
|
case 0x000003f6:
|
|||
|
case 0x00000176:
|
|||
|
|
|||
|
accessRange->RangeStart.LowPart = eisaPort->PortAddress;
|
|||
|
accessRange->RangeLength = 0x00000001;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"CPQARRAY: SearchEisaBus: IO base %x\n",
|
|||
|
eisaPort->PortAddress));
|
|||
|
|
|||
|
//
|
|||
|
// Advance pointers to next IO range.
|
|||
|
//
|
|||
|
|
|||
|
accessRange++;
|
|||
|
eisaPort++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate from which EISA slot to continue search.
|
|||
|
//
|
|||
|
|
|||
|
*((PULONG)Context) = eisaSlotNumber + 1;
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // end SearchEisaBus()
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaInitialize(
|
|||
|
IN PVOID HwDeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called by the system during initialization to
|
|||
|
prepare the controller to receive requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
|
|||
|
|
|||
|
if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER) {
|
|||
|
|
|||
|
{
|
|||
|
ULONG tmp;
|
|||
|
DebugPrint((3,"CPQARRAY: Initing DAZZLER\n"));
|
|||
|
tmp = ScsiPortReadPortUlong(&deviceExtension->eisapci->CPFIFO);
|
|||
|
DebugPrint((3,"IdaInitialize: Room for %x requests\n",tmp));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enable command completion interrupts and not channel clear
|
|||
|
//
|
|||
|
ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask,
|
|||
|
IDA_PCI_FIFO_NOT_EMPTY_MASK);
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Enable completion interrupts.
|
|||
|
//
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl,
|
|||
|
IDA_COMPLETION_INTERRUPT_ENABLE);
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask,
|
|||
|
IDA_COMPLETION_INTERRUPT_ENABLE);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
} // end IdaInitialize()
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaInitializePCI(
|
|||
|
IN PVOID HwDeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called by the system during initialization to
|
|||
|
prepare the controller to receive requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PULONG ptmp;
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
|
|||
|
DebugPrint((1,"CPQARRAY: Initing PCI DAZZLER at 0x%x\n",
|
|||
|
deviceExtension->HBAConfiguration.ulBaseIOAddress));
|
|||
|
//
|
|||
|
// Enable command completion interrupts and not channel clear
|
|||
|
//
|
|||
|
ptmp = deviceExtension->InterruptMask;
|
|||
|
ptmp[0] |= IDA_PCI_FIFO_NOT_EMPTY_MASK;
|
|||
|
|
|||
|
DebugPrint((1,"IdaInitializePCI: Room for %x requests\n",
|
|||
|
*((PULONG)deviceExtension->CPFIFO)));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // end IdaInitializePCI()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaResetBus(
|
|||
|
IN PVOID HwDeviceExtension,
|
|||
|
IN ULONG PathId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine resets the controller and completes outstanding requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
PathId - Indicates adapter to reset.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
#ifndef NCPQNO_TIMEOUT
|
|||
|
//
|
|||
|
// 10 second timeout is inappropriate for IDA-style controllers. The
|
|||
|
// better approach is to never timeout any requests. Proper fix is
|
|||
|
// to have a class driver for IDA and set the timeout for each type
|
|||
|
// of controller (currently 3 minutes is a good timeout).
|
|||
|
//
|
|||
|
/* ScsiPortLogError(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_REQUEST_TIMEOUT,
|
|||
|
1); */
|
|||
|
#else
|
|||
|
|
|||
|
//
|
|||
|
// Complete all outstanding requests.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortCompleteRequest(HwDeviceExtension,
|
|||
|
(UCHAR)PathId,
|
|||
|
0xFF,
|
|||
|
0xFF,
|
|||
|
SRB_STATUS_BUS_RESET);
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Adapter ready for next request.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortNotification(NextRequest,
|
|||
|
HwDeviceExtension,
|
|||
|
NULL);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // end IdaResetBus()
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
BuildCommandList(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine builds a command list suitable for submission to the
|
|||
|
Compaq IDA controller, from an SRB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Address of adapter storage area.
|
|||
|
Srb - System request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCOMMAND_LIST commandList = Srb->SrbExtension;
|
|||
|
PVOID dataPointer;
|
|||
|
ULONG physicalAddress;
|
|||
|
ULONG bytesLeft;
|
|||
|
ULONG descriptor;
|
|||
|
ULONG length;
|
|||
|
|
|||
|
//
|
|||
|
// Save SRB address for interrupt routine.
|
|||
|
//
|
|||
|
|
|||
|
commandList->SrbAddress = Srb;
|
|||
|
|
|||
|
//
|
|||
|
// Set up Command List Header.
|
|||
|
//
|
|||
|
|
|||
|
commandList->CommandListHeader.LogicalDriveNumber = Srb->TargetId;
|
|||
|
|
|||
|
commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
|
|||
|
|
|||
|
commandList->CommandListHeader.Flags =
|
|||
|
CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
|
|||
|
|
|||
|
//
|
|||
|
// Terminate request list.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.NextRequestOffset = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Clear request tracking flags.
|
|||
|
//
|
|||
|
|
|||
|
commandList->Flags = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Determine command.
|
|||
|
//
|
|||
|
|
|||
|
if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
|
|||
|
commandList->RequestHeader.CommandByte = RH_COMMAND_READ;
|
|||
|
} else {
|
|||
|
commandList->RequestHeader.CommandByte = RH_COMMAND_WRITE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reset error code.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.ErrorCode = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Clear reserved field.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.Reserved = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Determine number of blocks to transfer.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.BlockCount =
|
|||
|
((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb |
|
|||
|
((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8;
|
|||
|
|
|||
|
//
|
|||
|
// Determine number starting block.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.BlockNumber =
|
|||
|
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
|
|||
|
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
|
|||
|
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
|
|||
|
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
|
|||
|
|
|||
|
//
|
|||
|
// Build scatter/gather descriptor list.
|
|||
|
//
|
|||
|
|
|||
|
descriptor = 0;
|
|||
|
dataPointer = Srb->DataBuffer;
|
|||
|
bytesLeft = Srb->DataTransferLength;
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Get physical address and length of contiguous
|
|||
|
// physical buffer.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong(
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
Srb,
|
|||
|
dataPointer,
|
|||
|
&length));
|
|||
|
|
|||
|
//
|
|||
|
// If length of physical memory is more
|
|||
|
// than bytes left in transfer, use bytes
|
|||
|
// left as final length.
|
|||
|
//
|
|||
|
|
|||
|
if (length > bytesLeft) {
|
|||
|
length = bytesLeft;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in descriptor.
|
|||
|
//
|
|||
|
|
|||
|
commandList->SgDescriptor[descriptor].Address = physicalAddress;
|
|||
|
commandList->SgDescriptor[descriptor].Length = length;
|
|||
|
|
|||
|
//
|
|||
|
// Adjust counts.
|
|||
|
//
|
|||
|
|
|||
|
dataPointer = (PUCHAR)dataPointer + length;
|
|||
|
bytesLeft -= length;
|
|||
|
descriptor++;
|
|||
|
|
|||
|
} while (bytesLeft);
|
|||
|
|
|||
|
//
|
|||
|
// Calculate size of command list.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.ScatterGatherCount = (UCHAR) descriptor;
|
|||
|
commandList->CommandListSize = sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SG_DESCRIPTOR) *
|
|||
|
descriptor;
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // end BuildCommandList()
|
|||
|
|
|||
|
VOID
|
|||
|
SubmitCommandList(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PCOMMAND_LIST CommandList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine attempts to submit a command list to the controller. If
|
|||
|
the controller can't take it within a specified time interval, then the
|
|||
|
request is queued to be retried after another request completes.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Address of adapter storage area.
|
|||
|
CommandList - Request to be submitted.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG physicalAddress;
|
|||
|
ULONG length;
|
|||
|
ULONG i;
|
|||
|
PULONG ptmp;
|
|||
|
ULONG tmp;
|
|||
|
|
|||
|
#ifdef DBG
|
|||
|
DebugPrint((3,
|
|||
|
"%x %x %x %x Cmd=%x %x Bln=%x Blc=%x SGc=%x %x SG0l=%x SG0a=%x\n",
|
|||
|
CommandList->CommandListHeader.LogicalDriveNumber,
|
|||
|
CommandList->CommandListHeader.RequestPriority,
|
|||
|
CommandList->CommandListHeader.Flags,
|
|||
|
CommandList->RequestHeader.NextRequestOffset,
|
|||
|
CommandList->RequestHeader.CommandByte,
|
|||
|
CommandList->RequestHeader.ErrorCode,
|
|||
|
CommandList->RequestHeader.BlockNumber,
|
|||
|
CommandList->RequestHeader.BlockCount,
|
|||
|
CommandList->RequestHeader.ScatterGatherCount,
|
|||
|
CommandList->RequestHeader.Reserved,
|
|||
|
CommandList->SgDescriptor[0].Length,
|
|||
|
CommandList->SgDescriptor[0].Address
|
|||
|
));
|
|||
|
|
|||
|
if (CommandList->RequestHeader.ScatterGatherCount > 1) {
|
|||
|
|
|||
|
for (i=1;i<CommandList->RequestHeader.ScatterGatherCount;i++) {
|
|||
|
DebugPrint((1,
|
|||
|
"%d-l=%x a=%x ",
|
|||
|
i,CommandList->SgDescriptor[i].Length,
|
|||
|
CommandList->SgDescriptor[i].Address
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((3,"\n"));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Check for double submission.
|
|||
|
//
|
|||
|
|
|||
|
if (CommandList->Flags & CL_FLAGS_REQUEST_STARTED) {
|
|||
|
|
|||
|
DebugPrint((0,
|
|||
|
"CPQARRAY: SubmitCommandList: Double submission %x\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Log this error.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortLogError(DeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_INTERNAL_ADAPTER_ERROR,
|
|||
|
1);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get physical address of command list.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
NULL,
|
|||
|
CommandList,
|
|||
|
&length).LowPart;
|
|||
|
|
|||
|
// Handle slightly different Command Header in case of SMART-2
|
|||
|
// controllers. Probably should introduce union to clearify
|
|||
|
// Command Header structure instead of setting .Flags to List
|
|||
|
// size. Size is in dwords not bytes as was the case in SMART
|
|||
|
// and previous ida controllers.
|
|||
|
|
|||
|
if (DeviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
|
|||
|
|
|||
|
DebugPrint((9,"SubmitCommandList: DAZZLER PCI card\n"));
|
|||
|
|
|||
|
CommandList->CommandListHeader.RequestPriority = 0;
|
|||
|
|
|||
|
CommandList->CommandListHeader.Flags =
|
|||
|
((CommandList->CommandListSize % 4) ? 1 : 0) +
|
|||
|
(CommandList->CommandListSize >> 2);
|
|||
|
|
|||
|
ptmp = DeviceExtension->CPFIFO;
|
|||
|
|
|||
|
do {
|
|||
|
tmp = ptmp[0];
|
|||
|
ptmp[0] = physicalAddress;
|
|||
|
} while (tmp == 0);
|
|||
|
|
|||
|
DebugPrint((3,
|
|||
|
"SubmitCommandList: ptmp=0x%x physicalAddress=0x%x\n",
|
|||
|
ptmp,physicalAddress));
|
|||
|
CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
|
|||
|
|
|||
|
} else if ((DeviceExtension->HBAConfiguration.bHBAModel ==
|
|||
|
IDA_EISA_DAZZLER) && !DeviceExtension->PCIoff ) {
|
|||
|
DebugPrint((9,"SubmitCommandList: DAZZLER EISA PCI interface\n"));
|
|||
|
CommandList->CommandListHeader.RequestPriority = 0;
|
|||
|
CommandList->CommandListHeader.Flags =
|
|||
|
((CommandList->CommandListSize % 4) ? 1 : 0) +
|
|||
|
(CommandList->CommandListSize/4);
|
|||
|
|
|||
|
//
|
|||
|
// loop on CPFIFO until we have room to submit
|
|||
|
//
|
|||
|
do {
|
|||
|
tmp = ScsiPortReadPortUlong(&DeviceExtension->eisapci->CPFIFO);
|
|||
|
ScsiPortWritePortUlong(&DeviceExtension->eisapci->CPFIFO,
|
|||
|
physicalAddress);
|
|||
|
} while (tmp == 0);
|
|||
|
|
|||
|
CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
|
|||
|
} else {
|
|||
|
DebugPrint((9, "SubmitCommandList: DAZZLER EISA compatible interface\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Wait up to 100 microseconds for submission channel to clear.
|
|||
|
//
|
|||
|
|
|||
|
for (i=0; i<100; i++) {
|
|||
|
|
|||
|
if (!(ScsiPortReadPortUchar(&DeviceExtension->Bmic->SystemDoorBell) &
|
|||
|
SYSTEM_DOORBELL_SUBMIT_CHANNEL_CLEAR)) {
|
|||
|
|
|||
|
//
|
|||
|
// Stall for a microsecond.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortStallExecution(1);
|
|||
|
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for timeout.
|
|||
|
//
|
|||
|
|
|||
|
if (i == 100) {
|
|||
|
|
|||
|
//
|
|||
|
// Queue request for restart in completion routine.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"CPQARRAY: SubmitRequest: Queueing %x\n",
|
|||
|
CommandList));
|
|||
|
|
|||
|
CommandList->Flags |= CL_FLAGS_REQUEST_QUEUED;
|
|||
|
CommandList->NextEntry = DeviceExtension->RestartRequests;
|
|||
|
DeviceExtension->RestartRequests = CommandList;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
|
|||
|
|
|||
|
//
|
|||
|
// Reset channel clear bit to claim channel.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&DeviceExtension->Bmic->SystemDoorBell,
|
|||
|
SYSTEM_DOORBELL_SUBMIT_CHANNEL_CLEAR);
|
|||
|
|
|||
|
//
|
|||
|
// Write Command List physical address to BMIC mailbox.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUlong(&DeviceExtension->Bmic->CommandListSubmit.Address,
|
|||
|
physicalAddress);
|
|||
|
|
|||
|
//
|
|||
|
// Write Command List length to BMIC mailbox.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUshort(&DeviceExtension->Bmic->CommandListSubmit.Length,
|
|||
|
CommandList->CommandListSize);
|
|||
|
|
|||
|
//
|
|||
|
// Set channel busy bit to signal new Command List is available.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&DeviceExtension->Bmic->LocalDoorBell,
|
|||
|
LOCAL_DOORBELL_COMMAND_LIST_SUBMIT);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} // end SubmitCommandList()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaInterrupt(
|
|||
|
IN PVOID HwDeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This interrupt service routine is called by the system to process an
|
|||
|
adapter interrupt. The Compaq IDA controller interrupts to signal
|
|||
|
completion of a request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if adapter is interrupting.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
ULONG physicalAddress;
|
|||
|
PCOMMAND_LIST commandList;
|
|||
|
PCOMMAND_LIST nextCommand;
|
|||
|
PSCSI_REQUEST_BLOCK srb;
|
|||
|
UCHAR status;
|
|||
|
PSRB_IO_CONTROL pSrb;
|
|||
|
PIDA_ERROR_BITS dataPointer;
|
|||
|
PUCHAR ReturnPointer;
|
|||
|
PUCHAR MovePointer;
|
|||
|
UCHAR CmdListStatus;
|
|||
|
PULONG ptmp;
|
|||
|
|
|||
|
//
|
|||
|
// Verify that interrupt is from one of our controllers.
|
|||
|
//
|
|||
|
|
|||
|
if ((deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER) &&
|
|||
|
!deviceExtension->PCIoff ) {
|
|||
|
DebugPrint((3,"IdaInterrupt: DAZZLER PCI mode\n"));
|
|||
|
|
|||
|
//
|
|||
|
// The PCI interface specification calls for us to check the
|
|||
|
// InterruptPending register to verify that an interrupt has
|
|||
|
// been asserted at our controller. We are instead looking
|
|||
|
// at the InterruptStatus masked with 0x01 to acheive the same
|
|||
|
// result. We cannot use the spec's method, because at init and
|
|||
|
// during a rescan at runtime, we have disabled controller
|
|||
|
// interrupts and thus InterruptPending will be set to zero.
|
|||
|
//
|
|||
|
|
|||
|
if (!(ScsiPortReadPortUlong(&deviceExtension->eisapci->InterruptStatus)
|
|||
|
& IDA_PCI_COMPLETION_STATUS_ACTIVE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Interrupt is not for this controller.
|
|||
|
//
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Read the physical address
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress = ScsiPortReadPortUlong(&deviceExtension->
|
|||
|
eisapci->CCFIFO);
|
|||
|
|
|||
|
DebugPrint((1,"CCFIFO=%x\n",physicalAddress));
|
|||
|
|
|||
|
CmdListStatus = (UCHAR)physicalAddress &
|
|||
|
IDA_PCI_COMPLETION_STATUS_MASK;
|
|||
|
|
|||
|
if (CmdListStatus & IDA_PCI_COMPLETION_ERROR) {
|
|||
|
|
|||
|
DebugPrint((1,"IdaInterrupt: DAZZLER ERROR bit 0 set\n"));
|
|||
|
|
|||
|
//
|
|||
|
// for compatibility change the status to old style error code.
|
|||
|
//
|
|||
|
|
|||
|
CmdListStatus = RH_BAD_COMMAND_LIST;
|
|||
|
}
|
|||
|
|
|||
|
physicalAddress &= IDA_PCI_PHYS_ADDR_MASK;
|
|||
|
|
|||
|
} else if (deviceExtension->HBAConfiguration.bHBAModel
|
|||
|
== IDA_BASE_CONTROLLER) {
|
|||
|
|
|||
|
//
|
|||
|
// Check if interrupt is expected.
|
|||
|
//
|
|||
|
|
|||
|
if (!(ScsiPortReadPortUchar(&deviceExtension->Bmic->SystemDoorBell) &
|
|||
|
SYSTEM_DOORBELL_COMMAND_LIST_COMPLETE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Interrupt is spurious.
|
|||
|
//
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get physical command list address from mailbox.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortReadPortUlong(&deviceExtension->Bmic->
|
|||
|
CommandListComplete.Address);
|
|||
|
|
|||
|
CmdListStatus =
|
|||
|
ScsiPortReadPortUchar(&deviceExtension->Bmic->
|
|||
|
CommandListComplete.Status);
|
|||
|
|
|||
|
//
|
|||
|
// Dismiss interrupt at device by clearing command complete
|
|||
|
// bit in system doorbell.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBell,
|
|||
|
SYSTEM_DOORBELL_COMMAND_LIST_COMPLETE);
|
|||
|
|
|||
|
//
|
|||
|
// Free command completion channel.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->LocalDoorBell,
|
|||
|
LOCAL_DOORBELL_COMPLETE_CHANNEL_CLEAR);
|
|||
|
|
|||
|
} else if (deviceExtension->HBAConfiguration.bHBAModel
|
|||
|
== IDA_PCI_DAZZLER) {
|
|||
|
|
|||
|
// Flags gets the list size and then write the phys addr out
|
|||
|
// to the port
|
|||
|
DebugPrint((3,"IdaInterrupt: DAZZLER PCI card\n"));
|
|||
|
|
|||
|
//
|
|||
|
// The PCI interface specification calls for us to check the
|
|||
|
// InterruptPending register to verify that an interrupt has
|
|||
|
// been asserted at our controller. We are instead looking
|
|||
|
// at the InterruptStatus masked with 0x01 to acheive the same
|
|||
|
// result. We cannot use the spec's method, because at init and
|
|||
|
// during a rescan at runtime, we have disabled controller
|
|||
|
// interrupts and thus InterruptPending will be set to zero.
|
|||
|
//
|
|||
|
|
|||
|
if (!(*((PULONG)deviceExtension->InterruptStatus) &
|
|||
|
IDA_PCI_COMPLETION_STATUS_ACTIVE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Interrupt is not for this controller.
|
|||
|
//
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Read the physical address
|
|||
|
//
|
|||
|
|
|||
|
ptmp = deviceExtension->CCFIFO;
|
|||
|
physicalAddress = ptmp[0];
|
|||
|
|
|||
|
DebugPrint((9,"CCFIFO=%x\n",physicalAddress));
|
|||
|
|
|||
|
CmdListStatus = (UCHAR)physicalAddress & IDA_PCI_COMPLETION_STATUS_MASK;
|
|||
|
|
|||
|
if (CmdListStatus & IDA_PCI_COMPLETION_ERROR) {
|
|||
|
|
|||
|
DebugPrint((1,"IdaInterrupt: DAZZLER ERROR bit 0 set\n"));
|
|||
|
|
|||
|
CmdListStatus = RH_BAD_COMMAND_LIST;
|
|||
|
}
|
|||
|
|
|||
|
physicalAddress &= IDA_PCI_PHYS_ADDR_MASK;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DebugPrint((1,"IdaInterrupt: ERROR - unknown HBA\n"));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// As a sanity check make sure physical address is not zero.
|
|||
|
//
|
|||
|
|
|||
|
if (!physicalAddress) {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"IdaInterrupt: Physical address is zero\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Log this error.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortLogError(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_INTERNAL_ADAPTER_ERROR,
|
|||
|
2);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the virtual command list address.
|
|||
|
//
|
|||
|
|
|||
|
commandList =
|
|||
|
ScsiPortGetVirtualAddress(deviceExtension,
|
|||
|
ScsiPortConvertUlongToPhysicalAddress(
|
|||
|
physicalAddress));
|
|||
|
|
|||
|
DebugPrint((9, "Phys=%x %x\n",physicalAddress,commandList));
|
|||
|
|
|||
|
//
|
|||
|
// As a sanity check make sure command list is not zero.
|
|||
|
//
|
|||
|
|
|||
|
if (!commandList) {
|
|||
|
|
|||
|
DebugPrint((1, "IdaInterrupt: Command list is zero\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Log this error.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortLogError(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_INTERNAL_ADAPTER_ERROR,
|
|||
|
3);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for double completion.
|
|||
|
//
|
|||
|
|
|||
|
if (commandList->Flags & CL_FLAGS_REQUEST_COMPLETED) {
|
|||
|
|
|||
|
DebugPrint((1, "IdaInterrupt: Double completion %x\n",
|
|||
|
commandList));
|
|||
|
|
|||
|
//
|
|||
|
// Log this error.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortLogError(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_INTERNAL_ADAPTER_ERROR,
|
|||
|
4);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
commandList->Flags |= CL_FLAGS_REQUEST_COMPLETED;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((3,"ErrorCode=%x\n", commandList->RequestHeader.ErrorCode));
|
|||
|
|
|||
|
if (CmdListStatus & RH_BAD_COMMAND_LIST)
|
|||
|
commandList->RequestHeader.ErrorCode |= RH_BAD_COMMAND_LIST;
|
|||
|
|
|||
|
//
|
|||
|
// Check request block error code.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((3,"ErrorCode=%x\n", commandList->RequestHeader.ErrorCode));
|
|||
|
|
|||
|
switch (commandList->RequestHeader.ErrorCode & ~RH_WARNING) {
|
|||
|
|
|||
|
case RH_SUCCESS:
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case RH_FATAL_ERROR:
|
|||
|
|
|||
|
status = SRB_STATUS_ERROR;
|
|||
|
break;
|
|||
|
|
|||
|
case RH_RECOVERABLE_ERROR:
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case RH_INVALID_REQUEST:
|
|||
|
|
|||
|
status = SRB_STATUS_INVALID_REQUEST;
|
|||
|
break;
|
|||
|
|
|||
|
case RH_REQUEST_ABORTED:
|
|||
|
|
|||
|
status = SRB_STATUS_ABORTED;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
status = SRB_STATUS_ERROR;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get SRB.
|
|||
|
//
|
|||
|
|
|||
|
srb = commandList->SrbAddress;
|
|||
|
|
|||
|
//
|
|||
|
// As a sanity check make sure SRB is not zero.
|
|||
|
//
|
|||
|
|
|||
|
if (!srb) {
|
|||
|
|
|||
|
if (!commandList->Flags & CL_FLAGS_IDENTIFY_REQUEST) {
|
|||
|
|
|||
|
DebugPrint((1, "IdaInterrupt: SRB is zero\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Log this error.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortLogError(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
SP_INTERNAL_ADAPTER_ERROR,
|
|||
|
5);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (srb->Function == SRB_FUNCTION_IO_CONTROL) {
|
|||
|
|
|||
|
pSrb = (PSRB_IO_CONTROL)srb->DataBuffer;
|
|||
|
|
|||
|
switch (pSrb->ControlCode) {
|
|||
|
case CPQ_IOCTL_PASSTHROUGH:
|
|||
|
{
|
|||
|
dataPointer = (PIDA_ERROR_BITS)((PUCHAR)srb->DataBuffer
|
|||
|
+ srb->DataTransferLength
|
|||
|
- sizeof(IDA_ERROR_BITS));
|
|||
|
|
|||
|
if (CmdListStatus & RH_BAD_COMMAND_LIST) {
|
|||
|
DebugPrint((1,
|
|||
|
"IdaInterrupt: BAD_COMMAND_LIST error for PASSTHRU to %x\n",
|
|||
|
deviceExtension));
|
|||
|
|
|||
|
dataPointer->ControllerError = RH_BAD_COMMAND_LIST |
|
|||
|
(ULONG)commandList->RequestHeader.ErrorCode;
|
|||
|
} else {
|
|||
|
dataPointer->ControllerError =
|
|||
|
(ULONG)commandList->RequestHeader.ErrorCode;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CPQ_IOCTL_SCSIPASSTHROUGH:
|
|||
|
{
|
|||
|
|
|||
|
PSCSI_BUFFER_HEADER dataPacket;
|
|||
|
ULONG bufferOffset;
|
|||
|
|
|||
|
if (commandList->RequestHeader.BlockNumber == 1) {
|
|||
|
DebugPrint((3,
|
|||
|
"IdaInterrupt: SCSIPASSTHRU intermediate copy needed.\n"));
|
|||
|
|
|||
|
//
|
|||
|
// if BlockNumber == 1 then need to copy the data at the end of the
|
|||
|
// commandList into the user buffer.
|
|||
|
//
|
|||
|
|
|||
|
ReturnPointer = (PUCHAR)srb->DataBuffer
|
|||
|
+ sizeof(SRB_IO_CONTROL)
|
|||
|
+ sizeof(MAP_PARAMETER_PACKET);
|
|||
|
|
|||
|
MovePointer = (PUCHAR)commandList + sizeof(SG_DESCRIPTOR)
|
|||
|
+ sizeof(COMMAND_LIST_HEADER)
|
|||
|
+ sizeof(REQUEST_HEADER)
|
|||
|
+ sizeof(SCSI_PASSTHRU);
|
|||
|
|
|||
|
IdaMoveMemory(ReturnPointer, MovePointer,
|
|||
|
commandList->SgDescriptor[0].Length);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// setup the return fields in the return data area.
|
|||
|
//
|
|||
|
|
|||
|
bufferOffset = sizeof(SRB_IO_CONTROL) + sizeof(SCSI_PASSTHRU);
|
|||
|
|
|||
|
dataPacket = (PSCSI_BUFFER_HEADER)((PUCHAR)srb->DataBuffer
|
|||
|
+ bufferOffset);
|
|||
|
|
|||
|
dataPacket->CmdError = (UCHAR)commandList->RequestHeader.ErrorCode;
|
|||
|
|
|||
|
dataPacket->device_status =
|
|||
|
((PSCSI_PASSTHRU) (&commandList->
|
|||
|
SgDescriptor[
|
|||
|
commandList->
|
|||
|
RequestHeader.ScatterGatherCount
|
|||
|
].Length))->scsi_header.device_status;
|
|||
|
|
|||
|
dataPacket->machine_error =
|
|||
|
((PSCSI_PASSTHRU) (&commandList->
|
|||
|
SgDescriptor[commandList->RequestHeader.
|
|||
|
ScatterGatherCount
|
|||
|
].
|
|||
|
Length))->scsi_header.machine_error;
|
|||
|
|
|||
|
if (CmdListStatus & RH_BAD_COMMAND_LIST) {
|
|||
|
DebugPrint((1,
|
|||
|
"IdaInterrupt: BAD_COMMAND_LIST error for SCSI PASSTHRU to %x\n",
|
|||
|
deviceExtension));
|
|||
|
|
|||
|
dataPacket->CmdError = (UCHAR)(RH_BAD_COMMAND_LIST |
|
|||
|
commandList->RequestHeader.ErrorCode);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
|
|||
|
} // end switch
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
srb->SrbStatus = status;
|
|||
|
|
|||
|
//
|
|||
|
// Inform system that this request is complete.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortNotification(RequestComplete, deviceExtension, srb);
|
|||
|
|
|||
|
//
|
|||
|
// Check if any requests need restarting.
|
|||
|
//
|
|||
|
|
|||
|
if (deviceExtension->RestartRequests) {
|
|||
|
|
|||
|
//
|
|||
|
// Get pointer to head of list.
|
|||
|
//
|
|||
|
|
|||
|
nextCommand = deviceExtension->RestartRequests;
|
|||
|
deviceExtension->RestartRequests = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Try to restart each request in the list.
|
|||
|
//
|
|||
|
|
|||
|
while (nextCommand) {
|
|||
|
|
|||
|
commandList = nextCommand;
|
|||
|
nextCommand = nextCommand->NextEntry;
|
|||
|
|
|||
|
DebugPrint((1, "IdaInterrupt: Restarting request %x\n",
|
|||
|
commandList));
|
|||
|
|
|||
|
//
|
|||
|
// Submit command list to controller.
|
|||
|
//
|
|||
|
|
|||
|
SubmitCommandList(deviceExtension, commandList);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // IdaInterrupt();
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetDiskIdentifyData(
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|||
|
IN PVOID HwDeviceExtension,
|
|||
|
IN PCOMMAND_LIST CommandList,
|
|||
|
IN ULONG DriveNumber,
|
|||
|
IN UCHAR Command
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Issue request to get identify data for this drive. This
|
|||
|
routine has been modified for SMART-2 controller support.
|
|||
|
Specifically, we are not accepting rescans for added logical
|
|||
|
drives at run-time. Two functions have been added to
|
|||
|
disable/enable controller interrupts while requesting details
|
|||
|
from the firmware.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
CommandList - Buffer for building request to controller.
|
|||
|
DriveNumber - Identifies drive on controller.
|
|||
|
Command - IDA command code.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
ULONG length;
|
|||
|
ULONG i;
|
|||
|
BOOLEAN intrtn;
|
|||
|
|
|||
|
//
|
|||
|
// Disable controller interrupts. This action was added with support
|
|||
|
// for the SMART-2 controllers and online logical drive configuration.
|
|||
|
// Following the addition of one or more logical drives, a rescan will
|
|||
|
// request information about the new drives. Since this routine will
|
|||
|
// return requested information, we cannot simply submit the command
|
|||
|
// and exit. To reduce the number of outstanding requests we find at
|
|||
|
// the controller while searching for ours, interrupts are temporarily
|
|||
|
// disabled at all array controllers. The impact of this should not be
|
|||
|
// too significant, as we should not be rescanning often.
|
|||
|
//
|
|||
|
|
|||
|
IdaDisableInts(deviceExtension);
|
|||
|
|
|||
|
//
|
|||
|
// load srb for interrupt routine...
|
|||
|
//
|
|||
|
|
|||
|
CommandList->SrbAddress = Srb;
|
|||
|
|
|||
|
//
|
|||
|
// Set up Command List Header.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->CommandListHeader.LogicalDriveNumber = (UCHAR)DriveNumber;
|
|||
|
CommandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate no notification required.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->CommandListHeader.Flags = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Zero out unused fields.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->RequestHeader.NextRequestOffset = 0;
|
|||
|
CommandList->RequestHeader.ErrorCode = RH_SUCCESS;
|
|||
|
CommandList->RequestHeader.Reserved = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Determine command.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->RequestHeader.CommandByte = Command;
|
|||
|
|
|||
|
//
|
|||
|
// Set up request control fields.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->RequestHeader.BlockCount = 1;
|
|||
|
CommandList->RequestHeader.BlockNumber = 0;
|
|||
|
CommandList->Flags = CL_FLAGS_IDENTIFY_REQUEST;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in scatter/gather entry.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->SgDescriptor[0].Length = 512;
|
|||
|
|
|||
|
CommandList->SgDescriptor[0].Address =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong(
|
|||
|
ScsiPortGetPhysicalAddress(HwDeviceExtension,
|
|||
|
NULL,
|
|||
|
deviceExtension->
|
|||
|
IdentifyBuffer,
|
|||
|
&length));
|
|||
|
|
|||
|
//
|
|||
|
// Calculate size of command list.
|
|||
|
//
|
|||
|
|
|||
|
CommandList->RequestHeader.ScatterGatherCount=1;
|
|||
|
CommandList->CommandListSize = sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SG_DESCRIPTOR);
|
|||
|
|
|||
|
//
|
|||
|
// Submit command list to controller.
|
|||
|
//
|
|||
|
|
|||
|
SubmitCommandList(deviceExtension, CommandList);
|
|||
|
|
|||
|
DebugPrint((1, "GetDiskIdentifyData: Command Submitted:\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Poll interrupt routine. We are planning to poll for quite
|
|||
|
// some time. It appears that the initial request made to the
|
|||
|
// Dazzler/P board takes sometime and if we don't wait long
|
|||
|
// enough here, we will have problems at init and probably
|
|||
|
// anytime a rescan is requested. Current setting is 6 minutes
|
|||
|
// which is probably too long, but.....
|
|||
|
//
|
|||
|
|
|||
|
for (i=0; i < 360000; i++) {
|
|||
|
|
|||
|
//
|
|||
|
// Call interrupt routine directly.
|
|||
|
//
|
|||
|
|
|||
|
IdaInterrupt(HwDeviceExtension);
|
|||
|
|
|||
|
DebugPrint((1, "GetDiskIdentifyData: IdaInterrupt called:\n"));
|
|||
|
|
|||
|
// check for my completion...
|
|||
|
if (CommandList->Flags & CL_FLAGS_REQUEST_COMPLETED) {
|
|||
|
|
|||
|
//
|
|||
|
// Check status of completed request.
|
|||
|
//
|
|||
|
|
|||
|
if ((CommandList->RequestHeader.ErrorCode & ~RH_WARNING) ==
|
|||
|
RH_SUCCESS) {
|
|||
|
IdaEnableInts(deviceExtension);
|
|||
|
return TRUE;
|
|||
|
} else {
|
|||
|
DebugPrint((1, "GetDiskIdentifyData: Command failed: %x\n",
|
|||
|
CommandList->RequestHeader.ErrorCode));
|
|||
|
|
|||
|
//
|
|||
|
// Command failed.
|
|||
|
//
|
|||
|
|
|||
|
IdaEnableInts(deviceExtension);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ScsiPortStallExecution(1000);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IdaEnableInts(deviceExtension);
|
|||
|
return FALSE;
|
|||
|
|
|||
|
} // end GetDiskIdentifyData()
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaStartIo(
|
|||
|
IN PVOID HwDeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is routine is called by the system to start a request on the adapter.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Address of adapter storage area.
|
|||
|
Srb - Address of the request to be started.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - The request has been started.
|
|||
|
FALSE - The controller was busy.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|||
|
ULONG i;
|
|||
|
UCHAR status;
|
|||
|
|
|||
|
switch (Srb->Function) {
|
|||
|
|
|||
|
case SRB_FUNCTION_RESET_BUS:
|
|||
|
|
|||
|
if (!IdaResetBus(deviceExtension, Srb->PathId)) {
|
|||
|
status = SRB_STATUS_ERROR;
|
|||
|
} else {
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case SRB_FUNCTION_EXECUTE_SCSI:
|
|||
|
|
|||
|
switch (Srb->Cdb[0]) {
|
|||
|
|
|||
|
case SCSIOP_WRITE:
|
|||
|
case SCSIOP_READ:
|
|||
|
|
|||
|
//
|
|||
|
// Build command list from SRB.
|
|||
|
//
|
|||
|
|
|||
|
BuildCommandList(deviceExtension,
|
|||
|
Srb);
|
|||
|
|
|||
|
//
|
|||
|
// Submit command list to controller.
|
|||
|
//
|
|||
|
|
|||
|
SubmitCommandList(deviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension);
|
|||
|
|
|||
|
status = SRB_STATUS_PENDING;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSIOP_TEST_UNIT_READY:
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSIOP_READ_CAPACITY:
|
|||
|
|
|||
|
//
|
|||
|
// Get logical unit extension.
|
|||
|
//
|
|||
|
luExtension =
|
|||
|
ScsiPortGetLogicalUnit(deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
Srb->TargetId,
|
|||
|
Srb->Lun);
|
|||
|
|
|||
|
if (luExtension) {
|
|||
|
ULONG blockSize = luExtension->IdentifyData.BlockLength;
|
|||
|
|
|||
|
//
|
|||
|
// Get blocksize and number of blocks from identify
|
|||
|
// data.
|
|||
|
//
|
|||
|
REVERSE_BYTES
|
|||
|
(&((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock,
|
|||
|
&blockSize);
|
|||
|
|
|||
|
REVERSE_BYTES
|
|||
|
(&((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress,
|
|||
|
&luExtension->IdentifyData.NumberOfBlocks);
|
|||
|
|
|||
|
DebugPrint((1, "IdaStartIo: Block size %x\n",
|
|||
|
luExtension->IdentifyData.BlockLength));
|
|||
|
|
|||
|
DebugPrint((1, "IdaStartIo: Number of blocks %x\n",
|
|||
|
luExtension->IdentifyData.NumberOfBlocks));
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
status = SRB_STATUS_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case SCSIOP_INQUIRY:
|
|||
|
//
|
|||
|
// Only respond at logical unit 0;
|
|||
|
//
|
|||
|
|
|||
|
if (Srb->Lun != 0) {
|
|||
|
//
|
|||
|
// Indicate no device found at this address.
|
|||
|
//
|
|||
|
status = SRB_STATUS_SELECTION_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get number of logical drives.
|
|||
|
//
|
|||
|
|
|||
|
if (GetDiskIdentifyData(Srb, HwDeviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension,
|
|||
|
0, RH_COMMAND_IDENTIFY_CONTROLLER)) {
|
|||
|
deviceExtension->NumberOfLogicalDrives = (ULONG)
|
|||
|
((PIDENTIFY_CONTROLLER)
|
|||
|
deviceExtension->IdentifyBuffer)->NumberLogicalDrives;
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"IdaStartIo: Number of logical drives %x\n",
|
|||
|
deviceExtension->NumberOfLogicalDrives));
|
|||
|
|
|||
|
//
|
|||
|
// save off the identify controller buffer to the
|
|||
|
// extension area
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortMoveMemory(&deviceExtension->IdentifyData,
|
|||
|
deviceExtension->IdentifyBuffer,
|
|||
|
sizeof(IDENTIFY_CONTROLLER));
|
|||
|
|
|||
|
} else {
|
|||
|
DebugPrint((1,
|
|||
|
"IdaFindAdapters: Get controller information failed\n"));
|
|||
|
status = SRB_STATUS_ERROR;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if this is for one of the reported logical drives.
|
|||
|
//
|
|||
|
|
|||
|
if (Srb->TargetId >=
|
|||
|
deviceExtension->NumberOfLogicalDrives) {
|
|||
|
status = SRB_STATUS_SELECTION_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Issue identify command.
|
|||
|
//
|
|||
|
|
|||
|
if (!GetDiskIdentifyData(Srb, HwDeviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension,
|
|||
|
Srb->TargetId,
|
|||
|
RH_COMMAND_IDENTIFY_LOGICAL_DRIVES)) {
|
|||
|
status = SRB_STATUS_SELECTION_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get logical unit extension.
|
|||
|
//
|
|||
|
|
|||
|
luExtension = ScsiPortGetLogicalUnit(deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
Srb->TargetId,
|
|||
|
Srb->Lun);
|
|||
|
|
|||
|
//
|
|||
|
// Copy data from buffer to logical unit extension.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortMoveMemory(&luExtension->IdentifyData,
|
|||
|
deviceExtension->IdentifyBuffer,
|
|||
|
sizeof(IDENTIFY_LOGICAL_DRIVE));
|
|||
|
//
|
|||
|
// Issue sense configuration command.
|
|||
|
//
|
|||
|
|
|||
|
if (!GetDiskIdentifyData(Srb, HwDeviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension,
|
|||
|
Srb->TargetId,
|
|||
|
RH_COMMAND_SENSE_CONFIGURATION)) {
|
|||
|
|
|||
|
status = SRB_STATUS_SELECTION_TIMEOUT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy data from buffer to logical unit extension.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortMoveMemory(&luExtension->SenseData,
|
|||
|
deviceExtension->IdentifyBuffer,
|
|||
|
sizeof(SENSE_CONFIGURATION));
|
|||
|
|
|||
|
//
|
|||
|
// Zero INQUIRY data structure.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < Srb->DataTransferLength; i++) {
|
|||
|
((PUCHAR)Srb->DataBuffer)[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compaq IDA only supports disks.
|
|||
|
//
|
|||
|
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->DeviceType = DIRECT_ACCESS_DEVICE;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in vendor identification fields.
|
|||
|
//
|
|||
|
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[0] = 'C';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[1] = 'o';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[2] = 'm';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[3] = 'p';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[4] = 'a';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[5] = 'q';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[6] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->VendorId[7] = ' ';
|
|||
|
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[0] = 'D';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[1] = 'i';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[2] = 's';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[3] = 'k';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[4] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[5] = 'A';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[6] = 'r';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[7] = 'r';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[8] = 'a';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[9] = 'y';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[10] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[11] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[12] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[13] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[14] = ' ';
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductId[15] = ' ';
|
|||
|
|
|||
|
//
|
|||
|
// Move firmware revision from IDENTIFY data to
|
|||
|
// product revision in INQUIRY data.
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < 4; i++) {
|
|||
|
((PINQUIRYDATA)Srb->DataBuffer)->ProductRevisionLevel[i] =
|
|||
|
deviceExtension->IdentifyData.FirmwareRevision[i];
|
|||
|
}
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSIOP_VERIFY:
|
|||
|
|
|||
|
//
|
|||
|
// Compaq array controllers hotfix bad sectors as they are
|
|||
|
// encountered. A sector verify in unnecessary.
|
|||
|
//
|
|||
|
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
status = SRB_STATUS_INVALID_REQUEST;
|
|||
|
break;
|
|||
|
|
|||
|
} // end switch (Srb->Cdb[0])
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Issue FLUSH/DISABLE if shutdown command.
|
|||
|
//
|
|||
|
|
|||
|
case SRB_FUNCTION_SHUTDOWN:
|
|||
|
|
|||
|
BuildFlushDisable(deviceExtension,Srb);
|
|||
|
SubmitCommandList(deviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension);
|
|||
|
|
|||
|
status = SRB_STATUS_PENDING;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Do not need the flush command since all controllers have
|
|||
|
// memory that is battery backed up. Just return success.
|
|||
|
//
|
|||
|
|
|||
|
case SRB_FUNCTION_FLUSH:
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case SRB_FUNCTION_IO_CONTROL: {
|
|||
|
PCPQ_IDA_IDENTIFY pIoctlBuffer;
|
|||
|
|
|||
|
pIoctlBuffer = (PCPQ_IDA_IDENTIFY)Srb->DataBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Status is returned mainly in 2 fields to the calling thread.
|
|||
|
// These 2 fields determine if other status fields are valid to
|
|||
|
// check. If the request is not a valid request for this driver
|
|||
|
// then the Header.ReturnCode is not modified and the
|
|||
|
// Srb->SrbStatus is set to SRB_STATUS_INVALID_REQUEST. If
|
|||
|
// the request is valid for this driver then Srb->SrbStatus
|
|||
|
// is always returned as SRB_STATUS_SUCCESS and the
|
|||
|
// Header.ReturnCode contains information concerning the
|
|||
|
// status of the particular request.
|
|||
|
//
|
|||
|
|
|||
|
if (!IdaStrCmp(pIoctlBuffer->Header.Signature, IDA_SIGNATURE)) {
|
|||
|
|
|||
|
if (IdaProcessIoctl(deviceExtension,
|
|||
|
pIoctlBuffer,
|
|||
|
Srb) == CPQ_CIM_ISSUED) {
|
|||
|
|
|||
|
status = SRB_STATUS_PENDING;
|
|||
|
|
|||
|
} else {
|
|||
|
status = SRB_STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
status = SRB_STATUS_INVALID_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
status = SRB_STATUS_INVALID_REQUEST;
|
|||
|
|
|||
|
} // end switch
|
|||
|
|
|||
|
//
|
|||
|
// Check if SRB should be completed.
|
|||
|
//
|
|||
|
|
|||
|
if (status != SRB_STATUS_PENDING) {
|
|||
|
|
|||
|
//
|
|||
|
// Set status in SRB.
|
|||
|
//
|
|||
|
|
|||
|
Srb->SrbStatus = status;
|
|||
|
|
|||
|
//
|
|||
|
// Inform system that this request is complete.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortNotification(RequestComplete,
|
|||
|
deviceExtension,
|
|||
|
Srb);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate to system that the controller can take another request
|
|||
|
// for this device.
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortNotification(NextLuRequest,
|
|||
|
deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
Srb->TargetId,
|
|||
|
Srb->Lun);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // end IdaStartIo()
|
|||
|
|
|||
|
ULONG
|
|||
|
IdaProcessIoctl(
|
|||
|
IN PDEVICE_EXTENSION deviceExtension,
|
|||
|
PVOID pIoctlBuffer,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG currentId;
|
|||
|
ULONG numberOfLuns;
|
|||
|
ULONG status;
|
|||
|
PCPQ_IDA_IDENTIFY pCPQ = pIoctlBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Build command list from SRB.
|
|||
|
//
|
|||
|
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
|
|||
|
DebugPrint((3,
|
|||
|
"IdaProcessIoctl(): parsing request %d for PathId=%d TargetId=%d Lun=%d\n",
|
|||
|
pCPQ->Header.ControlCode,Srb->PathId,Srb->TargetId,Srb->Lun));
|
|||
|
|
|||
|
switch(pCPQ->Header.ControlCode) {
|
|||
|
case CPQ_IOCTL_IDENTIFY_DRIVER: {
|
|||
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|||
|
|
|||
|
PMAP_HEADER header = (PMAP_HEADER)((PUCHAR)Srb->DataBuffer +
|
|||
|
sizeof(SRB_IO_CONTROL));
|
|||
|
|
|||
|
IdaMoveMemory(header->DriverName, IDA_DRIVER_NAME,
|
|||
|
sizeof(header->DriverName));
|
|||
|
|
|||
|
header->DriverMajorVersion = IDA_MAJOR_VERSION;
|
|||
|
header->DriverMinorVersion = IDA_MINOR_VERSION;
|
|||
|
|
|||
|
header->ControllerCount = 1;
|
|||
|
|
|||
|
//
|
|||
|
// We need to give back the number of LUNs not the actual
|
|||
|
// number of LUNs available because LU extensions are not
|
|||
|
// discarded when a drive has been removed or taken off-line.
|
|||
|
//
|
|||
|
|
|||
|
currentId = 0;
|
|||
|
numberOfLuns = 0;
|
|||
|
|
|||
|
luExtension = ScsiPortGetLogicalUnit(deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
(UCHAR)currentId,
|
|||
|
Srb->Lun);
|
|||
|
while (luExtension) {
|
|||
|
numberOfLuns++;
|
|||
|
currentId++;
|
|||
|
luExtension = ScsiPortGetLogicalUnit(deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
(UCHAR)currentId,
|
|||
|
Srb->Lun);
|
|||
|
}
|
|||
|
|
|||
|
header->LogicalDiskCount = numberOfLuns;
|
|||
|
|
|||
|
header->RequiredMemory = sizeof(MAP_CONTROLLER_DATA) +
|
|||
|
(sizeof(MAP_LOGICALDRIVE_DATA) * numberOfLuns);
|
|||
|
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CPQ_IOCTL_IDENTIFY_CONTROLLERS: {
|
|||
|
ULONG i;
|
|||
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|||
|
PMAP_LOGICALDRIVE_DATA LdriveData;
|
|||
|
PMAP_CONTROLLER_DATA controllerData;
|
|||
|
|
|||
|
//
|
|||
|
// Take care of the controller struct first
|
|||
|
//
|
|||
|
|
|||
|
controllerData = (PMAP_CONTROLLER_DATA)
|
|||
|
((PUCHAR)Srb->DataBuffer + sizeof(SRB_IO_CONTROL));
|
|||
|
|
|||
|
controllerData->NextController = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// calculate offset from the beginning of the controller data area.
|
|||
|
//
|
|||
|
|
|||
|
controllerData->LogicalDriveList =
|
|||
|
(PMAP_LOGICALDRIVE_DATA)(controllerData + 1);
|
|||
|
controllerData->EisaId = deviceExtension->EisaId;
|
|||
|
controllerData->BmicIoAddress = (ULONG)deviceExtension->Bmic;
|
|||
|
controllerData->IrqLevel = deviceExtension->IrqLevel;
|
|||
|
|
|||
|
IdaMoveMemory((PUCHAR)&controllerData->ControllerInfo,
|
|||
|
(PUCHAR)&deviceExtension->IdentifyData,
|
|||
|
sizeof(IDENTIFY_CONTROLLER));
|
|||
|
|
|||
|
//
|
|||
|
// Now look for logical units until one is not found. In the future
|
|||
|
// support non-consecutive logical units, for now, stop searching.
|
|||
|
//
|
|||
|
|
|||
|
currentId = 0;
|
|||
|
luExtension = ScsiPortGetLogicalUnit(deviceExtension,
|
|||
|
Srb->PathId,
|
|||
|
(UCHAR)currentId,
|
|||
|
Srb->Lun);
|
|||
|
|
|||
|
LdriveData = controllerData->LogicalDriveList;
|
|||
|
|
|||
|
while (luExtension) {
|
|||
|
|
|||
|
//
|
|||
|
// Set the DeviceLengthXX sizes to 0, removed from CIM interface.
|
|||
|
//
|
|||
|
|
|||
|
LdriveData->NextLogicalDrive = LdriveData + 1;
|
|||
|
LdriveData->Controller = controllerData;
|
|||
|
LdriveData->LogicalDriveNumber = currentId;
|
|||
|
LdriveData->SystemDriveNumber = 0;
|
|||
|
LdriveData->DeviceLengthLo = 0;
|
|||
|
LdriveData->DeviceLengthHi = 0;
|
|||
|
LdriveData->SectorSize = (ULONG)(1 << deviceExtension->SectorShift);
|
|||
|
IdaMoveMemory((PUCHAR)&LdriveData->Configuration,
|
|||
|
(PUCHAR)&luExtension->SenseData,
|
|||
|
sizeof(SENSE_CONFIGURATION));
|
|||
|
|
|||
|
IdaMoveMemory((PUCHAR)&LdriveData->LogicalDriveInfo,
|
|||
|
(PUCHAR)&luExtension->IdentifyData,
|
|||
|
sizeof(IDENTIFY_LOGICAL_DRIVE));
|
|||
|
|
|||
|
currentId++;
|
|||
|
luExtension =
|
|||
|
ScsiPortGetLogicalUnit(deviceExtension, Srb->PathId,
|
|||
|
(UCHAR)currentId, Srb->Lun);
|
|||
|
|
|||
|
if (!luExtension) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
LdriveData = LdriveData + 1;
|
|||
|
}
|
|||
|
|
|||
|
LdriveData->NextLogicalDrive = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Need to convert NextLogicalDrive fields to offsets from virtual
|
|||
|
// addresses. currentId is the last ID that was found.
|
|||
|
//
|
|||
|
|
|||
|
if (currentId) {
|
|||
|
controllerData->LogicalDriveList =
|
|||
|
(PMAP_LOGICALDRIVE_DATA)sizeof(MAP_CONTROLLER_DATA);
|
|||
|
LdriveData = (PMAP_LOGICALDRIVE_DATA)(controllerData + 1);
|
|||
|
LdriveData->NextLogicalDrive = NULL;
|
|||
|
|
|||
|
for (i=0;i<(currentId-1);i++,LdriveData++) {
|
|||
|
LdriveData->NextLogicalDrive =
|
|||
|
(PMAP_LOGICALDRIVE_DATA)(sizeof(MAP_CONTROLLER_DATA)
|
|||
|
+ ((i+1)*sizeof(MAP_LOGICALDRIVE_DATA)));
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
controllerData->LogicalDriveList = NULL;
|
|||
|
}
|
|||
|
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CPQ_IOCTL_PASSTHROUGH:
|
|||
|
case CPQ_IOCTL_SCSIPASSTHROUGH: {
|
|||
|
|
|||
|
if (!(BuildCIMList(deviceExtension, Srb) == CPQ_CIM_CMDBUILT)) {
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Submit command list to controller.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((3,
|
|||
|
"IdaProcessIoctl(): Submitting PASSTHRU request to %x\n",
|
|||
|
deviceExtension));
|
|||
|
|
|||
|
SubmitCommandList(deviceExtension,
|
|||
|
(PCOMMAND_LIST)Srb->SrbExtension);
|
|||
|
status = CPQ_CIM_ISSUED;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CPQ_IOCTL_CONFIGURATION_INFO: {
|
|||
|
PIDA_CONFIGURATION pConfigData;
|
|||
|
|
|||
|
//
|
|||
|
// Setup pointer to the Config Data area
|
|||
|
//
|
|||
|
|
|||
|
pConfigData = (PIDA_CONFIGURATION)
|
|||
|
((PUCHAR)Srb->DataBuffer + sizeof(SRB_IO_CONTROL));
|
|||
|
|
|||
|
pConfigData->ulBaseMemoryAddress =
|
|||
|
deviceExtension->HBAConfiguration.ulBaseIOAddress;
|
|||
|
pConfigData->bIoBusType =
|
|||
|
deviceExtension->HBAConfiguration.bHBAIoBusType;
|
|||
|
pConfigData->ulBaseIOAddress = deviceExtension->BaseIOAddress;
|
|||
|
pConfigData->ulControllerID = deviceExtension->EisaId;
|
|||
|
IdaMoveMemory((PUCHAR)&pConfigData->IoBusData,
|
|||
|
(PUCHAR)&deviceExtension->HBAConfiguration.HBAIoBusData,
|
|||
|
sizeof(union _IO_BUS_DATA));
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
pCPQ->Header.ReturnCode = CPQ_SCSI_ERR_BAD_CNTL_CODE;
|
|||
|
status = CPQ_CIM_COMPLETED;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
BuildFlushDisable(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine builds a shutdown command list suitable for submission to the
|
|||
|
Compaq IDA controller, from an SRB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Address of adapter storage area.
|
|||
|
Srb - System request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCOMMAND_LIST commandList = Srb->SrbExtension;
|
|||
|
ULONG length,i;
|
|||
|
PFLUSH_DISABLE pFlushDisable;
|
|||
|
PSG_DESCRIPTOR sgList;
|
|||
|
|
|||
|
sgList = commandList->SgDescriptor;
|
|||
|
|
|||
|
// clear out reserved area
|
|||
|
|
|||
|
for (i=0;i<MAXIMUM_SG_DESCRIPTORS;i++) {
|
|||
|
sgList[i].Address = 0;
|
|||
|
sgList[i].Length = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save SRB address for interrupt routine.
|
|||
|
//
|
|||
|
|
|||
|
commandList->SrbAddress = Srb;
|
|||
|
|
|||
|
//
|
|||
|
// Set up Command List Header.
|
|||
|
//
|
|||
|
|
|||
|
commandList->CommandListHeader.LogicalDriveNumber = 0;
|
|||
|
|
|||
|
commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
|
|||
|
|
|||
|
commandList->CommandListHeader.Flags =
|
|||
|
CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
|
|||
|
|
|||
|
//
|
|||
|
// Set up Request Header.
|
|||
|
//
|
|||
|
// Terminate request list.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.NextRequestOffset = 0;
|
|||
|
|
|||
|
commandList->Flags = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Reset error code.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.ErrorCode = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Clear reserved field.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.Reserved = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Check for special Compaq passthrough command.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.BlockCount = (USHORT)1;
|
|||
|
commandList->RequestHeader.BlockNumber = (ULONG)0;
|
|||
|
commandList->RequestHeader.CommandByte = RH_COMMAND_FLUSH_DISABLE_CACHE;
|
|||
|
|
|||
|
pFlushDisable = (PFLUSH_DISABLE)&(sgList[1].Length);
|
|||
|
pFlushDisable->disable_flag = 1; //disable cache also
|
|||
|
|
|||
|
sgList[0].Address =
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
NULL,
|
|||
|
commandList,
|
|||
|
&length).LowPart;
|
|||
|
|
|||
|
//
|
|||
|
// ScsiPortGetPhysicalAddress only accepts certain virtual addresses,
|
|||
|
// so use the commandList and then increment over to the second s/g
|
|||
|
// descriptor where the structure for the flush/disable command is
|
|||
|
// located.
|
|||
|
//
|
|||
|
// Note that since it is difficult to allocate nonpaged memory at this
|
|||
|
// level of the driver, and the command has 510 bytes of reserved
|
|||
|
// area in the structure then memory will be retrieved by the controller
|
|||
|
// that is past the end of the defined commandlist allocated memory.
|
|||
|
// This will not be a problem unless the memory extends beyond the
|
|||
|
// actual physical end of memory in the machine.
|
|||
|
//
|
|||
|
// The IDA-2 controller requires a multiple of 512 for the length so
|
|||
|
// to avoid code that is controller dependent just use 512 that is
|
|||
|
// accepted by all controllers. This command returns an BAD REQUEST
|
|||
|
// when issued to IDA controllers since those controllers have no
|
|||
|
// memory on the board.
|
|||
|
//
|
|||
|
|
|||
|
sgList[0].Address += (sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SG_DESCRIPTOR));
|
|||
|
|
|||
|
sgList[0].Length = 512;
|
|||
|
commandList->RequestHeader.BlockNumber = 0;
|
|||
|
commandList->RequestHeader.ScatterGatherCount=1;
|
|||
|
|
|||
|
//
|
|||
|
// Build physical address translation list entry.
|
|||
|
//
|
|||
|
|
|||
|
commandList->CommandListSize = (sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SG_DESCRIPTOR));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
IdaFindAdapter(
|
|||
|
IN PVOID HwDeviceExtension,
|
|||
|
IN PVOID Context,
|
|||
|
IN PVOID BusInformation,
|
|||
|
IN PCHAR ArgumentString,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|||
|
OUT PBOOLEAN Again
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function fills in the configuration information structure
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HwDeviceExtension - Supplies a pointer to the device extension.
|
|||
|
Context - Supplies adapter initialization structure.
|
|||
|
BusInformation - Unused.
|
|||
|
ArgumentString - Unused.
|
|||
|
ConfigInfo - Pointer to the configuration information structure.
|
|||
|
Again - Indicates that system should continue search for adapters.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
SP_RETURN_FOUND - Indicates adapter found.
|
|||
|
SP_RETURN_NOT_FOUND - Indicates adapter not found.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
|
|||
|
PACCESS_RANGE accessRange;
|
|||
|
DebugPrint((9,"&deviceExtension=%x\n",&deviceExtension));
|
|||
|
DebugPrint((9,"&deviceExtension->PCIoff=%x\n",&deviceExtension->PCIoff));
|
|||
|
|
|||
|
//
|
|||
|
// Get access range.
|
|||
|
//
|
|||
|
|
|||
|
accessRange = &((*(ConfigInfo->AccessRanges))[0]);
|
|||
|
|
|||
|
if (accessRange->RangeLength == 0) {
|
|||
|
|
|||
|
if (!SearchEisaBus(HwDeviceExtension, Context, ConfigInfo)) {
|
|||
|
|
|||
|
//
|
|||
|
// Tell system nothing was found and not to call again.
|
|||
|
//
|
|||
|
|
|||
|
*Again = FALSE;
|
|||
|
return SP_RETURN_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get system-mapped controller address.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->Bmic =
|
|||
|
ScsiPortGetDeviceBase(HwDeviceExtension,
|
|||
|
ConfigInfo->AdapterInterfaceType,
|
|||
|
ConfigInfo->SystemIoBusNumber,
|
|||
|
accessRange->RangeStart,
|
|||
|
accessRange->RangeLength,
|
|||
|
(BOOLEAN) !accessRange->RangeInMemory);
|
|||
|
|
|||
|
//
|
|||
|
// Complete description of controller.
|
|||
|
//
|
|||
|
|
|||
|
ConfigInfo->MaximumTransferLength = (ULONG)-1;
|
|||
|
ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS;
|
|||
|
ConfigInfo->NumberOfBuses = 1;
|
|||
|
ConfigInfo->ScatterGather = TRUE;
|
|||
|
ConfigInfo->Master = TRUE;
|
|||
|
ConfigInfo->Dma32BitAddresses = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Get noncached extension for identify requests.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->EisaId =
|
|||
|
ScsiPortReadPortUlong(&deviceExtension->Bmic->BoardId);
|
|||
|
|
|||
|
if ((deviceExtension->EisaId & IDA_EISA_ID_MASKID_LOW) >=
|
|||
|
(IDA_EISA_ID_DAZZLER & IDA_EISA_ID_MASKID_LOW)) {
|
|||
|
|
|||
|
deviceExtension->HBAConfiguration.bHBAModel = IDA_EISA_DAZZLER;
|
|||
|
|
|||
|
deviceExtension->eisapci =
|
|||
|
(PEISAPCI_CONTROLLER)(((ULONG)deviceExtension->Bmic & 0xf000));
|
|||
|
|
|||
|
DebugPrint((9,"Found EISA DAZZLER: deviceExtension->eisapci=%x\n",
|
|||
|
deviceExtension->eisapci));
|
|||
|
|
|||
|
ConfigInfo->MaximumNumberOfTargets = 32;
|
|||
|
} else {
|
|||
|
deviceExtension->HBAConfiguration.bHBAModel = IDA_BASE_CONTROLLER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Setup some vars needed for the IDENTIFY commands
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->IrqLevel = (UCHAR)ConfigInfo->BusInterruptLevel;
|
|||
|
deviceExtension->SectorShift = 9;
|
|||
|
|
|||
|
deviceExtension->IdentifyBuffer =
|
|||
|
ScsiPortGetUncachedExtension(deviceExtension,
|
|||
|
ConfigInfo,
|
|||
|
512);
|
|||
|
ConfigInfo->CachesData = TRUE;
|
|||
|
|
|||
|
deviceExtension->HBAConfiguration.HBAIoBusData.usEisaSlot =
|
|||
|
(USHORT)ConfigInfo->SlotNumber;
|
|||
|
|
|||
|
deviceExtension->BaseIOAddress = ConfigInfo->SlotNumber * 0x1000;
|
|||
|
deviceExtension->HBAConfiguration.bHBAIoBusType = EISA_BUS;
|
|||
|
|
|||
|
//
|
|||
|
// Tell system to look for more adapters.
|
|||
|
//
|
|||
|
|
|||
|
*Again = TRUE;
|
|||
|
|
|||
|
return SP_RETURN_FOUND;
|
|||
|
|
|||
|
} // end IdaFindAdapter()
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
BuildCIMList(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PSCSI_REQUEST_BLOCK Srb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine builds a command list suitable for submission to the
|
|||
|
Compaq IDA controller, from an SRB.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - Address of adapter storage area.
|
|||
|
Srb - System request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCOMMAND_LIST commandList = Srb->SrbExtension;
|
|||
|
PUCHAR dataPointer;
|
|||
|
ULONG physicalAddress;
|
|||
|
ULONG bytesLeft;
|
|||
|
ULONG descriptor;
|
|||
|
ULONG length;
|
|||
|
ULONG bufferOffset;
|
|||
|
ULONG status;
|
|||
|
PSRB_IO_CONTROL pSrb;
|
|||
|
PSCSI_PASSTHRU scsipass;
|
|||
|
|
|||
|
//
|
|||
|
// Save SRB address for interrupt routine.
|
|||
|
//
|
|||
|
|
|||
|
commandList->SrbAddress = Srb;
|
|||
|
|
|||
|
//
|
|||
|
// Set up Command List Header.
|
|||
|
//
|
|||
|
|
|||
|
commandList->CommandListHeader.LogicalDriveNumber = Srb->TargetId;
|
|||
|
commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
|
|||
|
|
|||
|
commandList->CommandListHeader.Flags =
|
|||
|
CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
|
|||
|
|
|||
|
commandList->RequestHeader.NextRequestOffset = 0;
|
|||
|
commandList->Flags = 0;
|
|||
|
commandList->RequestHeader.ErrorCode = 0;
|
|||
|
commandList->RequestHeader.Reserved = 0;
|
|||
|
commandList->RequestHeader.BlockCount = 0;
|
|||
|
commandList->RequestHeader.BlockNumber = 0;
|
|||
|
|
|||
|
status = CPQ_CIM_ERROR;
|
|||
|
pSrb = (PSRB_IO_CONTROL)Srb->DataBuffer;
|
|||
|
|
|||
|
switch (pSrb->ControlCode) {
|
|||
|
case CPQ_IOCTL_PASSTHROUGH:
|
|||
|
{
|
|||
|
PMAP_PARAMETER_PACKET pParmPkt = (PMAP_PARAMETER_PACKET)
|
|||
|
(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
|
|||
|
|
|||
|
commandList->CommandListHeader.LogicalDriveNumber = pParmPkt->TargetId;
|
|||
|
commandList->RequestHeader.BlockCount = pParmPkt->BlockCount;
|
|||
|
commandList->RequestHeader.BlockNumber = pParmPkt->BlockNumber;
|
|||
|
commandList->RequestHeader.CommandByte = pParmPkt->IdaLogicalCommand;
|
|||
|
|
|||
|
//
|
|||
|
// Build scatter/gather descriptor list.
|
|||
|
//
|
|||
|
|
|||
|
descriptor = 0;
|
|||
|
bufferOffset = sizeof(SRB_IO_CONTROL) + sizeof(MAP_PARAMETER_PACKET);
|
|||
|
dataPointer = (PUCHAR)Srb->DataBuffer + bufferOffset;
|
|||
|
bytesLeft = Srb->DataTransferLength - bufferOffset -
|
|||
|
sizeof(IDA_ERROR_BITS);
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Get physical address and length of contiguous
|
|||
|
// physical buffer.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong(
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
Srb,
|
|||
|
dataPointer,
|
|||
|
&length)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If length of physical memory is more
|
|||
|
// than bytes left in transfer, use bytes
|
|||
|
// left as final length.
|
|||
|
//
|
|||
|
|
|||
|
if (length > bytesLeft) {
|
|||
|
length = bytesLeft;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fill in descriptor.
|
|||
|
//
|
|||
|
|
|||
|
commandList->SgDescriptor[descriptor].Address = physicalAddress;
|
|||
|
commandList->SgDescriptor[descriptor].Length = length;
|
|||
|
|
|||
|
//
|
|||
|
// Adjust counts.
|
|||
|
//
|
|||
|
|
|||
|
dataPointer = dataPointer + length;
|
|||
|
bytesLeft -= length;
|
|||
|
descriptor++;
|
|||
|
|
|||
|
} while (bytesLeft);
|
|||
|
|
|||
|
//
|
|||
|
// Calculate size of command list.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.ScatterGatherCount=(UCHAR)descriptor;
|
|||
|
commandList->CommandListSize = (sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SG_DESCRIPTOR) *
|
|||
|
descriptor);
|
|||
|
|
|||
|
status = CPQ_CIM_CMDBUILT;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CPQ_IOCTL_SCSIPASSTHROUGH:
|
|||
|
|
|||
|
//
|
|||
|
// Build scatter/gather descriptor list.
|
|||
|
//
|
|||
|
|
|||
|
descriptor = 0;
|
|||
|
|
|||
|
bufferOffset = (sizeof(SRB_IO_CONTROL) +
|
|||
|
sizeof(SCSI_PASSTHRU) +
|
|||
|
sizeof(SCSI_BUFFER_HEADER));
|
|||
|
|
|||
|
dataPointer = (PUCHAR)Srb->DataBuffer + bufferOffset;
|
|||
|
bytesLeft = Srb->DataTransferLength - bufferOffset;
|
|||
|
|
|||
|
//
|
|||
|
// Get physical address and length of contiguous
|
|||
|
// physical buffer.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong(
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
Srb,
|
|||
|
dataPointer,
|
|||
|
&length)
|
|||
|
);
|
|||
|
//
|
|||
|
// get to the scsi cdb area, and then copy to the end of the cmdlist
|
|||
|
// which is after the first s/g descriptor.
|
|||
|
// modify to move after the last USED s/g area when more than 1 s/g
|
|||
|
// functions in the controller f/w.
|
|||
|
//
|
|||
|
|
|||
|
scsipass = (PSCSI_PASSTHRU)(((PUCHAR)Srb->DataBuffer +
|
|||
|
sizeof(SRB_IO_CONTROL)));
|
|||
|
IdaMoveMemory((PUCHAR)&commandList->SgDescriptor[1].Length,
|
|||
|
(PUCHAR)scsipass,
|
|||
|
sizeof(SCSI_PASSTHRU_HEADER) +
|
|||
|
scsipass->scsi_header.cdb_length
|
|||
|
);
|
|||
|
|
|||
|
bufferOffset = (sizeof(SG_DESCRIPTOR) +
|
|||
|
sizeof(COMMAND_LIST_HEADER) +
|
|||
|
sizeof(REQUEST_HEADER) +
|
|||
|
sizeof(SCSI_PASSTHRU_HEADER) +
|
|||
|
scsipass->scsi_header.cdb_length);
|
|||
|
|
|||
|
//
|
|||
|
// If length of physical memory is less than needed space
|
|||
|
// attempt to use nonpaged memory left in the command list
|
|||
|
// else return error since allocating memory is not possible
|
|||
|
// under the miniport design.
|
|||
|
//
|
|||
|
|
|||
|
if (length < bytesLeft) {
|
|||
|
|
|||
|
if (( (MAXIMUM_SG_DESCRIPTORS * sizeof(SG_DESCRIPTOR)) -
|
|||
|
sizeof(SG_DESCRIPTOR) -
|
|||
|
sizeof(SCSI_PASSTHRU)) < bytesLeft) {
|
|||
|
|
|||
|
DebugPrint((3,
|
|||
|
"BuildCIMList(): Returning CPQ_SCSI_ERR_NONCONTIGUOUS\n"));
|
|||
|
|
|||
|
pSrb->ReturnCode = CPQ_SCSI_ERR_NONCONTIGUOUS;
|
|||
|
return(CPQ_CIM_NONCONTIGUOUS);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the physical address of the start of the command list and then
|
|||
|
// increment to the first non-used byte in the s/g descriptor list.
|
|||
|
// There are limitations on what physical addresses can be obtained
|
|||
|
// from the ScsiPort calls so use what we know is nonpaged memory.
|
|||
|
//
|
|||
|
|
|||
|
physicalAddress =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong(
|
|||
|
ScsiPortGetPhysicalAddress(DeviceExtension,
|
|||
|
Srb,
|
|||
|
commandList,
|
|||
|
&length)
|
|||
|
);
|
|||
|
|
|||
|
physicalAddress += bufferOffset;
|
|||
|
|
|||
|
//
|
|||
|
// set BlockNumber to flag that a copy from end of commandList is needed
|
|||
|
// when the request is completed.
|
|||
|
//
|
|||
|
|
|||
|
commandList->RequestHeader.BlockNumber = 1;
|
|||
|
}
|
|||
|
|
|||
|
commandList->RequestHeader.CommandByte = RH_COMMAND_SCSI_PASS_THRU;
|
|||
|
commandList->CommandListSize = (USHORT)bufferOffset;
|
|||
|
commandList->SgDescriptor[descriptor].Address = physicalAddress;
|
|||
|
commandList->SgDescriptor[descriptor].Length = bytesLeft;
|
|||
|
commandList->RequestHeader.ScatterGatherCount = 1;
|
|||
|
commandList->CommandListHeader.LogicalDriveNumber = 0;
|
|||
|
|
|||
|
status = CPQ_CIM_CMDBUILT;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"BuildCIMList(): Returning CPQ_SCSI_ERR_BAD_CNTL_CODE\n"));
|
|||
|
pSrb->ReturnCode = CPQ_SCSI_ERR_BAD_CNTL_CODE;
|
|||
|
status = CPQ_CIM_ERROR;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // end BuildCIMList()
|
|||
|
|
|||
|
VOID
|
|||
|
IdaMoveMemory(
|
|||
|
OUT PUCHAR pDestination,
|
|||
|
IN PUCHAR pSource,
|
|||
|
IN ULONG ulLength
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
while (ulLength--)
|
|||
|
*pDestination++ = *pSource++;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IdaStrCmp(
|
|||
|
IN PUCHAR p1,
|
|||
|
IN PUCHAR p2
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG count=0;
|
|||
|
ULONG p1count=0;
|
|||
|
ULONG p2count=0;
|
|||
|
|
|||
|
//
|
|||
|
// Get count of number of bytes in first
|
|||
|
// Get count for second
|
|||
|
// Perform while loop until out of greater number of bytes.
|
|||
|
//
|
|||
|
|
|||
|
while ((p1[count] < 0x7f) && (p1[count] > 0x1f))
|
|||
|
count++;
|
|||
|
|
|||
|
p1count = count;
|
|||
|
|
|||
|
while ((p2[count] < 0x7f) && (p2[count] > 0x1f))
|
|||
|
count++;
|
|||
|
|
|||
|
p2count = count;
|
|||
|
|
|||
|
if (p1count != p2count)
|
|||
|
return(TRUE);
|
|||
|
|
|||
|
count = p2count;
|
|||
|
|
|||
|
while (count) {
|
|||
|
if (p1[count-1] != p2[count-1])
|
|||
|
return(TRUE);
|
|||
|
count--;
|
|||
|
}
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Device extension global variable. This variable is needed for the
|
|||
|
// pci_bios function since there is no way to pass in this value. The
|
|||
|
// ScsiPortGetBusData and ScsiPortSetBusDataByOffset require a pointer
|
|||
|
// to the device extension as an argument to the function.
|
|||
|
|
|||
|
STATIC PVOID gpDeviceExtension = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Internal module function prototypes
|
|||
|
//
|
|||
|
|
|||
|
STATIC
|
|||
|
ULONG
|
|||
|
GetPciSpecifics(
|
|||
|
IN OUT PVOID pDeviceExtension,
|
|||
|
IN OUT PIDA_CONTEXT pIDAContext,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
|
|||
|
IN PPCI_SLOT_NUMBER pPciSlotNumber
|
|||
|
);
|
|||
|
|
|||
|
STATIC
|
|||
|
ULONG
|
|||
|
GetPciResources(
|
|||
|
IN PVOID pDeviceExtension,
|
|||
|
IN PPCI_COMMON_CONFIG pPciConfigHeader,
|
|||
|
IN ULONG ulPciSlotNumber,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
IDAFindPci(
|
|||
|
IN OUT PVOID pDeviceExtension,
|
|||
|
IN OUT PVOID pContext,
|
|||
|
IN PVOID pBusInformation,
|
|||
|
IN PCHAR pArgumentString,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
|
|||
|
OUT PBOOLEAN pAgain
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called by the SCSI port driver to find SMART-2/P
|
|||
|
controllers on the system's PCI buses. This routine searches only
|
|||
|
the input PCI bus number in the port configuration information. If
|
|||
|
a controller is found, the function fills out the controller's resource
|
|||
|
requirements in the port configuration information and begins the
|
|||
|
initialization process for the controller.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceExtension - pointer to the miniport driver's per-controller
|
|||
|
storage area
|
|||
|
pContext - pointer to the context value passed to ScsiPortInitialize()
|
|||
|
pBusInformation - pointer to bus type specific information
|
|||
|
pArgumentString - pointer to null-terminated ASCII string
|
|||
|
pConfigInfo - pointer to SCSI port configuration information
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
pDeviceExtension - Minport driver's per-controller storage area
|
|||
|
pContext - Context value passed to ScsiPortInitialize()
|
|||
|
pConfigInfo - pointer to SCSI port configuration information
|
|||
|
pAgain - Indicates to call function again to find more controllers.
|
|||
|
|
|||
|
|
|||
|
Function Return Values:
|
|||
|
|
|||
|
SP_RETURN_FOUND - Indicates a host adapter was found and the configuration
|
|||
|
information was successfully determined.
|
|||
|
|
|||
|
SP_RETURN_ERROR - Indicates a host adapter was found but an error occurred
|
|||
|
obtaining the configuration information.
|
|||
|
|
|||
|
SP_RETURN_NOT_FOUND - Indicates no host adapter was found for the supplied
|
|||
|
configuration information.
|
|||
|
|
|||
|
SP_RETURN_BAD_CONFIG - Indicates the supplied configuration information
|
|||
|
was invalid.
|
|||
|
|
|||
|
-- */
|
|||
|
|
|||
|
{
|
|||
|
BYTE bDeviceNumber;
|
|||
|
BYTE bFunctionNumber;
|
|||
|
BYTE bStartDeviceNumber;
|
|||
|
BYTE bStartFunctionNumber;
|
|||
|
PCI_SLOT_NUMBER PciSlotNumber;
|
|||
|
PIDA_CONTEXT pIDAContext = pContext;
|
|||
|
PCI_COMMON_CONFIG PciConfigHeader;
|
|||
|
ULONG ulBytes;
|
|||
|
ULONG ulInitStatus;
|
|||
|
ULONG ulTmp1;
|
|||
|
ULONG ulTmp2;
|
|||
|
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pBusInformation);
|
|||
|
UNREFERENCED_PARAMETER(pArgumentString);
|
|||
|
|
|||
|
|
|||
|
DebugPrint((4, "\nDAZZLER: Enter function IDAFindPci.\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Set the input pAgain argument to TRUE. This ensures that the function
|
|||
|
// will be called for every PCI bus in the system.
|
|||
|
//
|
|||
|
|
|||
|
*pAgain = TRUE;
|
|||
|
|
|||
|
// Clear the slot number.
|
|||
|
|
|||
|
PciSlotNumber.u.AsULONG = 0;
|
|||
|
|
|||
|
|
|||
|
// Set the initial search starting numbers.
|
|||
|
|
|||
|
bStartDeviceNumber = pIDAContext->PciAddress.bDeviceNumber;
|
|||
|
bStartFunctionNumber = pIDAContext->PciAddress.bFunctionNumber;
|
|||
|
|
|||
|
DebugPrint((4, "DAZZLER: Beginning search on system PCI bus %u.\n",
|
|||
|
pConfigInfo->SystemIoBusNumber));
|
|||
|
|
|||
|
|
|||
|
// Look at each device.
|
|||
|
|
|||
|
for (bDeviceNumber = bStartDeviceNumber;
|
|||
|
bDeviceNumber < PCI_MAX_DEVICES;
|
|||
|
bDeviceNumber++ ) {
|
|||
|
|
|||
|
// Set the device number in the PCI slot number.
|
|||
|
|
|||
|
PciSlotNumber.u.bits.DeviceNumber = bDeviceNumber;
|
|||
|
|
|||
|
// Look at each function of the device.
|
|||
|
|
|||
|
for (bFunctionNumber = bStartFunctionNumber;
|
|||
|
bFunctionNumber < PCI_MAX_FUNCTION;
|
|||
|
bFunctionNumber++) {
|
|||
|
// Set the function number in the PCI slot number.
|
|||
|
|
|||
|
PciSlotNumber.u.bits.FunctionNumber = bFunctionNumber;
|
|||
|
|
|||
|
// Get the PCI configuration data for the slot.
|
|||
|
|
|||
|
DebugPrint( (4, "DAZZLER: Searching device %#x, function %x.\n",
|
|||
|
bDeviceNumber, bFunctionNumber) );
|
|||
|
|
|||
|
ulBytes = ScsiPortGetBusData(pDeviceExtension,
|
|||
|
PCIConfiguration,
|
|||
|
pConfigInfo->SystemIoBusNumber,
|
|||
|
PciSlotNumber.u.AsULONG,
|
|||
|
&PciConfigHeader,
|
|||
|
PCI_COMMON_HDR_LENGTH);
|
|||
|
|
|||
|
if (ulBytes == 0) {
|
|||
|
// Out of PCI data for this bus.
|
|||
|
|
|||
|
DebugPrint((4, "DAZZLER: No more PCI devices on bus!\n"));
|
|||
|
|
|||
|
pIDAContext->PciAddress.bDeviceNumber = 0;
|
|||
|
pIDAContext->PciAddress.bFunctionNumber = 0;
|
|||
|
|
|||
|
return (SP_RETURN_NOT_FOUND);
|
|||
|
}
|
|||
|
|
|||
|
// Check for a valid vendor ID.
|
|||
|
|
|||
|
#ifdef DBG
|
|||
|
if (PciConfigHeader.VendorID != PCI_INVALID_VENDORID) {
|
|||
|
|
|||
|
// print out the PciConfigHeader
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"PciConfigHeader: VendorId=%x DeviceId=%x Command=%x Status=%x\n"
|
|||
|
,PciConfigHeader.VendorID,
|
|||
|
PciConfigHeader.DeviceID,
|
|||
|
PciConfigHeader.Command) );
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"RevisionID=%x ProgIf=%x SubClass=%x BaseClass=%x CacheLineSize=%x\n",
|
|||
|
PciConfigHeader.RevisionID,
|
|||
|
PciConfigHeader.ProgIf,
|
|||
|
PciConfigHeader.SubClass,
|
|||
|
PciConfigHeader.BaseClass,
|
|||
|
PciConfigHeader.CacheLineSize) );
|
|||
|
|
|||
|
DebugPrint((4,"LatencyTimer=%x HeaderType=%x BIST=%x\n",
|
|||
|
PciConfigHeader.LatencyTimer,
|
|||
|
PciConfigHeader.HeaderType,
|
|||
|
PciConfigHeader.BIST) );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if (PciConfigHeader.VendorID == PCI_INVALID_VENDORID) {
|
|||
|
// No PCI device or no more functions on the current device.
|
|||
|
// Go to the next device.
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// PCI controller found. Next check to see if it is one of the
|
|||
|
// controllers being searched for.
|
|||
|
|
|||
|
if ((PciConfigHeader.VendorID ==
|
|||
|
pIDAContext->PciIdentifier.usVendorID) &&
|
|||
|
(PciConfigHeader.DeviceID ==
|
|||
|
pIDAContext->PciIdentifier.usDeviceID)) {
|
|||
|
DebugPrint( (4, "DAZZLER: Found PCI controller.\n") );
|
|||
|
|
|||
|
// Check if the controller is enabled.
|
|||
|
|
|||
|
if ((PciConfigHeader.Command & PCI_ENABLE_IO_SPACE) &&
|
|||
|
(PciConfigHeader.Command & PCI_ENABLE_MEMORY_SPACE)) {
|
|||
|
DebugPrint( (4, "DAZZLER: Controller is enabled.\n") );
|
|||
|
|
|||
|
//
|
|||
|
// Get PCI Id placed in offset 0x2c and place into the
|
|||
|
// device extension EisaId.
|
|||
|
//
|
|||
|
|
|||
|
// ulTmp1 = ulTmp2 = PciConfigHeader.u.type0.Reserved1[1];
|
|||
|
ulTmp1 = ulTmp2 = ((((ULONG) PciConfigHeader.u.type0.SubSystemID) << 16) |
|
|||
|
PciConfigHeader.u.type0.SubVendorID);
|
|||
|
|
|||
|
// Fix it because the bytes are swapped
|
|||
|
|
|||
|
ulTmp1 &= 0xff00ff00;
|
|||
|
ulTmp2 &= 0x00ff00ff;
|
|||
|
|
|||
|
ulTmp1 = ulTmp1 >> 8;
|
|||
|
ulTmp2 = ulTmp2 << 8;
|
|||
|
|
|||
|
((PDEVICE_EXTENSION)pDeviceExtension)->EisaId = ulTmp1 | ulTmp2;
|
|||
|
|
|||
|
// Set starting address for the next search.
|
|||
|
|
|||
|
pIDAContext->PciAddress.bDeviceNumber = bDeviceNumber;
|
|||
|
pIDAContext->PciAddress.bFunctionNumber = bFunctionNumber + 1;
|
|||
|
|
|||
|
|
|||
|
// Get the PCI resource requirements for the controller.
|
|||
|
|
|||
|
ulInitStatus = GetPciResources(pDeviceExtension,
|
|||
|
&PciConfigHeader,
|
|||
|
PciSlotNumber.u.AsULONG,
|
|||
|
pConfigInfo);
|
|||
|
|
|||
|
if (ulInitStatus != SP_RETURN_FOUND) {
|
|||
|
|
|||
|
DebugPrint((0,
|
|||
|
"DAZZLER: Could not get PCI resources for controller!\n"));
|
|||
|
|
|||
|
return (ulInitStatus);
|
|||
|
}
|
|||
|
|
|||
|
// Get the PCI specifics for the controller.
|
|||
|
|
|||
|
ulInitStatus = GetPciSpecifics(pDeviceExtension,
|
|||
|
pIDAContext,
|
|||
|
pConfigInfo,
|
|||
|
&PciSlotNumber);
|
|||
|
|
|||
|
if (ulInitStatus != SP_RETURN_FOUND) {
|
|||
|
DebugPrint((0, "DAZZLER: Could not get PCI specifics for controller!\n") );
|
|||
|
}
|
|||
|
|
|||
|
return (ulInitStatus);
|
|||
|
|
|||
|
} else {
|
|||
|
DebugPrint( (4, "DAZZLER: Controller is disabled.\n") );
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
} // end if ((PciConfigHeader.VendorID == ...) &&
|
|||
|
|
|||
|
|
|||
|
} // end for (bFunctionNumber = bStartFunctionNumber; ...)
|
|||
|
|
|||
|
|
|||
|
// Reset the initial starting function number.
|
|||
|
|
|||
|
bStartFunctionNumber = 0;
|
|||
|
|
|||
|
} // end for (bDeviceNumber = bStartDeviceNumber; ...)
|
|||
|
|
|||
|
|
|||
|
// A controller was not found.
|
|||
|
|
|||
|
DebugPrint( (4,
|
|||
|
"DAZZLER: Failed to find any PCI controllers this pass.\n") );
|
|||
|
|
|||
|
pIDAContext->PciAddress.bDeviceNumber = 0;
|
|||
|
pIDAContext->PciAddress.bFunctionNumber = 0;
|
|||
|
|
|||
|
return (SP_RETURN_NOT_FOUND);
|
|||
|
|
|||
|
} // end IDAFindPci()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STATIC
|
|||
|
ULONG
|
|||
|
GetPciResources(
|
|||
|
IN PVOID pDeviceExtension,
|
|||
|
IN PPCI_COMMON_CONFIG pPciConfigHeader,
|
|||
|
IN ULONG ulPciSlotNumber,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine gets the resources required by the input PCI controller.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceExtension - pointer to the miniport driver's per-controller
|
|||
|
storage area
|
|||
|
pPciConfigHeader - pointer to the controller's PCI configuration header
|
|||
|
ulPciSlotNumber - the PCI controller's address represented as a ULONG
|
|||
|
pConfigInfo - pointer to SCSI port configuration information
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
pConfigInfo - pointer to SCSI port configuration information. The access
|
|||
|
range elements of the structure are filled in with the resources
|
|||
|
required by the controller.
|
|||
|
|
|||
|
Function Return Values:
|
|||
|
|
|||
|
SP_RETURN_FOUND - Used to indicate that the HBA was successfully
|
|||
|
initialized.
|
|||
|
|
|||
|
SP_RETURN_ERROR - Used to indicate that the HBA could not be properly
|
|||
|
initilaized.
|
|||
|
|
|||
|
-- */
|
|||
|
|
|||
|
{
|
|||
|
PACCESS_RANGE pAccessRange;
|
|||
|
PCI_COMMON_CONFIG PciTmpCfgHdr;
|
|||
|
ULONG ulAddressSpaceMask = 0xFFFFFFFF;
|
|||
|
ULONG ulAddressSpace, ulAddressLength, ulBytes;
|
|||
|
USHORT i;
|
|||
|
|
|||
|
|
|||
|
DebugPrint( (2, "\nDAZZLER: Enter function GetPciResources.\n") );
|
|||
|
|
|||
|
|
|||
|
// Verify the number of available access ranges.
|
|||
|
|
|||
|
if (pConfigInfo->NumberOfAccessRanges > IDA_PCI_NUM_ACCESS_RANGES) {
|
|||
|
|
|||
|
DebugPrint((0,
|
|||
|
"DAZZLER: # of access ranges invalid for PCI controller.\n"));
|
|||
|
|
|||
|
return (SP_RETURN_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Get the resources required for each PCI base address.
|
|||
|
|
|||
|
for (i = 0; i < IDA_PCI_NUM_ACCESS_RANGES; i++) {
|
|||
|
// Get pointer to the access range element to fill out.
|
|||
|
|
|||
|
pAccessRange = &((*(pConfigInfo->AccessRanges))[i]);
|
|||
|
|
|||
|
|
|||
|
// Check to see if the PCI base address is memory or I/O.
|
|||
|
|
|||
|
if (pPciConfigHeader->u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_SPACE) {
|
|||
|
|
|||
|
// Address is an I/O address.
|
|||
|
|
|||
|
pAccessRange->RangeStart =
|
|||
|
ScsiPortConvertUlongToPhysicalAddress(pPciConfigHeader->
|
|||
|
u.type0.BaseAddresses[i] &
|
|||
|
~PCI_ADDRESS_IO_SPACE);
|
|||
|
|
|||
|
pAccessRange->RangeInMemory = FALSE;
|
|||
|
} else {
|
|||
|
// Address is a memory address.
|
|||
|
|
|||
|
ASSERT((pPciConfigHeader->u.type0.BaseAddresses[i] &
|
|||
|
PCI_ADDRESS_MEMORY_TYPE_MASK) & PCI_TYPE_32BIT);
|
|||
|
|
|||
|
pAccessRange->RangeStart =
|
|||
|
ScsiPortConvertUlongToPhysicalAddress(pPciConfigHeader->
|
|||
|
u.type0.BaseAddresses[i] &
|
|||
|
0xfffffff0);
|
|||
|
|
|||
|
pAccessRange->RangeInMemory = TRUE;
|
|||
|
|
|||
|
DebugPrint((4, "pAccessRange->RangeStart.Hi/Low=%x %x ->RangeLength=%x\n",
|
|||
|
pAccessRange->RangeStart.HighPart,pAccessRange->
|
|||
|
RangeStart.LowPart,
|
|||
|
pAccessRange->RangeLength));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Get the amount of address space required. This is done by writing all
|
|||
|
// 1's to the register and then reading the value back. The device will
|
|||
|
// return 0's in all don't care bits. The first signficant bit set beyond
|
|||
|
// those used to indicate memory or I/O determines the address space
|
|||
|
// required. Finally, the register is restored with the original address.
|
|||
|
|
|||
|
ulBytes = ScsiPortSetBusDataByOffset(pDeviceExtension, PCIConfiguration,
|
|||
|
pConfigInfo->SystemIoBusNumber,
|
|||
|
ulPciSlotNumber,
|
|||
|
(PVOID) &ulAddressSpaceMask,
|
|||
|
FIELD_OFFSET(PCI_COMMON_CONFIG,
|
|||
|
u.type0.BaseAddresses[i]),
|
|||
|
sizeof(ULONG)
|
|||
|
);
|
|||
|
|
|||
|
if (ulBytes == 0) {
|
|||
|
DebugPrint((0,
|
|||
|
"DAZZLER: Could not set PCI slot information for slot %u.\n",
|
|||
|
ulPciSlotNumber));
|
|||
|
|
|||
|
return (SP_RETURN_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Read the value back.
|
|||
|
|
|||
|
ulBytes = ScsiPortGetBusData( pDeviceExtension,
|
|||
|
PCIConfiguration,
|
|||
|
pConfigInfo->SystemIoBusNumber,
|
|||
|
ulPciSlotNumber,
|
|||
|
&PciTmpCfgHdr,
|
|||
|
PCI_COMMON_HDR_LENGTH );
|
|||
|
|
|||
|
if (ulBytes == 0) {
|
|||
|
|
|||
|
DebugPrint((0,
|
|||
|
"DAZZLER: Could not get PCI information for slot %u.\n",
|
|||
|
ulPciSlotNumber)
|
|||
|
);
|
|||
|
|
|||
|
return(SP_RETURN_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Determine the space required by finding the first bit set.
|
|||
|
|
|||
|
ulAddressSpace = PciTmpCfgHdr.u.type0.BaseAddresses[i];
|
|||
|
ulAddressLength = 1 << ((ulAddressSpace & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
|
|||
|
|
|||
|
while (!(ulAddressSpace & ulAddressLength) && ulAddressLength) {
|
|||
|
ulAddressLength <<= 1;
|
|||
|
}
|
|||
|
|
|||
|
// Set the access range length.
|
|||
|
|
|||
|
pAccessRange->RangeLength = ulAddressLength;
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"pAccessRange->RangeStart.Hi/Low=%x %x ->RangeLength=%x\n",
|
|||
|
pAccessRange->RangeStart.HighPart,pAccessRange->
|
|||
|
RangeStart.LowPart,
|
|||
|
pAccessRange->RangeLength)
|
|||
|
);
|
|||
|
|
|||
|
// Reset the base address register to its original value.
|
|||
|
|
|||
|
ulBytes = ScsiPortSetBusDataByOffset(pDeviceExtension,
|
|||
|
PCIConfiguration,
|
|||
|
pConfigInfo->SystemIoBusNumber,
|
|||
|
ulPciSlotNumber,
|
|||
|
(PVOID) &pPciConfigHeader->
|
|||
|
u.type0.BaseAddresses[i],
|
|||
|
FIELD_OFFSET(PCI_COMMON_CONFIG,
|
|||
|
u.type0.BaseAddresses[i]),
|
|||
|
sizeof(ULONG)
|
|||
|
);
|
|||
|
|
|||
|
if (ulBytes == 0) {
|
|||
|
|
|||
|
DebugPrint((0, "DAZZLER: Could not set PCI information for slot %u.\n",
|
|||
|
ulPciSlotNumber));
|
|||
|
|
|||
|
return(SP_RETURN_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
} // end for (i = 0;...)
|
|||
|
|
|||
|
|
|||
|
// Set the IRQ information in the port configuration data structure.
|
|||
|
|
|||
|
pConfigInfo->BusInterruptLevel = pPciConfigHeader->u.type0.InterruptLine;
|
|||
|
pConfigInfo->InterruptMode = LevelSensitive;
|
|||
|
|
|||
|
// Return success.
|
|||
|
|
|||
|
return(SP_RETURN_FOUND);
|
|||
|
|
|||
|
} // end GetPciResources
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STATIC
|
|||
|
ULONG
|
|||
|
GetPciSpecifics(
|
|||
|
IN OUT PVOID pDeviceExtension,
|
|||
|
IN OUT PIDA_CONTEXT pIDAContext,
|
|||
|
IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
|
|||
|
IN PPCI_SLOT_NUMBER pPciSlotNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to complete initialization of the port configuration
|
|||
|
information for the input controller. This function also begins the
|
|||
|
configuration of the SMGR for the controller. Finally, the function makes
|
|||
|
the call to initialize the controller.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceExtension - Miniport driver's per-controller storage area
|
|||
|
pIDAContext - Context value passed to ScsiPortInitialize()
|
|||
|
pConfigInfo - pointer to SCSI port configuration information
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
pDeviceExtension - Minport driver's per-controller storage area
|
|||
|
pIDAContext - Context value passed to ScsiPortInitialize()
|
|||
|
pConfigInfo - pointer to SCSI port configuration information
|
|||
|
|
|||
|
|
|||
|
Function Return Values:
|
|||
|
|
|||
|
SP_RETURN_FOUND - Used to indicate that the HBA was successfully
|
|||
|
initialized.
|
|||
|
|
|||
|
SP_RETURN_ERROR - Used to indicate that the HBA could not be properly
|
|||
|
initilaized.
|
|||
|
|
|||
|
-- */
|
|||
|
|
|||
|
{
|
|||
|
PACCESS_RANGE pAccessRange;
|
|||
|
PDEVICE_EXTENSION pIDADeviceExtension = pDeviceExtension;
|
|||
|
PVOID pBaseAddress, pIoAddress = NULL, pMemoryAddress = NULL;
|
|||
|
ULONG ulInitStatus, ulPhysicalMemoryAddress;
|
|||
|
USHORT i;
|
|||
|
|
|||
|
|
|||
|
DebugPrint( (6, "Enter function GetPciSpecifics.\n") );
|
|||
|
|
|||
|
|
|||
|
// Get and verify the access ranges and its length.
|
|||
|
|
|||
|
for (i = 0; i < IDA_PCI_NUM_ACCESS_RANGES - 1; i++) {
|
|||
|
pAccessRange = &((*(pConfigInfo->AccessRanges))[i]);
|
|||
|
ASSERT(pAccessRange->RangeLength != 0);
|
|||
|
|
|||
|
// Get the mapped system address.
|
|||
|
|
|||
|
pBaseAddress = ScsiPortGetDeviceBase(pDeviceExtension,
|
|||
|
pConfigInfo->AdapterInterfaceType,
|
|||
|
pConfigInfo->SystemIoBusNumber,
|
|||
|
pAccessRange->RangeStart,
|
|||
|
pAccessRange->RangeLength,
|
|||
|
(BOOLEAN)!pAccessRange->RangeInMemory
|
|||
|
);
|
|||
|
|
|||
|
if (pBaseAddress == NULL) {
|
|||
|
|
|||
|
DebugPrint( (0,
|
|||
|
"DAZZLER: Error getting base addr. for PCI controller.\n"));
|
|||
|
|
|||
|
return (SP_RETURN_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Set the appropriate pointer to the mapped address.
|
|||
|
|
|||
|
if (pAccessRange->RangeInMemory) {
|
|||
|
pMemoryAddress = pBaseAddress;
|
|||
|
|
|||
|
ulPhysicalMemoryAddress =
|
|||
|
ScsiPortConvertPhysicalAddressToUlong( pAccessRange->RangeStart );
|
|||
|
|
|||
|
} else {
|
|||
|
pIoAddress = pBaseAddress;
|
|||
|
}
|
|||
|
|
|||
|
} // end for (i = 0;...)
|
|||
|
|
|||
|
|
|||
|
// Debug checks
|
|||
|
|
|||
|
ASSERT(pIoAddress);
|
|||
|
ASSERT(pMemoryAddress);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller I/O base address = %0#10x\n",
|
|||
|
pIoAddress)
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller memory base address = %0#10x\n",
|
|||
|
pMemoryAddress)
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller physical memory address = %0#10x\n",
|
|||
|
ulPhysicalMemoryAddress)
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller bus number = %#x\n",
|
|||
|
pConfigInfo->SystemIoBusNumber)
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller device number = %#x\n",
|
|||
|
pPciSlotNumber->u.bits.DeviceNumber)
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((4,
|
|||
|
"DAZZLER: PCI controller function number = %#x\n",
|
|||
|
pPciSlotNumber->u.bits.FunctionNumber)
|
|||
|
);
|
|||
|
|
|||
|
// Finish initalizing the port configuration information
|
|||
|
|
|||
|
pConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE;
|
|||
|
pConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS;
|
|||
|
pConfigInfo->ScatterGather = TRUE;
|
|||
|
pConfigInfo->Master = TRUE;
|
|||
|
pConfigInfo->NumberOfBuses = 1;
|
|||
|
pConfigInfo->Dma32BitAddresses = TRUE;
|
|||
|
pConfigInfo->MaximumNumberOfTargets = 32;
|
|||
|
|
|||
|
//
|
|||
|
// Setup some vars needed for the IDENTIFY commands
|
|||
|
//
|
|||
|
|
|||
|
pConfigInfo->CachesData = TRUE;
|
|||
|
pIDADeviceExtension->SectorShift = 9;
|
|||
|
pIDADeviceExtension->IdentifyBuffer =
|
|||
|
ScsiPortGetUncachedExtension(pDeviceExtension,
|
|||
|
pConfigInfo,
|
|||
|
512);
|
|||
|
|
|||
|
// Fill in the HBA configuration data in the device extension.
|
|||
|
|
|||
|
if (pMemoryAddress) {
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.ulBaseIOAddress =
|
|||
|
(ULONG)pMemoryAddress;
|
|||
|
|
|||
|
pIDADeviceExtension->CPFIFO =
|
|||
|
(PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_CPFIFO_OFFSET);
|
|||
|
|
|||
|
pIDADeviceExtension->CCFIFO =
|
|||
|
(PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_CCFIFO_OFFSET);
|
|||
|
|
|||
|
pIDADeviceExtension->InterruptMask =
|
|||
|
(PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_MASK_OFFSET);
|
|||
|
|
|||
|
pIDADeviceExtension->InterruptStatus = (PULONG)((PUCHAR)pMemoryAddress+
|
|||
|
IDA_PCI_STATUS_OFFSET);
|
|||
|
|
|||
|
pIDADeviceExtension->InterruptPending = (PULONG)((PUCHAR)pMemoryAddress+
|
|||
|
IDA_PCI_PENDING_OFFSET);
|
|||
|
|
|||
|
ulInitStatus = SP_RETURN_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
if (pIoAddress) {
|
|||
|
pIDADeviceExtension->BaseIOAddress = (ULONG)pIoAddress;
|
|||
|
}
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.bHBAModel = pIDAContext->bHBAModel;
|
|||
|
pIDADeviceExtension->HBAConfiguration.bHBAIoBusType = PCI_BUS;
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
|
|||
|
bPCIBusNumber = (BYTE)pConfigInfo->SystemIoBusNumber;
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
|
|||
|
bDeviceNumber = (BYTE)pPciSlotNumber->u.bits.DeviceNumber;
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
|
|||
|
bFunctionNumber = (BYTE)pPciSlotNumber->u.bits.FunctionNumber;
|
|||
|
|
|||
|
pIDADeviceExtension->HBAConfiguration.bNumScsiBuses =
|
|||
|
pConfigInfo->NumberOfBuses;
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"CCFIFO=0x%x CPFIFO=0x%x InterruptMask=0x%x InterruptStatus=0x%x InterruptPending=0x%x BaseIo=0x%x\n",
|
|||
|
pIDADeviceExtension->CCFIFO,
|
|||
|
pIDADeviceExtension->CPFIFO,
|
|||
|
pIDADeviceExtension->InterruptMask,
|
|||
|
pIDADeviceExtension->InterruptStatus,
|
|||
|
pIDADeviceExtension->InterruptPending,
|
|||
|
pIDADeviceExtension->BaseIOAddress )
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (ulInitStatus != SP_RETURN_FOUND) {
|
|||
|
|
|||
|
// Free the device base for this controller.
|
|||
|
|
|||
|
ScsiPortFreeDeviceBase(pDeviceExtension, pIoAddress);
|
|||
|
ScsiPortFreeDeviceBase(pDeviceExtension, pMemoryAddress);
|
|||
|
|
|||
|
} // end if (ulInitStatus != SP_RETURN_FOUND)
|
|||
|
|
|||
|
return (ulInitStatus);
|
|||
|
|
|||
|
} // end GetPciSpecifics()
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
DriverEntry(
|
|||
|
IN PVOID pDriverObject,
|
|||
|
IN PVOID pArgument2
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Installable driver initialization entry point. This function initializes
|
|||
|
the hardware initialization data structure and begins the process of
|
|||
|
finding controllers that the driver supports.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDriverObject - Pointer to the driver's driver object.
|
|||
|
pArgument2 - Pointer to driver's entry in the Registry.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status from ScsiPortInitialize()
|
|||
|
|
|||
|
-- */
|
|||
|
|
|||
|
{
|
|||
|
IDA_CONTEXT IDAContext;
|
|||
|
HW_INITIALIZATION_DATA hwInitializationData;
|
|||
|
ULONG i, ulStatus, ulReturnStatus=0;
|
|||
|
ULONG eisaSlotNumber;
|
|||
|
UCHAR deviceId[8] = {'0', '0', '4', '0', '1', '1', '0', 'E'};
|
|||
|
|
|||
|
DebugPrint((0,"\n\nCompaq Disk Array Miniport Driver\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Zero out structure.
|
|||
|
//
|
|||
|
|
|||
|
for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++) {
|
|||
|
((PUCHAR)&hwInitializationData)[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set size of hwInitializationData.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.HwInitializationDataSize =
|
|||
|
sizeof(HW_INITIALIZATION_DATA);
|
|||
|
|
|||
|
//
|
|||
|
// Set entry points.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.HwInitialize = IdaInitialize;
|
|||
|
hwInitializationData.HwResetBus = IdaResetBus;
|
|||
|
hwInitializationData.HwStartIo = IdaStartIo;
|
|||
|
hwInitializationData.HwInterrupt = IdaInterrupt;
|
|||
|
hwInitializationData.HwFindAdapter = IdaFindAdapter;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate no buffer mapping but will need physical addresses.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.NeedPhysicalAddresses = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Specify size of extensions.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.DeviceExtensionSize =
|
|||
|
sizeof(DEVICE_EXTENSION);
|
|||
|
hwInitializationData.SpecificLuExtensionSize =
|
|||
|
sizeof(LOGICAL_UNIT_EXTENSION);
|
|||
|
|
|||
|
//
|
|||
|
// Specifiy the bus type.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.AdapterInterfaceType = Eisa;
|
|||
|
hwInitializationData.NumberOfAccessRanges = 3;
|
|||
|
|
|||
|
//
|
|||
|
// Ask for SRB extensions for command lists.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.SrbExtensionSize = sizeof(COMMAND_LIST);
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that this controller supports multiple outstand
|
|||
|
// requests to its devices.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.MultipleRequestPerLu = TRUE;
|
|||
|
hwInitializationData.AutoRequestSense = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Set the context parameter to indicate that the search for controllers
|
|||
|
// should start at the first EISA slot. This is only for a manual search
|
|||
|
// by the miniport driver, if the system does not pass in predetermined
|
|||
|
// configuration.
|
|||
|
//
|
|||
|
|
|||
|
eisaSlotNumber = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate EISA id.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.DeviceId = &deviceId;
|
|||
|
hwInitializationData.DeviceIdLength = 8;
|
|||
|
|
|||
|
//
|
|||
|
// Call the system to search for this adapter.
|
|||
|
//
|
|||
|
|
|||
|
ulReturnStatus = ScsiPortInitialize(pDriverObject, pArgument2,
|
|||
|
&hwInitializationData,
|
|||
|
&eisaSlotNumber
|
|||
|
);
|
|||
|
|
|||
|
// Initialize hardware initialization data structure to zeroes.
|
|||
|
|
|||
|
for (i = 0; i < sizeof(HW_INITIALIZATION_DATA); i++) {
|
|||
|
((PUCHAR)&hwInitializationData)[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
// Fill in the hardware initialization data structure.
|
|||
|
|
|||
|
hwInitializationData.HwInitializationDataSize =
|
|||
|
sizeof(HW_INITIALIZATION_DATA);
|
|||
|
|
|||
|
// Set driver entry points.
|
|||
|
|
|||
|
hwInitializationData.HwInitialize = IdaInitializePCI;
|
|||
|
hwInitializationData.HwStartIo = IdaStartIo;
|
|||
|
hwInitializationData.HwInterrupt = IdaInterrupt;
|
|||
|
hwInitializationData.HwResetBus = IdaResetBus;
|
|||
|
hwInitializationData.HwDmaStarted = NULL;
|
|||
|
hwInitializationData.HwAdapterState = NULL;
|
|||
|
|
|||
|
|
|||
|
// Specify size of extensions.
|
|||
|
|
|||
|
hwInitializationData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION);
|
|||
|
|
|||
|
hwInitializationData.SpecificLuExtensionSize =
|
|||
|
sizeof(LOGICAL_UNIT_EXTENSION);
|
|||
|
|
|||
|
hwInitializationData.SrbExtensionSize = sizeof(COMMAND_LIST);
|
|||
|
|
|||
|
|
|||
|
// Initialize other data.
|
|||
|
|
|||
|
hwInitializationData.MapBuffers = FALSE;
|
|||
|
hwInitializationData.NeedPhysicalAddresses = TRUE;
|
|||
|
hwInitializationData.TaggedQueuing = TRUE;
|
|||
|
hwInitializationData.AutoRequestSense = TRUE;
|
|||
|
hwInitializationData.MultipleRequestPerLu = TRUE;
|
|||
|
hwInitializationData.ReceiveEvent = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that this controller supports multiple outstanding
|
|||
|
// requests to its devices.
|
|||
|
//
|
|||
|
|
|||
|
hwInitializationData.MultipleRequestPerLu = TRUE;
|
|||
|
hwInitializationData.AutoRequestSense = TRUE;
|
|||
|
|
|||
|
// Setup required values to find PCI Compaq 32-Bit Array controllers.
|
|||
|
|
|||
|
hwInitializationData.AdapterInterfaceType = PCIBus;
|
|||
|
hwInitializationData.NumberOfAccessRanges = IDA_PCI_NUM_ACCESS_RANGES;
|
|||
|
hwInitializationData.HwFindAdapter = IDAFindPci;
|
|||
|
|
|||
|
IDAContext.bHBAModel = IDA_PCI_DAZZLER;
|
|||
|
IDAContext.PciAddress.bPCIBusNumber = 0;
|
|||
|
IDAContext.PciAddress.bDeviceNumber = 0;
|
|||
|
IDAContext.PciAddress.bFunctionNumber = 0;
|
|||
|
IDAContext.PciIdentifier.usVendorID = IDA_PCI_COMPAQ_ID;
|
|||
|
IDAContext.PciIdentifier.usDeviceID = IDA_PCI_DAZZLER_DEVICE_ID;
|
|||
|
|
|||
|
|
|||
|
ulStatus = ScsiPortInitialize(pDriverObject,
|
|||
|
pArgument2,
|
|||
|
&hwInitializationData,
|
|||
|
&IDAContext
|
|||
|
);
|
|||
|
|
|||
|
DebugPrint((0, "DAZZLER: PCI search status = %0#10x\n", ulStatus));
|
|||
|
|
|||
|
ulReturnStatus = (ulReturnStatus < ulStatus) ? ulReturnStatus : ulStatus;
|
|||
|
|
|||
|
// Return the final status value.
|
|||
|
|
|||
|
DebugPrint( (4, "DAZZLER: Final status = %0#10x\n", ulReturnStatus) );
|
|||
|
|
|||
|
return (ulReturnStatus);
|
|||
|
|
|||
|
} // end DriverEntry()
|
|||
|
|
|||
|
VOID
|
|||
|
IdaDisableInts(
|
|||
|
IN PDEVICE_EXTENSION pDeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Called for SCSI inquiry processing (GetDiskIdentifyData) to eliminate
|
|||
|
interupts generated at the controller. This was added to support
|
|||
|
Online Blazer and the ability to recognize Logical Volumes that may
|
|||
|
be added while the system is running. GetDiskIdentifyData submits
|
|||
|
the inquiry packet and returns to Startio when this packet has been
|
|||
|
processed by the controller. Interrupts at the adapter are temporarily
|
|||
|
disabled because we must wait for completion and we want to get back
|
|||
|
in a timely fashion.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceExtension - pointer to adapter's device extension.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = pDeviceExtension;
|
|||
|
PULONG pIntMask;
|
|||
|
|
|||
|
if (deviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
|
|||
|
|
|||
|
//
|
|||
|
// we are looking at the Dazzler/P...use memapped io
|
|||
|
//
|
|||
|
|
|||
|
pIntMask = deviceExtension->InterruptMask;
|
|||
|
*pIntMask = 0L;
|
|||
|
} else
|
|||
|
|
|||
|
if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER
|
|||
|
&& !deviceExtension->PCIoff)
|
|||
|
|
|||
|
//
|
|||
|
// we are dealing with Dazzler/E use mapped portio
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask, 0L);
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// ida and prior adapter....BMIC interface
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl, 0);
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask, 0);
|
|||
|
}
|
|||
|
|
|||
|
} // end IdaDisableInts
|
|||
|
|
|||
|
VOID
|
|||
|
IdaEnableInts(
|
|||
|
IN PDEVICE_EXTENSION pDeviceExtension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Called for SCSI inquiry processing (GetDiskIdentifyData) to enable
|
|||
|
interupts generated at the controller. This was added to support
|
|||
|
Online Blazer and the ability to recognize Logical Volumes that may
|
|||
|
be added while the system is running. GetDiskIdentifyData submits
|
|||
|
the inquiry packet and returns to Startio when this packet has been
|
|||
|
processed by the controller. Interrupts at the adapter are temporarily
|
|||
|
disabled because we must wait for completion and we want to get back
|
|||
|
in a timely fashion.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceExtension - pointer to adapter's device extension.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = pDeviceExtension;
|
|||
|
PULONG pIntMask;
|
|||
|
|
|||
|
if (deviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
|
|||
|
|
|||
|
//
|
|||
|
// Dazzler/P...use memmapped io
|
|||
|
//
|
|||
|
|
|||
|
pIntMask = deviceExtension->InterruptMask;
|
|||
|
*pIntMask = 1L;
|
|||
|
} else
|
|||
|
|
|||
|
if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER
|
|||
|
&& !deviceExtension->PCIoff)
|
|||
|
|
|||
|
//
|
|||
|
// Dazzler/E...use 32bit port io
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask, 1L);
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// ida and prior...use BMIC interface
|
|||
|
//
|
|||
|
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl, 1);
|
|||
|
ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask, 1);
|
|||
|
}
|
|||
|
|
|||
|
} // end IdaEnableInts
|