From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/miniport/dell/delldsa.c | 1767 +++++++++++++++++++++++++++++++++ private/ntos/miniport/dell/delldsa.h | 210 ++++ private/ntos/miniport/dell/delldsa.rc | 12 + private/ntos/miniport/dell/makefile | 6 + private/ntos/miniport/dell/sources | 40 + 5 files changed, 2035 insertions(+) create mode 100644 private/ntos/miniport/dell/delldsa.c create mode 100644 private/ntos/miniport/dell/delldsa.h create mode 100644 private/ntos/miniport/dell/delldsa.rc create mode 100644 private/ntos/miniport/dell/makefile create mode 100644 private/ntos/miniport/dell/sources (limited to 'private/ntos/miniport/dell') diff --git a/private/ntos/miniport/dell/delldsa.c b/private/ntos/miniport/dell/delldsa.c new file mode 100644 index 000000000..e2c7b203c --- /dev/null +++ b/private/ntos/miniport/dell/delldsa.c @@ -0,0 +1,1767 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + DellDsa.c + +Abstract: + + This is the device driver for the DELL Drive Arrays. + +Authors: + + Mike Glass (mglass) + +Environment: + + kernel mode only + +Revision History: + +--*/ + +#include "miniport.h" +#include "scsi.h" +#include "delldsa.h" + +// +// Disk Device Extension +// + +typedef struct _DEVICE_EXTENSION { + + // + // BMIC registers + // + + PDDA_REGISTERS Bmic; + + // + // Buffer for IDENTIFY command. + // + + PVOID IdentifyBuffer; + + // + // The rest of the fields are for the controller itself. + // + // Firmware major version + // 1 : DDA + // 2 : DSA + // + + USHORT MajorVersion; + USHORT MinorVersion; + + UCHAR NumberOfLogicalDrives; + UCHAR EmulationMode; + UCHAR MaximumQueueDepth; + UCHAR MaximumNumberOfSgDescriptors; + + // + // Keep track of state of verify. + // + + ULONG CurrentSector; + ULONG RemainingSectors; + + // + // Array of outstanding requests + // + + PSCSI_REQUEST_BLOCK Srb[256]; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +BOOLEAN +ExtendedCommand( + PDEVICE_EXTENSION DeviceExtension, + IN UCHAR LogicalDrive, + IN UCHAR Command + ) + +/*++ + +Routine Description: + + Ring local doorbell and wait for completion bit in local doorbell to be set. + This function is only called during initialization. + +Arguments: + + DeviceExtension - Controller data. + LogicalDrive - Logical drive defined on this controller. + Command - Command byte to be sent. + +Return Value: + + TRUE - if completion bit set in local doorbell. + FALSE - if timeout occurred waiting for bit to be set. + +--*/ + +{ + ULONG i; + + // + // Claim submission semaphore. + // + + for (i=0; i<1000; i++) { + + if (ScsiPortReadPortUchar(&DeviceExtension->Bmic->SubmissionSemaphore)) { + ScsiPortStallExecution(1); + } else { + break; + } + } + + // + // Check for timeout. + // + + if (i == 1000) { + return FALSE; + } + + // + // Claim submission semaphore + // + + ScsiPortWritePortUchar(&DeviceExtension->Bmic->SubmissionSemaphore, 1); + + // + // Write command byte to controller. + // + + ScsiPortWritePortUchar(&DeviceExtension->Bmic->Command, Command); + + // + // Write logical drive number to controller. Some extended commands + // do not require a logical drive number, but setting it doesn't hurt + // anything and makes this routine more flexible. + // + + ScsiPortWritePortUchar(&DeviceExtension->Bmic->DriveNumber, LogicalDrive); + + // + // Ring submission doorbell. + // + + ScsiPortWritePortUchar(&DeviceExtension->Bmic->SubmissionDoorBell, + DDA_DOORBELL_EXTENDED_COMMAND); + + // + // Spin for completion. + // + + for (i=0; i<10000; i++) { + + if (ScsiPortReadPortUchar(&DeviceExtension->Bmic->CompletionDoorBell) & + DDA_DOORBELL_EXTENDED_COMMAND) { + break; + } else { + ScsiPortStallExecution(10); + } + } + + // + // Check for timeout. + // + + if (i == 10000) { + return FALSE; + } + + // + // Clear completion status + // + + ScsiPortWritePortUchar(&DeviceExtension->Bmic->CompletionDoorBell, 0xff); + + return TRUE; + +} // end ExtendedCommand + + +VOID +BuildRequest( + IN PDEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Translates a system SRB to a DDA request. + + A DDA request has the following restrictions: + + (1) Its scatter/gather list size is specified in + DeviceExtension->MaximumSGSize and is usually 16. + (2) Scatter/gather requests use sector counts rather than byte counts. + A scather/gather boundry cannot exist in the middle of a sector. + (3) The maximum size of a single DDA I/O is limited to 128 sectors. + This is because some early disk devices have bugs causing data + corruption in larger transfers. + + DSA controllers do not have restriction (2), but some DSA firmware + have bugs such that byte SG hangs the controller. + +Arguments: + + DeviceExtension - Represents the target disk. + SRB - System request. + +Return Value: + + None. + +--*/ + +{ + PDDA_REQUEST_BLOCK ddaRequest = Srb->SrbExtension; + PUCHAR dataPointer; + ULONG descriptorNumber; + ULONG bytesLeft; + PSG_DESCRIPTOR sgList; + ULONG length; + + // + // Set drive number if request packet. + // + + ddaRequest->DriveNumber = Srb->TargetId; + + // + // Use SRB tag as request id. + // + + ddaRequest->RequestId = Srb->QueueTag; + DeviceExtension->Srb[Srb->QueueTag] = Srb; + + // + // Determine starting block number. + // + + ddaRequest->StartingSector = + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + // + // Get data pointer, byte count and index to scatter/gather list. + // + + sgList = ddaRequest->SgList.Descriptor; + descriptorNumber = 0; + bytesLeft = Srb->DataTransferLength; + dataPointer = Srb->DataBuffer; + + // + // Build the scatter/gather list. + // + + while (bytesLeft) { + + // + // Get physical address and length of contiguous + // physical buffer. + // + + sgList[descriptorNumber].Address = + 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; + } + + // + // Complete SG descriptor. + // + + sgList[descriptorNumber].Count = length; + + // + // Update pointers and counters. + // + + bytesLeft -= length; + dataPointer += length; + descriptorNumber++; + } + + // + // Convert to non-scatter/gather if possible. This improves + // command overhead by eliminating the SG list read across the bus. + // + + if (descriptorNumber == 1) { + + // + // Change data pointer and count. + // + + ddaRequest->Size = (UCHAR)(sgList[0].Count / 512); + ddaRequest->PhysicalAddress = sgList[0].Address; + + // + // Determine new command code. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ddaRequest->Command = DDA_COMMAND_READ; + } else { + ddaRequest->Command = DDA_COMMAND_WRITE; + } + + } else { + + ddaRequest->Size = (UCHAR)descriptorNumber; + + // + // Calculate physical address of the scatter/gather list. + // + + ddaRequest->PhysicalAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + NULL, + sgList, + &length)); + + // + // Check firmware to determine whether scatter/gather + // descriptors must be converted to sector descripters or + // whether byte-level scatter/gather can be used. + // + + if (DeviceExtension->MajorVersion == 1) { + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ddaRequest->Command = DDA_COMMAND_SG_READ; + } else { + ddaRequest->Command = DDA_COMMAND_SG_WRITE; + } + + // + // Convert byte counts to sector counts. + // + + for (descriptorNumber=0; + descriptorNumber < ddaRequest->Size; descriptorNumber++) { + sgList[descriptorNumber].Count /= 512; + } + + } else { + + // + // Use byte-level scatter/gather. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ddaRequest->Command = DDA_COMMAND_SG_READB; + } else { + ddaRequest->Command = DDA_COMMAND_SG_WRITEB; + } + } + } + + return; + +} // end BuildRequest() + + +VOID +BuildVerifyRequest( + IN PDEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + +Arguments: + + DeviceExtension - Represents the target disk. + SRB - System request. + +Return Value: + + None. + +--*/ + +{ + PDDA_REQUEST_BLOCK ddaRequest = Srb->SrbExtension; + PUCHAR dataPointer; + ULONG descriptorNumber; + ULONG bytesLeft; + PSG_DESCRIPTOR sgList; + ULONG length; + USHORT sectorCount; + + // + // Set drive number if request packet. + // + + ddaRequest->DriveNumber = Srb->TargetId; + + // + // Use SRB tag as request id. + // + + ddaRequest->RequestId = Srb->QueueTag; + DeviceExtension->Srb[Srb->QueueTag] = Srb; + + // + // Determine starting block number. + // + + ddaRequest->StartingSector = + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + // + // Get data pointer, byte count and index to scatter/gather list. + // + + sectorCount = ((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 | ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb; + DeviceExtension->CurrentSector = ddaRequest->StartingSector; + + if (sectorCount > 0x80) { + DeviceExtension->RemainingSectors = sectorCount - 0x80; + sectorCount = 0x80; + } else { + DeviceExtension->RemainingSectors = 0; + } + + ddaRequest->Size = (UCHAR)sectorCount; + ddaRequest->Command = DDA_COMMAND_VERIFY; + ddaRequest->PhysicalAddress = 0; + + return; + +} // end BuildVerifyRequest() + +BOOLEAN +SubmitRequest( + IN PDEVICE_EXTENSION DeviceExtension, + IN PDDA_REQUEST_BLOCK DdaRequest + ) + +/*++ + +Routine Description: + + Submit DDA/DSA request to controller. + +Arguments: + + DeviceExtension - Address of adapter storage area. + DdaRequest - Request to be submitted. + +Return Value: + + TRUE - if request submitted. + FALSE - if timeout occurred waiting for submission semaphore. + +--*/ + +{ + PDDA_REGISTERS bmic = DeviceExtension->Bmic; + ULONG i; + + // + // Claim submission semaphore. + // + + for (i=0; i<100; i++) { + + if (ScsiPortReadPortUchar(&bmic->SubmissionSemaphore)) { + ScsiPortStallExecution(20); + } else { + break; + } + } + + // + // Check for timeout. + // + + if (i == 100) { + + DebugPrint((1, + "DELLDSA: SubmitRequest: Timeout waiting for submission channel %x\n", + DdaRequest)); + + return FALSE; + } + + // + // Submit request. + // + + ScsiPortWritePortUchar(&bmic->SubmissionSemaphore, 1); + ScsiPortWritePortUchar(&bmic->Command, DdaRequest->Command); + ScsiPortWritePortUchar(&bmic->DriveNumber, DdaRequest->DriveNumber); + ScsiPortWritePortUchar(&bmic->TransferCount, DdaRequest->Size); + ScsiPortWritePortUchar(&bmic->RequestIdOut, DdaRequest->RequestId); + ScsiPortWritePortUlong(&bmic->StartingSector, DdaRequest->StartingSector); + ScsiPortWritePortUlong(&bmic->DataAddress, DdaRequest->PhysicalAddress); + ScsiPortWritePortUchar(&bmic->SubmissionDoorBell, + DDA_DOORBELL_LOGICAL_COMMAND); + + return TRUE; + +} // SubmitRequest() + + +BOOLEAN +DsaResetBus( + 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 + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG i; + + // + // Reset controller. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 1); + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionDoorBell, + DDA_DOORBELL_SOFT_RESET); + + // + // Spin for reset completion. + // + + for (i=0; i<1000000; i++) { + + if (!(ScsiPortReadPortUchar(&deviceExtension->Bmic->SubmissionSemaphore) & 1)) { + break; + } + + ScsiPortStallExecution(5); + } + + // + // Check for timeout. + // + + if (i == 1000000) { + return FALSE; + } + + // + // Complete all outstanding requests. + // + + ScsiPortCompleteRequest(HwDeviceExtension, + (UCHAR)PathId, + 0xFF, + 0xFF, + SRB_STATUS_BUS_RESET); + + // + // Clear all SRB entries in device extension. + // + + for (i=0; i<256; i++) { + deviceExtension->Srb[i] = NULL; + } + + // + // Adapter ready for next request. + // + + ScsiPortNotification(NextRequest, + HwDeviceExtension, + NULL); + + return TRUE; + +} // end DsaResetBus() + + +BOOLEAN +DsaInterrupt( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This is the interrupt handler for the DELL DDA/DSA controller. + +Arguments: + + DeviceExtension - Represents the target controller. + +Return Value: + + Return TRUE if controller is interrupting. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PDDA_REGISTERS bmic = deviceExtension->Bmic; + PDDA_REQUEST_BLOCK ddaRequest; + PSCSI_REQUEST_BLOCK srb; + UCHAR status; + UCHAR tag; + + // + // Check if interrupt expected and clear it. + // + + status = ScsiPortReadPortUchar(&deviceExtension->Bmic->CompletionDoorBell); + ScsiPortWritePortUchar(&deviceExtension->Bmic->CompletionDoorBell, status); + + if (!(status & DDA_INTERRUPTS)) { + + // + // Interrupt is spurious. + // + + return FALSE; + } + + if (status & DDA_DOORBELL_EXTENDED_COMMAND) { + return TRUE; + } + + status = ScsiPortReadPortUchar(&bmic->Status); + tag = ScsiPortReadPortUchar(&bmic->RequestIdIn); + + // + // Release completion semaphore. + // + + ScsiPortWritePortUchar(&bmic->CompletionSemaphore, 0); + + // + // Get SRB address. + // + + srb = deviceExtension->Srb[tag]; + deviceExtension->Srb[tag] = NULL; + + if (!srb) { + return TRUE; + } + + if (srb->Cdb[0] == SCSIOP_VERIFY) { + + if (status == DDA_STATUS_NO_ERROR) { + + // + // See if the verify isn't yet complete. + // + + if (deviceExtension->RemainingSectors != 0) { + + ULONG i; + USHORT sectorCount; + + ddaRequest = srb->SrbExtension; + sectorCount = ddaRequest->Size; + + // + // Determine the new starting block number. + // + + ddaRequest->StartingSector += sectorCount; + + sectorCount = (USHORT)deviceExtension->RemainingSectors; + + if (sectorCount > 0x80) { + deviceExtension->RemainingSectors = sectorCount - 0x80; + sectorCount = 0x80; + } else { + deviceExtension->RemainingSectors = 0; + } + + + ddaRequest->Size = (UCHAR)sectorCount; + ddaRequest->Command = DDA_COMMAND_VERIFY; + ddaRequest->PhysicalAddress = 0; + + ddaRequest->RequestId = srb->QueueTag; + deviceExtension->Srb[srb->QueueTag] = srb; + + // + // Claim submission semaphore. + // + + for (i=0; i<100; i++) { + + if (ScsiPortReadPortUchar(&bmic->SubmissionSemaphore)) { + ScsiPortStallExecution(20); + } else { + break; + } + } + + // + // Check for timeout. + // + + if (i == 100) { + + DebugPrint((1, + "DELLDSA: SubmitRequest: Timeout waiting for submission channel %x\n", + ddaRequest)); + + status = DDA_STATUS_TIMEOUT; + + } else { + + // + // Submit request. + // + + ScsiPortWritePortUchar(&bmic->SubmissionSemaphore, 1); + ScsiPortWritePortUchar(&bmic->Command, ddaRequest->Command); + ScsiPortWritePortUchar(&bmic->DriveNumber, ddaRequest->DriveNumber); + ScsiPortWritePortUchar(&bmic->TransferCount, ddaRequest->Size); + ScsiPortWritePortUchar(&bmic->RequestIdOut, ddaRequest->RequestId); + ScsiPortWritePortUlong(&bmic->StartingSector, ddaRequest->StartingSector); + ScsiPortWritePortUlong(&bmic->DataAddress, ddaRequest->PhysicalAddress); + ScsiPortWritePortUchar(&bmic->SubmissionDoorBell, + DDA_DOORBELL_LOGICAL_COMMAND); + + return TRUE; + } + } else { + + // + // Ask for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + + } + } + } + + // + // Map DDA/DSA status to SRB status. + // + + switch (status) { + + case DDA_STATUS_NO_ERROR: + + srb->SrbStatus = SRB_STATUS_SUCCESS; + break; + + case DDA_STATUS_TIMEOUT: + + srb->SrbStatus = SRB_STATUS_TIMEOUT; + break; + + case DDA_STATUS_TRACK0_NOT_FOUND: + case DDA_STATUS_ABORTED: + case DDA_STATUS_UNCORRECTABLE_ERROR: + + srb->SrbStatus = SRB_STATUS_ERROR; + break; + + case DDA_STATUS_CORRECTABLE_ERROR: + + srb->SrbStatus = SRB_STATUS_ERROR_RECOVERY; + break; + + case DDA_STATUS_SECTOR_ID_NOT_FOUND: + + srb->SrbStatus = SRB_STATUS_ERROR; + break; + + case DDA_STATUS_BAD_BLOCK_FOUND: + case DDA_STATUS_WRITE_ERROR: + + srb->SrbStatus = SRB_STATUS_ERROR; + break; + } + + // + // Inform system that this request is complete. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + return TRUE; + +} // end DsaInterrupt() + + +BOOLEAN +DsaStartIo( + 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; + UCHAR status; + ULONG i; + + switch (Srb->Function) { + + case SRB_FUNCTION_RESET_BUS: + + if (!DsaResetBus(deviceExtension, + Srb->PathId)) { + + status = SRB_STATUS_ERROR; + + } else { + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SRB_FUNCTION_EXECUTE_SCSI: + + switch (Srb->Cdb[0]) { + + case SCSIOP_VERIFY: + + + BuildVerifyRequest(deviceExtension,Srb); + + if (SubmitRequest(deviceExtension, + (PDDA_REQUEST_BLOCK)Srb->SrbExtension)) { + + status = SRB_STATUS_PENDING; + + // + // return, upon completion the verify path will ask for the next request. + // + + return TRUE; + + } else { + + // + // Timed out waiting for submission channel to clear. + // + + status = SRB_STATUS_BUSY; + } + + break; + + + case SCSIOP_WRITE: + case SCSIOP_READ: + + // + // Build command list from SRB. + // + + BuildRequest(deviceExtension, + Srb); + + // + // Submit request to controller. + // + + if (SubmitRequest(deviceExtension, + (PDDA_REQUEST_BLOCK)Srb->SrbExtension)) { + + status = SRB_STATUS_PENDING; + + } else { + + // + // Timed out waiting for submission channel to clear. + // + + status = SRB_STATUS_BUSY; + } + + break; + + case SCSIOP_TEST_UNIT_READY: + + status = SRB_STATUS_SUCCESS; + + break; + + case SCSIOP_READ_CAPACITY: + + // + // Issue extended command to get disk geometry. + // + + if (!ExtendedCommand(deviceExtension, + Srb->TargetId, + DDA_GET_LOGICAL_GEOMETRY)) { + + DebugPrint((1,"DsaStartIo: Couldn't get logical drive geometry\n")); + status = SRB_STATUS_ERROR; + + } else { + + ULONG blockSize = 512; + UCHAR sectorsPerTrack = + ScsiPortReadPortUchar(&((PDDA_GET_GEOMETRY)deviceExtension->Bmic)->SectorsPerTrack); + UCHAR numberOfHeads = + ScsiPortReadPortUchar(&((PDDA_GET_GEOMETRY)deviceExtension->Bmic)->NumberOfHeads); + USHORT numberOfCylinders = + ScsiPortReadPortUshort(&((PDDA_GET_GEOMETRY)deviceExtension->Bmic)->NumberOfCylinders); + ULONG numberOfBlocks; + + // + // Release semaphore. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 0); + + DebugPrint((1, + "DsaStartIo: Block size %x\n", + blockSize)); + + DebugPrint((1, + "DsaStartIo: Sectors per track %x\n", + sectorsPerTrack)); + + DebugPrint((1, + "DsaStartIo: Number of heads %x\n", + numberOfHeads)); + + DebugPrint((1, + "DsaStartIo: Number of cylinders %x\n", + numberOfCylinders)); + + // + // Calculate number of sectors on this disk. + // + + numberOfBlocks = + sectorsPerTrack * + numberOfHeads * + numberOfCylinders; + + // + // 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, + &numberOfBlocks); + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_INQUIRY: + + // + // Only respond at logical unit 0; + // + + if (Srb->Lun != 0 || + Srb->TargetId >= + deviceExtension->NumberOfLogicalDrives) { + + // + // Indicate no device found at this address. + // + + status = SRB_STATUS_SELECTION_TIMEOUT; + + } else { + + PINQUIRYDATA inquiryData = Srb->DataBuffer; + + // + // Zero INQUIRY data structure. + // + + for (i = 0; i < Srb->DataTransferLength; i++) { + ((PUCHAR)Srb->DataBuffer)[i] = 0; + } + + // + // Dell DDA/DSA only supports disks. + // + + inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; + + // + // Fill in vendor identification fields. + // + + inquiryData->VendorId[0] = 'D'; + inquiryData->VendorId[1] = 'e'; + inquiryData->VendorId[2] = 'l'; + inquiryData->VendorId[3] = 'l'; + inquiryData->VendorId[4] = ' '; + inquiryData->VendorId[5] = ' '; + inquiryData->VendorId[6] = ' '; + inquiryData->VendorId[7] = 'D'; + inquiryData->VendorId[8] = 'i'; + inquiryData->VendorId[9] = 's'; + inquiryData->VendorId[10] = 'k'; + inquiryData->VendorId[11] = ' '; + inquiryData->VendorId[12] = 'A'; + inquiryData->VendorId[13] = 'r'; + inquiryData->VendorId[14] = 'r'; + inquiryData->VendorId[15] = 'a'; + inquiryData->VendorId[16] = 'y'; + inquiryData->VendorId[17] = ' '; + inquiryData->VendorId[18] = ' '; + inquiryData->VendorId[19] = ' '; + inquiryData->VendorId[20] = ' '; + + // + // Initialize unused portion of product id. + // + + for (i = 0; i < 4; i++) { + inquiryData->ProductId[12+i] = ' '; + } + + // + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + // + + for (i = 0; i < 4; i += 2) { + inquiryData->ProductRevisionLevel[i] = ' '; + inquiryData->ProductRevisionLevel[i+1] = ' '; + } + + status = SRB_STATUS_SUCCESS; + } + + break; + + default: + + status = SRB_STATUS_INVALID_REQUEST; + + break; + + } // end switch (Srb->Cdb[0]) + + 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); + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } else { + + // + // 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 DsaStartIo() + + +BOOLEAN +SearchEisaBus( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo + ) + +/*++ + +Routine Description: + + This routine is called from DsaFindAdapter 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 Dell DDA/DSA 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 information 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 Dell board id. + // + + if ((slotInformation->CompressedId & 0xf0ffffff) != DDA_ID) { + continue; + } + + // + // Set up default port address. + // + + accessRange->RangeStart.LowPart = + (eisaSlotNumber * 0x1000) + 0x0C80; + accessRange->RangeLength = sizeof(DDA_REGISTERS); + + accessRange++; + + // + // 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 = Latched; + } + + // + // 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, + "DELLDSA: 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; + } + + return FALSE; + +} // end SearchEisaBus() + + +BOOLEAN +DsaInitialize( + 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; + ULONG i; + + // + // Check if controller needs a reset. + // + + if (ScsiPortReadPortUchar(&deviceExtension->Bmic->SubmissionSemaphore)) { + + // + // Issue a soft reset. + // + + DebugPrint((1, + "DellDsa: Submission channnel stuck; Resetting controller\n")); + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 1); + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionDoorBell, + DDA_DOORBELL_SOFT_RESET); + + // + // Spin for reset completion. + // + + for (i=0; i<1000000; i++) { + + if (!(ScsiPortReadPortUchar((PUCHAR)&deviceExtension->Bmic->SubmissionSemaphore) & 1)) { + break; + } + + ScsiPortStallExecution(5); + } + + // + // Check for timeout. + // + + if (i == 1000000) { + + return FALSE; + } + } + + // + // Enable completion interrupts. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl, 1); + ScsiPortWritePortUchar(&deviceExtension->Bmic->CompletionInterruptMask, + DDA_DOORBELL_LOGICAL_COMMAND); + + // + // Clear all SRB entries in device extension. + // + + for (i=0; i<256; i++) { + deviceExtension->Srb[i] = NULL; + } + + return TRUE; + +} // end DsaInitialize() + + +ULONG +DsaFindAdapter( + 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; + + // + // 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); + + + ConfigInfo->NumberOfBuses = 1; + ConfigInfo->ScatterGather = TRUE; + ConfigInfo->Master = TRUE; + ConfigInfo->Dma32BitAddresses = TRUE; + + + // + // Initialize controller. + // + + DsaInitialize(deviceExtension); + + // + // Get firmware version. + // + + if (!ExtendedCommand(deviceExtension, + 0, + DDA_GET_FIRMWARE_VERSION)) { + + DebugPrint((1,"DsaFindAdapter: Couldn't get firmware version\n")); + return SRB_STATUS_ERROR; + } + + deviceExtension->MajorVersion = + ScsiPortReadPortUshort((PUSHORT)&deviceExtension->Bmic->Command); + + deviceExtension->MinorVersion = + ScsiPortReadPortUshort((PUSHORT)&deviceExtension->Bmic->TransferCount); + + // + // Release semaphore. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 0); + + // + // Beta versions of DDA do not have scatter/gather. Versions prior + // to 1.23 have a starvation bug that makes command queueing risky. + // We simply won't run on these dinosaurs. + // + + if (deviceExtension->MajorVersion == 1 && + deviceExtension->MinorVersion <= 22) { + + // + // Log error and return. + // + + ScsiPortLogError(HwDeviceExtension, + NULL, + 0, + 0, + 0, + SP_BAD_FW_ERROR, + 5 << 8); + + return SRB_STATUS_ERROR; + } + + // + // DDA adapters support scatter/gather with the caveat that the descriptors + // must be sector aligned and can't handle large transfers. + // + + if (deviceExtension->MajorVersion == 1) { + ConfigInfo->AlignmentMask = 511; + ConfigInfo->MaximumTransferLength = 0x8000; + ConfigInfo->NumberOfPhysicalBreaks = 8; + } else { + ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS; + } + + // + // Get noncached extension for identify requests. + // + + deviceExtension->IdentifyBuffer = + ScsiPortGetUncachedExtension(deviceExtension, + ConfigInfo, + 512); + + // + // Get hardware configuration. + // + + if (!ExtendedCommand(deviceExtension, + 0, + DDA_GET_HARDWARE_CONFIGURATION)) { + + DebugPrint((1,"DsaFindAdapter: Couldn't get hw configuration\n")); + return SRB_STATUS_ERROR; + } + + // + // Record interrupt level. + // + + ConfigInfo->BusInterruptLevel = ScsiPortReadPortUchar(&deviceExtension->Bmic->Command); + + // + // Release semaphore. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 0); + + // + // Read Number of drives and emulation mode. + // + + if (!ExtendedCommand(deviceExtension, + 0, + DDA_GET_NUMBER_LOGICAL_DRIVES)) { + + DebugPrint((1,"DsaFindAdapter: Couldn't get number of drives\n")); + + return SRB_STATUS_ERROR; + } + + deviceExtension->NumberOfLogicalDrives = + ScsiPortReadPortUchar(&deviceExtension->Bmic->Command); + deviceExtension->EmulationMode = + ScsiPortReadPortUchar(&deviceExtension->Bmic->DriveNumber); + + // + // Release semaphore. + // + + ScsiPortWritePortUchar(&deviceExtension->Bmic->SubmissionSemaphore, 0); + + // + // Check if any logical drives reported. + // + + DebugPrint((1, + "DsaFindAdapter: numDrives %d emulation %d\n", + deviceExtension->NumberOfLogicalDrives, + deviceExtension->EmulationMode)); + + if (!deviceExtension->NumberOfLogicalDrives) { + + DebugPrint((1,"DsaFindAdapter: No logical drives defined\n")); + return SP_RETURN_NOT_FOUND; + } + + // + // Tell system to look for more adapters. + // + + *Again = TRUE; + + return SP_RETURN_FOUND; + +} // end DsaFindAdapter() + + +ULONG +DriverEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + This is the initial entry point for system initialization. + +Arguments: + + DriverObject - System's representation of this driver. + Argument2 - Not used. + +Return Value: + + BOOLEAN + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; + ULONG eisaSlotNumber; + ULONG i; + + DebugPrint((1,"\n\nDELL DSA/DDA Miniport Driver\n")); + + // + // Zero out structure. + // + + for (i=0; i + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Dell Disk Array Driver" +#define VER_INTERNALNAME_STR "delldsa.sys" +#define VER_ORIGINALFILENAME_STR "delldsa.sys" + +#include "common.ver" + diff --git a/private/ntos/miniport/dell/makefile b/private/ntos/miniport/dell/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/miniport/dell/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/miniport/dell/sources b/private/ntos/miniport/dell/sources new file mode 100644 index 000000000..80ff399f7 --- /dev/null +++ b/private/ntos/miniport/dell/sources @@ -0,0 +1,40 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=miniport + +TARGETNAME=delldsa +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=MINIPORT + +INCLUDES=..\..\inc + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib +!IF $(ALPHA) +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib $(BASEDIR)\public\sdk\lib\*\libcntpr.lib +!ENDIF +SOURCES=delldsa.c delldsa.rc + + -- cgit v1.2.3