diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/miniport/atapi/atapi.c | 7983 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/atapi.h | 857 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/atapi.rc | 12 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/intel.c | 37 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/intel.h | 10 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/makefile | 6 | ||||
-rw-r--r-- | private/ntos/miniport/atapi/sources | 42 |
7 files changed, 8947 insertions, 0 deletions
diff --git a/private/ntos/miniport/atapi/atapi.c b/private/ntos/miniport/atapi/atapi.c new file mode 100644 index 000000000..35e8d6c1f --- /dev/null +++ b/private/ntos/miniport/atapi/atapi.c @@ -0,0 +1,7983 @@ +/*++ + +Copyright (c) 1993-6 Microsoft Corporation + +Module Name: + + atapi.c + +Abstract: + + This is the miniport driver for ATAPI IDE controllers. + +Author: + + Mike Glass (MGlass) + Chuck Park (ChuckP) + Joe Dai (joedai) + +Environment: + + kernel mode only + +Notes: + +Revision History: + + george C.(georgioc) Merged wtih Compaq code to make miniport driver function + with the 120MB floppy drive + Added support for MEDIA STATUS NOTIFICATION + Added support for SCSIOP_START_STOP_UNIT (eject media) + + joedai PCI Bus Master IDE Support + ATA Passthrough (temporary solution) + LBA with ATA drive > 8G + PCMCIA IDE support + Native mode support + +--*/ + + +#include "miniport.h" +#include "atapi.h" // includes scsi.h +#include "ntdddisk.h" +#include "ntddscsi.h" + +#include "intel.h" + +// +// Logical unit extension +// + +typedef struct _HW_LU_EXTENSION { + ULONG Reserved; +} HW_LU_EXTENSION, *PHW_LU_EXTENSION; + +// +// control DMA detection +// +ULONG AtapiPlaySafe = 0; + +// +// PCI IDE Controller List +// +CONTROLLER_PARAMETERS +PciControllerParameters[] = { + { PCIBus, + "8086", // Intel + 4, + "7111", // PIIX4 82371 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + IntelIsChannelEnabled + }, + { PCIBus, + "8086", // Intel + 4, + "7010", // PIIX3 82371 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + IntelIsChannelEnabled + }, + { PCIBus, + "8086", // Intel + 4, + "1230", // PIIX 82371 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + IntelIsChannelEnabled + }, + { PCIBus, + "1095", // CMD + 4, + "0646", // 646 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + ChannelIsAlwaysEnabled + }, + { PCIBus, + "10b9", // ALi (Acer) + 4, + "5219", // 5219 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + ChannelIsAlwaysEnabled + }, + { PCIBus, + "1039", // SiS + 4, + "5513", // 5513 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + ChannelIsAlwaysEnabled + }, + { PCIBus, + "0e11", // Compaq + 4, + "ae33", // + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + ChannelIsAlwaysEnabled + }, + { PCIBus, + "10ad", // WinBond + 4, + "0105", // 105 + 4, + 2, // NumberOfIdeBus + FALSE, // Dual FIFO + NULL, + ChannelIsAlwaysEnabled + }, + + +// Broken PCI controllers + { PCIBus, + "1095", // CMD + 4, + "0640", // 640 + 4, + 2, // NumberOfIdeBus + TRUE, // Single FIFO + NULL, + ChannelIsAlwaysEnabled + }, + { PCIBus, + "1039", // SiS + 4, + "0601", // ???? + 4, + 2, // NumberOfIdeBus + TRUE, // Single FIFO + NULL, + ChannelIsAlwaysEnabled + } +}; +#define NUMBER_OF_PCI_CONTROLLER (sizeof(PciControllerParameters) / sizeof(CONTROLLER_PARAMETERS)) + +PSCSI_REQUEST_BLOCK +BuildMechanismStatusSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ); + +PSCSI_REQUEST_BLOCK +BuildRequestSenseSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ); + +VOID +AtapiHwInitializeChanger ( + IN PVOID HwDeviceExtension, + IN ULONG TargetId, + IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus + ); + +ULONG +AtapiSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +AtapiZeroMemory( + IN PCHAR Buffer, + IN ULONG Count + ); + +VOID +AtapiHexToString ( + ULONG Value, + PCHAR *Buffer + ); + +LONG +AtapiStringCmp ( + PCHAR FirstStr, + PCHAR SecondStr, + ULONG Count + ); + +BOOLEAN +AtapiInterrupt( + IN PVOID HwDeviceExtension + ); + +BOOLEAN +AtapiHwInitialize( + IN PVOID HwDeviceExtension + ); + +ULONG +IdeBuildSenseBuffer( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +IdeMediaStatus( + IN BOOLEAN EnableMSN, + IN PVOID HwDeviceExtension, + IN ULONG Channel + ); + +VOID +DeviceSpecificInitialize( + IN PVOID HwDeviceExtension + ); + +BOOLEAN +PrepareForBusMastering( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +EnableBusMastering( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +SetBusMasterDetectionLevel ( + IN PVOID HwDeviceExtension, + IN PCHAR userArgumentString + ); + +BOOLEAN +AtapiDeviceDMACapable ( + IN PVOID HwDeviceExtension, + IN ULONG deviceNumber + ); + +#if defined (xDBG) +// Need to link to nt kernel +void KeQueryTickCount(PLARGE_INTEGER c); +LONGLONG lastTickCount = 0; +#define DebugPrintTickCount() _DebugPrintTickCount (__LINE__) + +// +// for performance tuning +// +void _DebugPrintTickCount (ULONG lineNumber) +{ + LARGE_INTEGER tickCount; + + KeQueryTickCount(&tickCount); + DebugPrint ((1, "Line %u: CurrentTick = %u (%u ticks since last check)\n", lineNumber, tickCount.LowPart, (ULONG) (tickCount.QuadPart - lastTickCount))); + lastTickCount = tickCount.QuadPart; +} +#else +#define DebugPrintTickCount() +#endif //DBG + + + +BOOLEAN +IssueIdentify( + IN PVOID HwDeviceExtension, + IN ULONG DeviceNumber, + IN ULONG Channel, + IN UCHAR Command, + IN BOOLEAN InterruptOff + ) + +/*++ + +Routine Description: + + Issue IDENTIFY command to a device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + DeviceNumber - Indicates which device. + Command - Either the standard (EC) or the ATAPI packet (A1) IDENTIFY. + InterruptOff - should leave interrupt disabled + +Return Value: + + TRUE if all goes well. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel] ; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + ULONG waitCount = 20000; + ULONG i,j; + UCHAR statusByte; + UCHAR signatureLow, + signatureHigh; + IDENTIFY_DATA fullIdentifyData; + + DebugPrintTickCount(); + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); + + // + // Check that the status register makes sense. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + if (Command == IDE_COMMAND_IDENTIFY) { + + // + // Mask status byte ERROR bits. + // + + statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX); + + DebugPrint((1, + "IssueIdentify: Checking for IDE. Status (%x)\n", + statusByte)); + + // + // Check if register value is reasonable. + // + + if (statusByte != IDE_STATUS_IDLE) { + + // + // Reset the controller. + // + + AtapiSoftReset(baseIoAddress1,DeviceNumber, InterruptOff); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); + + WaitOnBusy(baseIoAddress1,statusByte); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + DebugPrintTickCount(); + return FALSE; + } + + DebugPrint((1, + "IssueIdentify: Resetting controller.\n")); + + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS); + ScsiPortStallExecution(500 * 1000); + if (InterruptOff) { + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS); + } else { + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER); + } + + + // We really should wait up to 31 seconds + // The ATA spec. allows device 0 to come back from BUSY in 31 seconds! + // (30 seconds for device 1) + do { + + // + // Wait for Busy to drop. + // + + ScsiPortStallExecution(100); + GetStatus(baseIoAddress1, statusByte); + + } while ((statusByte & IDE_STATUS_BUSY) && waitCount--); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); + + // + // Another check for signature, to deal with one model Atapi that doesn't assert signature after + // a soft reset. + // + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + DebugPrintTickCount(); + return FALSE; + } + + statusByte &= ~IDE_STATUS_INDEX; + + if (statusByte != IDE_STATUS_IDLE) { + + // + // Give up on this. + // + + DebugPrintTickCount(); + return FALSE; + } + + } + + } else { + + DebugPrint((1, + "IssueIdentify: Checking for ATAPI. Status (%x)\n", + statusByte)); + + } + + // + // Load CylinderHigh and CylinderLow with number bytes to transfer. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, (0x200 >> 8)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, (0x200 & 0xFF)); + + for (j = 0; j < 2; j++) { + + // + // Send IDENTIFY command. + // + + WaitOnBusy(baseIoAddress1,statusByte); + + ScsiPortWritePortUchar(&baseIoAddress1->Command, Command); + + WaitOnBusy(baseIoAddress1,statusByte); + + // + // Wait for DRQ. + // + + for (i = 0; i < 4; i++) { + + WaitForDrq(baseIoAddress1, statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + // + // Read status to acknowledge any interrupts generated. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + // + // One last check for Atapi. + // + + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + DebugPrintTickCount(); + return FALSE; + } + + break; + } + + if (Command == IDE_COMMAND_IDENTIFY) { + + // + // Check the signature. If DRQ didn't come up it's likely Atapi. + // + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + DebugPrintTickCount(); + return FALSE; + } + } + + WaitOnBusy(baseIoAddress1,statusByte); + } + + if (i == 4 && j == 0) { + + // + // Device didn't respond correctly. It will be given one more chances. + // + + DebugPrint((1, + "IssueIdentify: DRQ never asserted (%x). Error reg (%x)\n", + statusByte, + ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1))); + + AtapiSoftReset(baseIoAddress1, DeviceNumber, InterruptOff); + + GetStatus(baseIoAddress1,statusByte); + + DebugPrint((1, + "IssueIdentify: Status after soft reset (%x)\n", + statusByte)); + + } else { + + break; + + } + } + + // + // Check for error on really stupid master devices that assert random + // patterns of bits in the status register at the slave address. + // + + if ((Command == IDE_COMMAND_IDENTIFY) && (statusByte & IDE_STATUS_ERROR)) { + DebugPrintTickCount(); + return FALSE; + } + + DebugPrint((1, + "IssueIdentify: Status before read words %x\n", + statusByte)); + + // + // Suck out 256 words. After waiting for one model that asserts busy + // after receiving the Packet Identify command. + // + + WaitOnBusy(baseIoAddress1,statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) { + DebugPrintTickCount(); + return FALSE; + } + + ReadBuffer(baseIoAddress1, + (PUSHORT)&fullIdentifyData, + sizeof (fullIdentifyData) / 2); + + // + // Check out a few capabilities / limitations of the device. + // + + if (fullIdentifyData.SpecialFunctionsEnabled & 1) { + + // + // Determine if this drive supports the MSN functions. + // + + DebugPrint((2,"IssueIdentify: Marking drive %d as removable. SFE = %d\n", + Channel * 2 + DeviceNumber, + fullIdentifyData.SpecialFunctionsEnabled)); + + + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE; + } + + if (fullIdentifyData.MaximumBlockTransfer) { + + // + // Determine max. block transfer for this device. + // + + deviceExtension->MaximumBlockXfer[(Channel * 2) + DeviceNumber] = + (UCHAR)(fullIdentifyData.MaximumBlockTransfer & 0xFF); + } + + ScsiPortMoveMemory(&deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber],&fullIdentifyData,sizeof(IDENTIFY_DATA2)); + + if (deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0x20 && + Command != IDE_COMMAND_IDENTIFY) { + + // + // This device interrupts with the assertion of DRQ after receiving + // Atapi Packet Command + // + + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_INT_DRQ; + + DebugPrint((2, + "IssueIdentify: Device interrupts on assertion of DRQ.\n")); + + } else { + + DebugPrint((2, + "IssueIdentify: Device does not interrupt on assertion of DRQ.\n")); + } + + if (((deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0xF00) == 0x100) && + Command != IDE_COMMAND_IDENTIFY) { + + // + // This is a tape. + // + + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_TAPE_DEVICE; + + DebugPrint((2, + "IssueIdentify: Device is a tape drive.\n")); + + } else { + + DebugPrint((2, + "IssueIdentify: Device is not a tape drive.\n")); + } + + // + // Work around for some IDE and one model Atapi that will present more than + // 256 bytes for the Identify data. + // + + WaitOnBusy(baseIoAddress1,statusByte); + + for (i = 0; i < 0x10000; i++) { + + GetStatus(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + // + // Suck out any remaining bytes and throw away. + // + + ScsiPortReadPortUshort(&baseIoAddress1->Data); + + } else { + + break; + + } + } + + DebugPrint((3, + "IssueIdentify: Status after read words (%x)\n", + statusByte)); + + DebugPrintTickCount(); + return TRUE; + +} // end IssueIdentify() + + +BOOLEAN +SetDriveParameters( + IN PVOID HwDeviceExtension, + IN ULONG DeviceNumber, + IN ULONG Channel + ) + +/*++ + +Routine Description: + + Set drive parameters using the IDENTIFY data. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + DeviceNumber - Indicates which device. + +Return Value: + + TRUE if all goes well. + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber]; + ULONG i; + UCHAR statusByte; + + DebugPrint((1, + "SetDriveParameters: Number of heads %x\n", + identifyData->NumberOfHeads)); + + DebugPrint((1, + "SetDriveParameters: Sectors per track %x\n", + identifyData->SectorsPerTrack)); + + // + // Set up registers for SET PARAMETER command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((DeviceNumber << 4) | 0xA0) | (identifyData->NumberOfHeads - 1))); + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)identifyData->SectorsPerTrack); + + // + // Send SET PARAMETER command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_SET_DRIVE_PARAMETERS); + + // + // Wait for up to 30 milliseconds for ERROR or command complete. + // + + for (i=0; i<30 * 1000; i++) { + + UCHAR errorByte; + + GetStatus(baseIoAddress1, statusByte); + + if (statusByte & IDE_STATUS_ERROR) { + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + DebugPrint((1, + "SetDriveParameters: Error bit set. Status %x, error %x\n", + errorByte, + statusByte)); + + return FALSE; + } else if ((statusByte & ~IDE_STATUS_INDEX ) == IDE_STATUS_IDLE) { + break; + } else { + ScsiPortStallExecution(100); + } + } + + // + // Check for timeout. + // + + if (i == 30 * 1000) { + return FALSE; + } else { + return TRUE; + } + +} // end SetDriveParameters() + + +BOOLEAN +AtapiResetController( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ) + +/*++ + +Routine Description: + + Reset IDE controller and/or Atapi device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + Nothing. + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG numberChannels = deviceExtension->NumberChannels; + PIDE_REGISTERS_1 baseIoAddress1; + PIDE_REGISTERS_2 baseIoAddress2; + BOOLEAN result = FALSE; + ULONG i,j; + UCHAR statusByte; + + DebugPrint((2,"AtapiResetController: Reset IDE\n")); + + // + // Check and see if we are processing an internal srb + // + if (deviceExtension->OriginalSrb) { + deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + // + // Check if request is in progress. + // + + if (deviceExtension->CurrentSrb) { + + // + // Complete outstanding request with SRB_STATUS_BUS_RESET. + // + + ScsiPortCompleteRequest(deviceExtension, + deviceExtension->CurrentSrb->PathId, + deviceExtension->CurrentSrb->TargetId, + deviceExtension->CurrentSrb->Lun, + (ULONG)SRB_STATUS_BUS_RESET); + + // + // Clear request tracking fields. + // + + deviceExtension->CurrentSrb = NULL; + deviceExtension->WordsLeft = 0; + deviceExtension->DataBuffer = NULL; + + // + // Indicate ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } + + // + // Clear DMA + // + if (deviceExtension->DMAInProgress) { + + for (j = 0; j < numberChannels; j++) { + UCHAR dmaStatus; + + dmaStatus = ScsiPortReadPortUchar (&deviceExtension->BusMasterPortBase[j]->Status); + ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[j]->Command, 0); // disable BusMastering + ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[j]->Status, + (UCHAR) (dmaStatus & (BUSMASTER_DEVICE0_DMA_OK | BUSMASTER_DEVICE1_DMA_OK))); // clear interrupt/error + } + deviceExtension->DMAInProgress = FALSE; + } + + // + // Clear expecting interrupt flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->RDP = FALSE; + + for (j = 0; j < numberChannels; j++) { + + baseIoAddress1 = deviceExtension->BaseIoAddress1[j]; + baseIoAddress2 = deviceExtension->BaseIoAddress2[j]; + + // + // Do special processing for ATAPI and IDE disk devices. + // + + for (i = 0; i < 2; i++) { + + // + // Check if device present. + // + + if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_DEVICE_PRESENT) { + + // + // Check for ATAPI disk. + // + + if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_ATAPI_DEVICE) { + + // + // Issue soft reset and issue identify. + // + + GetStatus(baseIoAddress1,statusByte); + DebugPrint((1, + "AtapiResetController: Status before Atapi reset (%x).\n", + statusByte)); + + AtapiSoftReset(baseIoAddress1,i, FALSE); + + GetStatus(baseIoAddress1,statusByte); + + + if (statusByte == 0x0) { + + IssueIdentify(HwDeviceExtension, + i, + j, + IDE_COMMAND_ATAPI_IDENTIFY, + FALSE); + } else { + + DebugPrint((1, + "AtapiResetController: Status after soft reset %x\n", + statusByte)); + } + + } else { + + // + // Write IDE reset controller bits. + // + + IdeHardReset(baseIoAddress1, baseIoAddress2,result); + + if (!result) { + return FALSE; + } + + // + // Set disk geometry parameters. + // + + if (!SetDriveParameters(HwDeviceExtension, + i, + j)) { + + DebugPrint((1, + "AtapiResetController: SetDriveParameters failed\n")); + } + + // re-enable MSN + IdeMediaStatus(TRUE, HwDeviceExtension, j * numberChannels + i); + } + } + } + } + + // + // Call the HwInitialize routine to setup multi-block. + // + + AtapiHwInitialize(HwDeviceExtension); + + return TRUE; + +} // end AtapiResetController() + + + +ULONG +MapError( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine maps ATAPI and IDE errors to specific SRB statuses. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG i; + UCHAR errorByte; + UCHAR srbStatus; + UCHAR scsiStatus; + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + DebugPrint((1, + "MapError: Error register is %x\n", + errorByte)); + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + switch (errorByte >> 4) { + case SCSI_SENSE_NO_SENSE: + + DebugPrint((1, + "ATAPI: No sense information\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_RECOVERED_ERROR: + + DebugPrint((1, + "ATAPI: Recovered error\n")); + scsiStatus = 0; + srbStatus = SRB_STATUS_SUCCESS; + break; + + case SCSI_SENSE_NOT_READY: + + DebugPrint((1, + "ATAPI: Device not ready\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_MEDIUM_ERROR: + + DebugPrint((1, + "ATAPI: Media error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_HARDWARE_ERROR: + + DebugPrint((1, + "ATAPI: Hardware error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ILLEGAL_REQUEST: + + DebugPrint((1, + "ATAPI: Illegal request\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_UNIT_ATTENTION: + + DebugPrint((1, + "ATAPI: Unit attention\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_DATA_PROTECT: + + DebugPrint((1, + "ATAPI: Data protect\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_BLANK_CHECK: + + DebugPrint((1, + "ATAPI: Blank check\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ABORTED_COMMAND: + DebugPrint((1, + "Atapi: Command Aborted\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + default: + + DebugPrint((1, + "ATAPI: Invalid sense information\n")); + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + break; + } + + } else { + + scsiStatus = 0; + // + // Save errorByte,to be used by SCSIOP_REQUEST_SENSE. + // + + deviceExtension->ReturningMediaStatus = errorByte; + + if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) { + DebugPrint((1, + "IDE: Media change\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + } else if (errorByte & IDE_ERROR_COMMAND_ABORTED) { + DebugPrint((1, + "IDE: Command abort\n")); + srbStatus = SRB_STATUS_ABORTED; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + deviceExtension->ErrorCount++; + + } else if (errorByte & IDE_ERROR_END_OF_MEDIA) { + + DebugPrint((1, + "IDE: End of media\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)){ + deviceExtension->ErrorCount++; + } + + } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) { + + DebugPrint((1, + "IDE: Illegal length\n")); + srbStatus = SRB_STATUS_INVALID_REQUEST; + + } else if (errorByte & IDE_ERROR_BAD_BLOCK) { + + DebugPrint((1, + "IDE: Bad block\n")); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + } else if (errorByte & IDE_ERROR_ID_NOT_FOUND) { + + DebugPrint((1, + "IDE: Id not found\n")); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + deviceExtension->ErrorCount++; + + } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) { + + DebugPrint((1, + "IDE: Media change\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + } else if (errorByte & IDE_ERROR_DATA_ERROR) { + + DebugPrint((1, + "IDE: Data error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)){ + deviceExtension->ErrorCount++; + } + + // + // Build sense buffer + // + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + + if ((deviceExtension->ErrorCount >= MAX_ERRORS) && + (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA))) { + deviceExtension->DWordIO = FALSE; + deviceExtension->MaximumBlockXfer[Srb->TargetId] = 0; + + DebugPrint((1, + "MapError: Disabling 32-bit PIO and Multi-sector IOs\n")); + + // + // Log the error. + // + + ScsiPortLogError( HwDeviceExtension, + Srb, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + SP_BAD_FW_WARNING, + 4); + // + // Reprogram to not use Multi-sector. + // + + for (i = 0; i < 4; i++) { + UCHAR statusByte; + + if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT && + !(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((i & 0x1) << 4) | 0xA0)); + + // + // Setup sector count to reflect the # of blocks. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + 0); + + // + // Issue the command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_SET_MULTIPLE); + + // + // Wait for busy to drop. + // + + WaitOnBaseBusy(baseIoAddress1,statusByte); + + // + // Check for errors. Reset the value to 0 (disable MultiBlock) if the + // command was aborted. + // + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + + DebugPrint((1, + "AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n", + statusByte, + errorByte)); + // + // Adjust the devExt. value, if necessary. + // + + deviceExtension->MaximumBlockXfer[i] = 0; + + } + + deviceExtension->DeviceParameters[i].IdeReadCommand = IDE_COMMAND_READ; + deviceExtension->DeviceParameters[i].IdeWriteCommand = IDE_COMMAND_WRITE; + deviceExtension->DeviceParameters[i].MaxWordPerInterrupt = 256; + deviceExtension->MaximumBlockXfer[i] = 0; + } + } + } + } + + + // + // Set SCSI status to indicate a check condition. + // + + Srb->ScsiStatus = scsiStatus; + + return srbStatus; + +} // end MapError() + + +BOOLEAN +AtapiHwInitialize( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE - if initialization successful. + FALSE - if initialization unsuccessful. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress; + ULONG i; + UCHAR statusByte, errorByte; + + + for (i = 0; i < 4; i++) { + if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) { + + if (!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) { + + // + // Enable media status notification + // + + baseIoAddress = deviceExtension->BaseIoAddress1[i >> 1]; + + IdeMediaStatus(TRUE,HwDeviceExtension,i); + + // + // If supported, setup Multi-block transfers. + // + if (deviceExtension->MaximumBlockXfer[i]) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress->DriveSelect, + (UCHAR)(((i & 0x1) << 4) | 0xA0)); + + // + // Setup sector count to reflect the # of blocks. + // + + ScsiPortWritePortUchar(&baseIoAddress->BlockCount, + deviceExtension->MaximumBlockXfer[i]); + + // + // Issue the command. + // + + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_SET_MULTIPLE); + + // + // Wait for busy to drop. + // + + WaitOnBaseBusy(baseIoAddress,statusByte); + + // + // Check for errors. Reset the value to 0 (disable MultiBlock) if the + // command was aborted. + // + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress + 1); + + DebugPrint((1, + "AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n", + statusByte, + errorByte)); + // + // Adjust the devExt. value, if necessary. + // + + deviceExtension->MaximumBlockXfer[i] = 0; + + } else { + DebugPrint((2, + "AtapiHwInitialize: Using Multiblock on Device %d. Blocks / int - %d\n", + i, + deviceExtension->MaximumBlockXfer[i])); + } + } + } else if (!(deviceExtension->DeviceFlags[i] & DFLAGS_CHANGER_INITED)){ + + ULONG j; + BOOLEAN isSanyo = FALSE; + UCHAR vendorId[26]; + + // + // Attempt to identify any special-case devices - psuedo-atapi changers, atapi changers, etc. + // + + for (j = 0; j < 13; j += 2) { + + // + // Build a buffer based on the identify data. + // + + vendorId[j] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j + 1]; + vendorId[j+1] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j]; + } + + if (!AtapiStringCmp (vendorId, "CD-ROM CDR", 11)) { + + // + // Inquiry string for older model had a '-', newer is '_' + // + + if (vendorId[12] == 'C') { + + // + // Torisan changer. Set the bit. This will be used in several places + // acting like 1) a multi-lun device and 2) building the 'special' TUR's. + // + + deviceExtension->DeviceFlags[i] |= (DFLAGS_CHANGER_INITED | DFLAGS_SANYO_ATAPI_CHANGER); + deviceExtension->DiscsPresent[i] = 3; + isSanyo = TRUE; + } + } + } + + // + // We need to get our device ready for action before + // returning from this function + // + // According to the atapi spec 2.5 or 2.6, an atapi device + // clears its status BSY bit when it is ready for atapi commands. + // However, some devices (Panasonic SQ-TC500N) are still + // not ready even when the status BSY is clear. They don't react + // to atapi commands. + // + // Since there is really no other indication that tells us + // the drive is really ready for action. We are going to check BSY + // is clear and then just wait for an arbitrary amount of time! + // + if (deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) { + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[i >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[i >> 1]; + ULONG waitCount; + + // have to get out of the loop sometime! + // 10000 * 100us = 1000,000us = 1000ms = 1s + waitCount = 10000; + GetStatus(baseIoAddress1, statusByte); + while ((statusByte & IDE_STATUS_BUSY) && waitCount) { + // + // Wait for Busy to drop. + // + ScsiPortStallExecution(100); + GetStatus(baseIoAddress1, statusByte); + waitCount--; + } + + // 5000 * 100us = 500,000us = 500ms = 0.5s + waitCount = 5000; + do { + ScsiPortStallExecution(100); + } while (waitCount--); + } + } + } + + return TRUE; + +} // end AtapiHwInitialize() + + +VOID +AtapiHwInitializeChanger ( + IN PVOID HwDeviceExtension, + IN ULONG TargetId, + IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + + if (MechanismStatus) { + deviceExtension->DiscsPresent[TargetId] = MechanismStatus->NumberAvailableSlots; + if (deviceExtension->DiscsPresent[TargetId] > 1) { + deviceExtension->DeviceFlags[TargetId] |= DFLAGS_ATAPI_CHANGER; + } + } + return; +} + + + +BOOLEAN +FindDevices( + IN PVOID HwDeviceExtension, + IN BOOLEAN AtapiOnly, + IN ULONG Channel + ) + +/*++ + +Routine Description: + + This routine is called from AtapiFindController to identify + devices attached to an IDE controller. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + AtapiOnly - Indicates that routine should return TRUE only if + an ATAPI device is attached to the controller. + +Return Value: + + TRUE - True if devices found. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + BOOLEAN deviceResponded = FALSE, + skipSetParameters = FALSE; + ULONG waitCount = 10000; + ULONG deviceNumber; + ULONG i; + UCHAR signatureLow, + signatureHigh; + UCHAR statusByte; + + DebugPrintTickCount(); + + // + // Clear expecting interrupt flag and current SRB field. + // + + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->CurrentSrb = NULL; + + // We are about to talk to our devices before our interrupt handler is installed + // If our device uses sharable level sensitive interrupt, we may assert too + // many bogus interrupts + // Turn off device interrupt here + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) { + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((deviceNumber << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS); + } + + // + // Search for devices. + // + + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((deviceNumber << 4) | 0xA0)); + + // + // Check here for some SCSI adapters that incorporate IDE emulation. + // + + GetStatus(baseIoAddress1, statusByte); + if (statusByte == 0xFF) { + continue; + } + + DebugPrintTickCount(); + + AtapiSoftReset(baseIoAddress1,deviceNumber, TRUE); + + DebugPrintTickCount(); + + WaitOnBusy(baseIoAddress1,statusByte); + + DebugPrintTickCount(); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // ATAPI signature found. + // Issue the ATAPI identify command if this + // is not for the crash dump utility. + // + +atapiIssueId: + + if (!deviceExtension->DriverMustPoll) { + + // + // Issue ATAPI packet identify command. + // + + if (IssueIdentify(HwDeviceExtension, + deviceNumber, + Channel, + IDE_COMMAND_ATAPI_IDENTIFY, + TRUE)) { + + // + // Indicate ATAPI device. + // + + DebugPrint((1, + "FindDevices: Device %x is ATAPI\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_ATAPI_DEVICE; + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT; + + deviceResponded = TRUE; + + GetStatus(baseIoAddress1, statusByte); + if (statusByte & IDE_STATUS_ERROR) { + AtapiSoftReset(baseIoAddress1, deviceNumber, TRUE); + } + + + } else { + + // + // Indicate no working device. + // + + DebugPrint((1, + "FindDevices: Device %x not responding\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_DEVICE_PRESENT; + } + + } + + } else { + + // + // Issue IDE Identify. If an Atapi device is actually present, the signature + // will be asserted, and the drive will be recognized as such. + // + + if (IssueIdentify(HwDeviceExtension, + deviceNumber, + Channel, + IDE_COMMAND_IDENTIFY, + TRUE)) { + + // + // IDE drive found. + // + + + DebugPrint((1, + "FindDevices: Device %x is IDE\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT; + + if (!AtapiOnly) { + deviceResponded = TRUE; + } + + // + // Indicate IDE - not ATAPI device. + // + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_ATAPI_DEVICE; + + + } else { + + // + // Look to see if an Atapi device is present. + // + + AtapiSoftReset(baseIoAddress1, deviceNumber, TRUE); + + WaitOnBusy(baseIoAddress1,statusByte); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + goto atapiIssueId; + } + } + } + +#if DBG + if (deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] & DFLAGS_DEVICE_PRESENT) { + { + ULONG i; + UCHAR string[41]; + + for (i=0; i<8; i+=2) { + string[i] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].FirmwareRevision[i + 1]; + string[i + 1] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].FirmwareRevision[i]; + } + string[i] = 0; + DebugPrint((1, "FindDevices: firmware version: %s\n", string)); + + + for (i=0; i<40; i+=2) { + string[i] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].ModelNumber[i + 1]; + string[i + 1] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].ModelNumber[i]; + } + string[i] = 0; + DebugPrint((1, "FindDevices: model number: %s\n", string)); + } + } +#endif + } + + for (i = 0; i < 2; i++) { + if ((deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_DEVICE_PRESENT) && + (!(deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_ATAPI_DEVICE)) && deviceResponded) { + + // + // This hideous hack is to deal with ESDI devices that return + // garbage geometry in the IDENTIFY data. + // This is ONLY for the crashdump environment as + // these are ESDI devices. + // + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x35 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x07) { + + DebugPrint((1, + "FindDevices: Found nasty Compaq ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x34; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x0E; + } + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x35 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x0F) { + + DebugPrint((1, + "FindDevices: Found nasty Compaq ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x34; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x0F; + } + + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x36 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x07) { + + DebugPrint((1, + "FindDevices: Found nasty UltraStor ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x3F; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x10; + skipSetParameters = TRUE; + } + + + if (!skipSetParameters) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((i << 4) | 0xA0)); + + WaitOnBusy(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Reset the device. + // + + DebugPrint((2, + "FindDevices: Resetting controller before SetDriveParameters.\n")); + + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS); + ScsiPortStallExecution(500 * 1000); + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS); + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((i << 4) | 0xA0)); + + do { + + // + // Wait for Busy to drop. + // + + ScsiPortStallExecution(100); + GetStatus(baseIoAddress1, statusByte); + + } while ((statusByte & IDE_STATUS_BUSY) && waitCount--); + } + + WaitOnBusy(baseIoAddress1,statusByte); + DebugPrint((2, + "FindDevices: Status before SetDriveParameters: (%x) (%x)\n", + statusByte, + ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect))); + + // + // Use the IDENTIFY data to set drive parameters. + // + + if (!SetDriveParameters(HwDeviceExtension,i,Channel)) { + + DebugPrint((0, + "AtapHwInitialize: Set drive parameters for device %d failed\n", + i)); + + // + // Don't use this device as writes could cause corruption. + // + + deviceExtension->DeviceFlags[i + Channel] = 0; + continue; + + } + if (deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] & DFLAGS_REMOVABLE_DRIVE) { + + // + // Pick up ALL IDE removable drives that conform to Yosemite V0.2... + // + + AtapiOnly = FALSE; + } + + + // + // Indicate that a device was found. + // + + if (!AtapiOnly) { + deviceResponded = TRUE; + } + } + } + } + + // + // Make sure master device is selected on exit. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, 0xA0); + + // + // Reset the controller. This is a feeble attempt to leave the ESDI + // controllers in a state that ATDISK driver will recognize them. + // The problem in ATDISK has to do with timings as it is not reproducible + // in debug. The reset should restore the controller to its poweron state + // and give the system enough time to settle. + // + + if (!deviceResponded) { + + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS); + ScsiPortStallExecution(50 * 1000); + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_REENABLE_CONTROLLER); + } + + // Turn device interrupt back on + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) { + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((deviceNumber << 4) | 0xA0)); + // Clear any pending interrupts + GetStatus(baseIoAddress1, statusByte); + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER); + } + + DebugPrintTickCount(); + + return deviceResponded; + +} // end FindDevices() + + +ULONG +AtapiParseArgumentString( + IN PCHAR String, + IN PCHAR KeyWord + ) + +/*++ + +Routine Description: + + This routine will parse the string for a match on the keyword, then + calculate the value for the keyword and return it to the caller. + +Arguments: + + String - The ASCII string to parse. + KeyWord - The keyword for the value desired. + +Return Values: + + Zero if value not found + Value converted from ASCII to binary. + +--*/ + +{ + PCHAR cptr; + PCHAR kptr; + ULONG value; + ULONG stringLength = 0; + ULONG keyWordLength = 0; + ULONG index; + + if (!String) { + return 0; + } + if (!KeyWord) { + return 0; + } + + // + // Calculate the string length and lower case all characters. + // + + cptr = String; + while (*cptr) { + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + stringLength++; + } + + // + // Calculate the keyword length and lower case all characters. + // + + cptr = KeyWord; + while (*cptr) { + + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + keyWordLength++; + } + + if (keyWordLength > stringLength) { + + // + // Can't possibly have a match. + // + + return 0; + } + + // + // Now setup and start the compare. + // + + cptr = String; + +ContinueSearch: + + // + // The input string may start with white space. Skip it. + // + + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + if (*cptr == '\0') { + + // + // end of string. + // + + return 0; + } + + kptr = KeyWord; + while (*cptr++ == *kptr++) { + + if (*(cptr - 1) == '\0') { + + // + // end of string + // + + return 0; + } + } + + if (*(kptr - 1) == '\0') { + + // + // May have a match backup and check for blank or equals. + // + + cptr--; + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + // + // Found a match. Make sure there is an equals. + // + + if (*cptr != '=') { + + // + // Not a match so move to the next semicolon. + // + + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + return 0; + } + + // + // Skip the equals sign. + // + + cptr++; + + // + // Skip white space. + // + + while ((*cptr == ' ') || (*cptr == '\t')) { + cptr++; + } + + if (*cptr == '\0') { + + // + // Early end of string, return not found + // + + return 0; + } + + if (*cptr == ';') { + + // + // This isn't it either. + // + + cptr++; + goto ContinueSearch; + } + + value = 0; + if ((*cptr == '0') && (*(cptr + 1) == 'x')) { + + // + // Value is in Hex. Skip the "0x" + // + + cptr += 2; + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (16 * value) + (*(cptr + index) - '0'); + } else { + if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) { + value = (16 * value) + (*(cptr + index) - 'a' + 10); + } else { + + // + // Syntax error, return not found. + // + return 0; + } + } + } + } else { + + // + // Value is in Decimal. + // + + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (10 * value) + (*(cptr + index) - '0'); + } else { + + // + // Syntax error return not found. + // + return 0; + } + } + } + + return value; + } else { + + // + // Not a match check for ';' to continue search. + // + + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + + return 0; + } +} + + + +BOOLEAN +AtapiAllocateIoBase ( + IN PVOID HwDeviceExtension, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PFIND_STATE FindState, + OUT PIDE_REGISTERS_1 CmdLogicalBasePort[2], + OUT PIDE_REGISTERS_2 CtrlLogicalBasePort[2], + OUT PIDE_BUS_MASTER_REGISTERS BmLogicalBasePort[2], + OUT ULONG *NumIdeChannel, + OUT PBOOLEAN PreConfig +) +/*++ + +Routine Description: + + Return ide controller io addresses for device detection + + This function populate these PORT_CONFIGURATION_INFORMATION + entries + + 1st ACCESS_RANGE - first channel command block register base + 2nd ACCESS_RANGE - first channel control block register base + 3rd ACCESS_RANGE - second channel command block register base + 4th ACCESS_RANGE - second channel control block register base + 5th ACCESS_RANGE - first channel bus master register base + 6th ACCESS_RANGE - second channel bus master register base + + InterruptMode - first channel interrupt mode + BusInterruptLevel - first channel interrupt level + InterruptMode2 - second channel interrupt mode + BusInterruptLevel2 - second channel interrupt level + AdapterInterfaceType + AtdiskPrimaryClaimed + AtdiskSecondaryClaimed + + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + ArgumentString - registry user arugment + ConfigInfo = Scsi Port Config. Structure + FindState - Keep track of what addresses has been returned + CmdLogicalBasePort - command block register logical base address + CtrlLogicalBasePort - control block register logical base address + BmLogicalBasePort - bus master register logical base address + NumIdeChannel - number of IDE channel base address returned + PreConfig - the reutrned address is user configured + +Return Value: + + TRUE - io address is returned. + FASLE - no more io address to return. + + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + + ULONG cmdBasePort[2] = {0, 0}; + ULONG ctrlBasePort[2] = {0, 0}; + ULONG bmBasePort[2] = {0, 0}; + BOOLEAN getDeviceBaseFailed; + + + CmdLogicalBasePort[0] = 0; + CmdLogicalBasePort[1] = 0; + CtrlLogicalBasePort[0] = 0; + CtrlLogicalBasePort[1] = 0; + BmLogicalBasePort[0] = 0; + BmLogicalBasePort[1] = 0; + *PreConfig = FALSE; + + // + // check for pre-config controller (pcmcia) + // + cmdBasePort[0] = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart); + ctrlBasePort[0] = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[1].RangeStart); + if (cmdBasePort[0] != 0) { + *PreConfig = TRUE; + *NumIdeChannel = 1; + + if (!ctrlBasePort[0]) { + // + // The pre-config init is really made for pcmcia ata disk + // When we get pre-config data, both io access ranges are lumped together + // the command registers are mapped to the first 7 address locations, and + // the control registers are mapped to the 0xE th location + // + ctrlBasePort[0] = cmdBasePort[0] + 0xe; + } + + DebugPrint ((2, "AtapiAllocateIoBase: found pre-config pcmcia controller\n")); + } + + // + // check for user defined controller (made for IBM Caroline ppc) + // + if ((cmdBasePort[0] == 0) && ArgumentString) { + + ULONG irq; + + irq = AtapiParseArgumentString(ArgumentString, "Interrupt"); + cmdBasePort[0] = AtapiParseArgumentString(ArgumentString, "BaseAddress"); + if (irq && cmdBasePort[0]) { + + *NumIdeChannel = 1; + + // the control register offset is implied!! + ctrlBasePort[0] = cmdBasePort[0] + 0x206; + + ConfigInfo->InterruptMode = Latched; + ConfigInfo->BusInterruptLevel = irq; + + DebugPrint ((2, "AtapiAllocateIoBase: found user config controller\n")); + } + } + + // + // PCI controller + // + if (cmdBasePort[0] == 0 && + (ConfigInfo->AdapterInterfaceType == Isa)) { + + PCI_SLOT_NUMBER pciSlot; + PUCHAR vendorStrPtr; + PUCHAR deviceStrPtr; + UCHAR vendorString[5]; + UCHAR deviceString[5]; + + ULONG pciBusNumber; + ULONG slotNumber; + ULONG logicalDeviceNumber; + ULONG ideChannel; + PCI_COMMON_CONFIG pciData; + ULONG cIndex; + UCHAR bmStatus; + BOOLEAN foundController; + + pciBusNumber = FindState->BusNumber; + slotNumber = FindState->SlotNumber; + logicalDeviceNumber = FindState->LogicalDeviceNumber; + ideChannel = FindState->IdeChannel; + foundController = FALSE; + *NumIdeChannel = 1; + + for (;pciBusNumber < 256 && !(cmdBasePort[0]); pciBusNumber++, slotNumber=logicalDeviceNumber=0) { + pciSlot.u.AsULONG = 0; + + for (;slotNumber < PCI_MAX_DEVICES && !(cmdBasePort[0]); slotNumber++, logicalDeviceNumber=0) { + pciSlot.u.bits.DeviceNumber = slotNumber; + + for (;logicalDeviceNumber < PCI_MAX_FUNCTION && !(cmdBasePort[0]); logicalDeviceNumber++, ideChannel=0) { + + pciSlot.u.bits.FunctionNumber = logicalDeviceNumber; + + for (;ideChannel < MAX_CHANNEL && !(cmdBasePort[0]); ideChannel++) { + + if (!GetPciBusData(HwDeviceExtension, + pciBusNumber, + pciSlot, + &pciData, + offsetof (PCI_COMMON_CONFIG, DeviceSpecific))) { + break; + } + + if (pciData.VendorID == PCI_INVALID_VENDORID) { + break; + } + + // + // Translate hex ids to strings. + // + vendorStrPtr = vendorString; + deviceStrPtr = deviceString; + AtapiHexToString(pciData.VendorID, &vendorStrPtr); + AtapiHexToString(pciData.DeviceID, &deviceStrPtr); + + DebugPrint((2, + "AtapiAllocateIoBase: Bus %x Slot %x Function %x Vendor %s Product %s\n", + pciBusNumber, + slotNumber, + logicalDeviceNumber, + vendorString, + deviceString)); + + // + // Search for controller we know about + // + ConfigInfo->AdapterInterfaceType = Isa; + foundController = FALSE; + *NumIdeChannel = 1; + for (cIndex = 0; cIndex < NUMBER_OF_PCI_CONTROLLER; cIndex++) { + + if ((!AtapiStringCmp(vendorString, + FindState->ControllerParameters[cIndex].VendorId, + FindState->ControllerParameters[cIndex].VendorIdLength) && + !AtapiStringCmp(deviceString, + FindState->ControllerParameters[cIndex].DeviceId, + FindState->ControllerParameters[cIndex].DeviceIdLength))) { + + foundController = TRUE; + deviceExtension->BMTimingControl = FindState->ControllerParameters[cIndex].TimingControl; + deviceExtension->IsChannelEnabled = FindState->ControllerParameters[cIndex].IsChannelEnabled; + + if (FindState->ControllerParameters[cIndex].SingleFIFO) { + DebugPrint ((0, "AtapiAllocateIoBase: hardcoded single FIFO pci controller\n")); + *NumIdeChannel = 2; + } + break; + } + } + + // + // Look for generic IDE controller + // + if (cIndex >= NUMBER_OF_PCI_CONTROLLER) { + if (pciData.BaseClass == 0x1) { // Mass Storage Device + if (pciData.SubClass == 0x1) { // IDE Controller + + DebugPrint ((0, "AtapiAllocateIoBase: found an unknown pci ide controller\n")); + deviceExtension->BMTimingControl = NULL; + deviceExtension->IsChannelEnabled = ChannelIsAlwaysEnabled; + foundController = TRUE; + } + } + } + + if (foundController) { + + DebugPrint ((2, "AtapiAllocateIoBase: found pci ide controller 0x%4x 0x%4x\n", pciData.VendorID, pciData.DeviceID)); + + GetPciBusData(HwDeviceExtension, + pciBusNumber, + pciSlot, + &pciData, + sizeof (PCI_COMMON_CONFIG)); + + // + // Record pci device location + // + deviceExtension->PciBusNumber = pciBusNumber; + deviceExtension->PciDeviceNumber = slotNumber; + deviceExtension->PciLogDevNumber = logicalDeviceNumber; + +#if defined (DBG) + { + ULONG i, j; + + DebugPrint ((2, "AtapiAllocateIoBase: PCI Configuration Data\n")); + for (i=0; i<sizeof(PCI_COMMON_CONFIG); i+=16) { + DebugPrint ((2, "AtapiAllocateIoBase: ")); + for (j=0; j<16; j++) { + if ((i + j) < sizeof(PCI_COMMON_CONFIG)) { + DebugPrint ((2, "%02x ", ((PUCHAR)&pciData)[i + j])); + } else { + break; + } + } + DebugPrint ((2, "\n")); + } + } +#endif //DBG + + if (!AtapiPlaySafe) { + // + // Try to turn on the bus master bit in PCI space if it is not already on + // + if ((pciData.Command & PCI_ENABLE_BUS_MASTER) == 0) { + + DebugPrint ((0, "ATAPI: Turning on PCI Bus Master bit\n")); + + pciData.Command |= PCI_ENABLE_BUS_MASTER; + + SetPciBusData (HwDeviceExtension, + pciBusNumber, + pciSlot, + &pciData.Command, + offsetof (PCI_COMMON_CONFIG, Command), + sizeof(pciData.Command)); + + GetPciBusData(HwDeviceExtension, + pciBusNumber, + pciSlot, + &pciData, + offsetof (PCI_COMMON_CONFIG, DeviceSpecific) + ); + + if (pciData.Command & PCI_ENABLE_BUS_MASTER) { + DebugPrint ((0, "ATAPI: If we play safe, we would NOT detect this IDE controller is busmaster capable\n")); + } + } + } + + // + // Check to see if the controller is bus master capable + // + bmStatus = 0; + if ((pciData.Command & PCI_ENABLE_BUS_MASTER) && + (pciData.ProgIf & 0x80) && + deviceExtension->UseBusMasterController) { + + PIDE_BUS_MASTER_REGISTERS bmLogicalBasePort; + + bmBasePort[0] = pciData.u.type0.BaseAddresses[4] & 0xfffffffc; + if ((bmBasePort[0] != 0) && (bmBasePort[0] != 0xffffffff)) { + + bmLogicalBasePort = (PIDE_BUS_MASTER_REGISTERS) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(bmBasePort[0]), + 8, + TRUE); + + if (bmLogicalBasePort) { + + // Some controller (ALi M5219) doesn't implement the readonly simplex bit + // We will try to clear it. If it works, we will assume simplex bit + // is not set + bmStatus = ScsiPortReadPortUchar(&bmLogicalBasePort->Status); + ScsiPortWritePortUchar(&bmLogicalBasePort->Status, (UCHAR) (bmStatus & ~BUSMASTER_DMA_SIMPLEX_BIT)); + + + bmStatus = ScsiPortReadPortUchar(&bmLogicalBasePort->Status); + ScsiPortFreeDeviceBase(HwDeviceExtension, bmLogicalBasePort); + + DebugPrint ((2, "AtapiAllocateIoBase: controller is capable of bus mastering\n")); + } else { + bmBasePort[0] = 0; + DebugPrint ((2, "AtapiAllocateIoBase: controller is NOT capable of bus mastering\n")); + } + } else { + bmBasePort[0] = 0; + DebugPrint ((2, "AtapiAllocateIoBase: controller is NOT capable of bus mastering\n")); + } + + } else { + bmBasePort[0] = 0; + DebugPrint ((2, "AtapiAllocateIoBase: controller is NOT capable of bus mastering\n")); + } + + if (bmStatus & BUSMASTER_DMA_SIMPLEX_BIT) { + DebugPrint ((0, "AtapiAllocateIoBase: simplex bit is set. single FIFO pci controller\n")); + *NumIdeChannel = 2; + } + + if (*NumIdeChannel == 2) { + if (!((*deviceExtension->IsChannelEnabled) (&pciData, + 0) && + (*deviceExtension->IsChannelEnabled) (&pciData, + 0))) { + // + // if we have a single FIFO controller, but one of the channels + // is not turned on. We don't need to sync. access the both channels + // We can pretend we have a single channel controller + // + *NumIdeChannel = 1; + } + } + + // + // figure out what io address the controller is using + // If it is in native mode, get the address out of the PCI + // config space. If it is in legacy mode, it will be hard + // wired to use standard primary (0x1f0) or secondary + // (0x170) channel addresses + // + if (ideChannel == 0) { + + if ((*deviceExtension->IsChannelEnabled) (&pciData, + ideChannel)) { + + // + // check to see if the controller has a single FIFO for both + // IDE channel + // + if (bmStatus & BUSMASTER_DMA_SIMPLEX_BIT) { + DebugPrint ((0, "AtapiAllocateIoBase: simplex bit is set. single FIFO pci controller\n")); + *NumIdeChannel = 2; + } + + if ((pciData.ProgIf & 0x3) == 0x3) { + // Native Mode + cmdBasePort[0] = pciData.u.type0.BaseAddresses[0] & 0xfffffffc; + ctrlBasePort[0] = (pciData.u.type0.BaseAddresses[1] & 0xfffffffc) + 2; + + ConfigInfo->InterruptMode = LevelSensitive; + ConfigInfo->BusInterruptVector = + ConfigInfo->BusInterruptLevel = pciData.u.type0.InterruptLine; + ConfigInfo->AdapterInterfaceType = PCIBus; + + } else { + // Legacy Mode + cmdBasePort[0] = 0x1f0; + ctrlBasePort[0] = 0x1f0 + 0x206; + + ConfigInfo->InterruptMode = Latched; + ConfigInfo->BusInterruptLevel = 14; + } + } + if (*NumIdeChannel == 2) { + + // grab both channels + ideChannel++; + + if ((*deviceExtension->IsChannelEnabled) (&pciData, + ideChannel)) { + + if (bmBasePort[0]) { + bmBasePort[1] = bmBasePort[0] + 8; + } + + if ((pciData.ProgIf & 0xc) == 0xc) { + // Native Mode + cmdBasePort[1] = pciData.u.type0.BaseAddresses[2] & 0xfffffffc; + ctrlBasePort[1] = (pciData.u.type0.BaseAddresses[3] & 0xfffffffc) + 2; + } else { + // Legacy Mode + cmdBasePort[1] = 0x170; + ctrlBasePort[1] = 0x170 + 0x206; + + ConfigInfo->InterruptMode2 = Latched; + ConfigInfo->BusInterruptLevel2 = 15; + } + } + } + } else if (ideChannel == 1) { + + if ((*deviceExtension->IsChannelEnabled) (&pciData, + ideChannel)) { + + if (bmBasePort[0]) { + bmBasePort[0] += 8; + } + + if ((pciData.ProgIf & 0xc) == 0xc) { + // Native Mode + cmdBasePort[0] = pciData.u.type0.BaseAddresses[2] & 0xfffffffc; + ctrlBasePort[0] = (pciData.u.type0.BaseAddresses[3] & 0xfffffffc) + 2; + + ConfigInfo->InterruptMode = LevelSensitive; + ConfigInfo->BusInterruptVector = + ConfigInfo->BusInterruptLevel = pciData.u.type0.InterruptLine; + ConfigInfo->AdapterInterfaceType = PCIBus; + + } else { + // Legacy Mode + cmdBasePort[0] = 0x170; + ctrlBasePort[0] = 0x170 + 0x206; + + ConfigInfo->InterruptMode = Latched; + ConfigInfo->BusInterruptLevel = 15; + } + } + } + } else { + ideChannel = MAX_CHANNEL; + } + if (cmdBasePort[0]) + break; + } + if (cmdBasePort[0]) + break; + } + if (cmdBasePort[0]) + break; + } + if (cmdBasePort[0]) + break; + } + FindState->BusNumber = pciBusNumber; + FindState->SlotNumber = slotNumber; + FindState->LogicalDeviceNumber = logicalDeviceNumber; + FindState->IdeChannel = ideChannel + 1; + } + + // + // look for legacy controller + // + if (cmdBasePort[0] == 0) { + ULONG i; + + for (i = 0; FindState->DefaultIoPort[i]; i++) { + if (FindState->IoAddressUsed[i] != TRUE) { + + *NumIdeChannel = 1; + cmdBasePort[0] = FindState->DefaultIoPort[i]; + ctrlBasePort[0] = FindState->DefaultIoPort[i] + 0x206; + bmBasePort[0] = 0; + + ConfigInfo->InterruptMode = Latched; + ConfigInfo->BusInterruptLevel = FindState->DefaultInterrupt[i]; + break; + } + } + } + + if (cmdBasePort[0]) { + ULONG i; + + // Mark io addresses used + for (i = 0; FindState->DefaultIoPort[i]; i++) { + if (FindState->DefaultIoPort[i] == cmdBasePort[0]) { + FindState->IoAddressUsed[i] = TRUE; + } + if (FindState->DefaultIoPort[i] == cmdBasePort[1]) { + FindState->IoAddressUsed[i] = TRUE; + } + } + + + if (cmdBasePort[0] == 0x1f0) { + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + deviceExtension->PrimaryAddress = TRUE; + } else if (cmdBasePort[0] == 0x170) { + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + deviceExtension->PrimaryAddress = FALSE; + } + + if (cmdBasePort[1] == 0x1f0) { + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + } else if (cmdBasePort[1] == 0x170) { + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + } + + if (*PreConfig == FALSE) { + + (*ConfigInfo->AccessRanges)[0].RangeStart = ScsiPortConvertUlongToPhysicalAddress(cmdBasePort[0]); + (*ConfigInfo->AccessRanges)[0].RangeLength = 8; + (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + + (*ConfigInfo->AccessRanges)[1].RangeStart = ScsiPortConvertUlongToPhysicalAddress(ctrlBasePort[0]); + (*ConfigInfo->AccessRanges)[1].RangeLength = 1; + (*ConfigInfo->AccessRanges)[1].RangeInMemory = FALSE; + + if (cmdBasePort[1]) { + (*ConfigInfo->AccessRanges)[2].RangeStart = ScsiPortConvertUlongToPhysicalAddress(cmdBasePort[1]); + (*ConfigInfo->AccessRanges)[2].RangeLength = 8; + (*ConfigInfo->AccessRanges)[2].RangeInMemory = FALSE; + + (*ConfigInfo->AccessRanges)[3].RangeStart = ScsiPortConvertUlongToPhysicalAddress(ctrlBasePort[1]); + (*ConfigInfo->AccessRanges)[3].RangeLength = 1; + (*ConfigInfo->AccessRanges)[3].RangeInMemory = FALSE; + } + + if (bmBasePort[0]) { + (*ConfigInfo->AccessRanges)[4].RangeStart = ScsiPortConvertUlongToPhysicalAddress(bmBasePort[0]); + (*ConfigInfo->AccessRanges)[4].RangeLength = 8; + (*ConfigInfo->AccessRanges)[4].RangeInMemory = FALSE; + } + + if (bmBasePort[1]) { + (*ConfigInfo->AccessRanges)[5].RangeStart = ScsiPortConvertUlongToPhysicalAddress(bmBasePort[1]); + (*ConfigInfo->AccessRanges)[5].RangeLength = 8; + (*ConfigInfo->AccessRanges)[5].RangeInMemory = FALSE; + } + } + + + // + // map all raw io addresses to logical io addresses + // + getDeviceBaseFailed = FALSE; + CmdLogicalBasePort[0] = (PIDE_REGISTERS_1) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[0].RangeStart, + (*ConfigInfo->AccessRanges)[0].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[0].RangeInMemory)); + if (!CmdLogicalBasePort[0]) { + getDeviceBaseFailed = TRUE; + } + + if (*PreConfig) { + CtrlLogicalBasePort[0] = (PIDE_REGISTERS_2) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(ctrlBasePort[0]), + 1, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[0].RangeInMemory)); + + } else { + CtrlLogicalBasePort[0] = (PIDE_REGISTERS_2) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[1].RangeStart, + (*ConfigInfo->AccessRanges)[1].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[1].RangeInMemory)); + } + if (!CtrlLogicalBasePort[0]) { + getDeviceBaseFailed = TRUE; + } + + if (cmdBasePort[1]) { + CmdLogicalBasePort[1] = (PIDE_REGISTERS_1) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[2].RangeStart, + (*ConfigInfo->AccessRanges)[2].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[2].RangeInMemory)); + if (!CmdLogicalBasePort[0]) { + getDeviceBaseFailed = TRUE; + } + + CtrlLogicalBasePort[1] = (PIDE_REGISTERS_2) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[3].RangeStart, + (*ConfigInfo->AccessRanges)[3].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[3].RangeInMemory)); + if (!CtrlLogicalBasePort[0]) { + getDeviceBaseFailed = TRUE; + } + } + + if (bmBasePort[0]) { + BmLogicalBasePort[0] = (PIDE_BUS_MASTER_REGISTERS) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[4].RangeStart, + (*ConfigInfo->AccessRanges)[4].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[4].RangeInMemory)); + if (!BmLogicalBasePort[0]) { + getDeviceBaseFailed = TRUE; + } + } + if (bmBasePort[1]) { + BmLogicalBasePort[1] = (PIDE_BUS_MASTER_REGISTERS) ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[5].RangeStart, + (*ConfigInfo->AccessRanges)[5].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[5].RangeInMemory)); + if (!BmLogicalBasePort[1]) { + getDeviceBaseFailed = TRUE; + } + } + + if (!getDeviceBaseFailed) + return TRUE; + } + + return FALSE; +} + + +BOOLEAN +AtapiFreeIoBase ( + IN PVOID HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PFIND_STATE FindState, + OUT PIDE_REGISTERS_1 CmdLogicalBasePort[2], + OUT PIDE_REGISTERS_2 CtrlLogicalBasePort[2], + OUT PIDE_BUS_MASTER_REGISTERS BmLogicalBasePort[2] +) +/*++ + +Routine Description: + + free logical io addresses. + + This function is called when no device found on the io addresses + + This function clears these PORT_CONFIGURATION_INFORMATION entries + + AccessRanges + BusInterruptLevel + BusInterruptLevel2 + AtdiskPrimaryClaimed + AtdiskSecondaryClaimed + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + ConfigInfo = Scsi Port Config. Structure + FindState - Keep track of what addresses has been returned + CmdLogicalBasePort - command block register logical base address + CtrlLogicalBasePort - control block register logical base address + BmLogicalBasePort - bus master register logical base address + +Return Value: + + TRUE - always + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + + if (CmdLogicalBasePort[0]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + CmdLogicalBasePort[0]); + (*ConfigInfo->AccessRanges)[0].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + CmdLogicalBasePort[0] = 0; + } + if (CmdLogicalBasePort[1]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + CmdLogicalBasePort[1]); + (*ConfigInfo->AccessRanges)[1].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + CmdLogicalBasePort[1] = 0; + } + if (CtrlLogicalBasePort[0]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + CtrlLogicalBasePort[0]); + (*ConfigInfo->AccessRanges)[2].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + CtrlLogicalBasePort[0] = 0; + } + if (CtrlLogicalBasePort[1]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + CtrlLogicalBasePort[1]); + (*ConfigInfo->AccessRanges)[3].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + CtrlLogicalBasePort[1] = 0; + } + if (BmLogicalBasePort[0]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + BmLogicalBasePort[0]); + (*ConfigInfo->AccessRanges)[4].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + BmLogicalBasePort[0] = 0; + } + if (BmLogicalBasePort[1]) { + ScsiPortFreeDeviceBase(HwDeviceExtension, + BmLogicalBasePort[1]); + (*ConfigInfo->AccessRanges)[5].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0); + BmLogicalBasePort[1] = 0; + } + + ConfigInfo->AtdiskPrimaryClaimed = FALSE; + ConfigInfo->AtdiskSecondaryClaimed = FALSE; + ConfigInfo->BusInterruptLevel = 0; + ConfigInfo->BusInterruptLevel2 = 0; + deviceExtension->PrimaryAddress = FALSE; + deviceExtension->BMTimingControl = NULL; + + return TRUE; +} + + + +BOOLEAN +AtapiAllocatePRDT( + IN OUT PVOID HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo + ) +/*++ + +Routine Description: + + allocate scatter/gather list for PCI IDE controller call + Physical Region Descriptor Table (PRDT) + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + ConfigInfo = Scsi Port Config. Structure + +Return Value: + + TRUE - if successful + FASLE - if failed + + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG bytesMapped; + ULONG totalBytesMapped; + PUCHAR buffer; + PHYSICAL_ADDRESS physAddr; + ULONG uncachedExtensionSize; + INTERFACE_TYPE oldAdapterInterfaceType; + + ConfigInfo->ScatterGather = TRUE; + ConfigInfo->Master = TRUE; + ConfigInfo->Dma32BitAddresses = TRUE; + ConfigInfo->DmaWidth = Width16Bits; + + // + // word align + // + ConfigInfo->AlignmentMask = 1; + + // + // PRDT cannot cross a page boundary, so number of physical breaks + // are limited by page size + // + ConfigInfo->NumberOfPhysicalBreaks = PAGE_SIZE / sizeof(PHYSICAL_REGION_DESCRIPTOR); + + // + // MAX_TRANSFER_SIZE_PER_SRB can spread over + // (MAX_TRANSFER_SIZE_PER_SRB / PAGE_SIZE) + 2 pages + // Each page requires 8 bytes in the bus master descriptor table + // To guarantee we will have a buffer that is big enough and does not + // cross a page boundary, we will allocate twice of what we need. + // Half of that will always be big enough and will not cross + // any page boundary + // + uncachedExtensionSize = (MAX_TRANSFER_SIZE_PER_SRB / PAGE_SIZE) + 2; + uncachedExtensionSize *= sizeof (PHYSICAL_REGION_DESCRIPTOR); + uncachedExtensionSize *= 2; + + // + // ScsiPortGetUncachedExtension() will allocate Adapter object, + // change the AdapterInterfaceType to PCI temporarily so that + // we don't inherit ISA DMA limitation. + // We can't keep the AdapterInterfaceType to PCI because the + // irq resources we are asking for are ISA resources + // + oldAdapterInterfaceType = ConfigInfo->AdapterInterfaceType; + ConfigInfo->AdapterInterfaceType = PCIBus; + + buffer = ScsiPortGetUncachedExtension(HwDeviceExtension, + ConfigInfo, + uncachedExtensionSize); + + ConfigInfo->AdapterInterfaceType = oldAdapterInterfaceType; + + if (buffer) { + + deviceExtension->DataBufferDescriptionTableSize = 0; + totalBytesMapped = 0; + while (totalBytesMapped < uncachedExtensionSize) { + physAddr = ScsiPortGetPhysicalAddress(HwDeviceExtension, + NULL, + buffer, + &bytesMapped); + if (bytesMapped == 0) { + break; + } + + // + // Find the biggest chuck of physically contiguous memory + // + totalBytesMapped += bytesMapped; + while (bytesMapped) { + ULONG chunkSize; + + chunkSize = PAGE_SIZE - (ScsiPortConvertPhysicalAddressToUlong(physAddr) & (PAGE_SIZE-1)); + if (chunkSize > bytesMapped) + chunkSize = bytesMapped; + + if (chunkSize > deviceExtension->DataBufferDescriptionTableSize) { + deviceExtension->DataBufferDescriptionTableSize = chunkSize; + deviceExtension->DataBufferDescriptionTablePtr = (PPHYSICAL_REGION_DESCRIPTOR) buffer; + deviceExtension->DataBufferDescriptionTablePhysAddr = physAddr; + } + buffer += chunkSize; + physAddr = ScsiPortConvertUlongToPhysicalAddress + (ScsiPortConvertPhysicalAddressToUlong(physAddr) + chunkSize); + bytesMapped -= chunkSize; + } + } + // Did we get at least the minimal amount (half of what we ask for)? + if (deviceExtension->DataBufferDescriptionTableSize < (uncachedExtensionSize / 2)) { + buffer = NULL; + } + } + + if (buffer) { + return TRUE; + } else { + DebugPrint ((0, "atapi: unable to get buffer for physical descriptor table!\n")); + ConfigInfo->ScatterGather = FALSE; + ConfigInfo->Master = FALSE; + ConfigInfo->Dma32BitAddresses = FALSE; + return FALSE; + } +} + + +ULONG +AtapiFindController( + IN PVOID HwDeviceExtension, + IN PFIND_STATE FindState, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) +/*++ + +Routine Description: + + This function is called by the OS-specific port driver after + the necessary storage has been allocated, to gather information + about the adapter's configuration. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Context - Address of findstate + ArgumentString - Used to determine whether driver is client of ntldr or crash dump utility. + ConfigInfo - Configuration information structure describing HBA + Again - Indicates search for adapters to continue + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG i,j; + ULONG retryCount; + PCI_SLOT_NUMBER slotData; + PPCI_COMMON_CONFIG pciData; + ULONG pciBuffer; + BOOLEAN atapiOnly; + UCHAR statusByte; + ULONG ideChannel; + BOOLEAN foundDevice = FALSE; + + PIDE_REGISTERS_1 cmdLogicalBasePort[2]; + PIDE_REGISTERS_2 ctrlLogicalBasePort[2]; + PIDE_BUS_MASTER_REGISTERS bmLogicalBasePort[2]; + ULONG numIdeChannel; + BOOLEAN preConfig = FALSE; + PCHAR userArgumentString; + + + if (!deviceExtension) { + return SP_RETURN_ERROR; + } + + // + // set the dma detection level + // + SetBusMasterDetectionLevel (HwDeviceExtension, ArgumentString); + + *Again = TRUE; + userArgumentString = ArgumentString; + while (AtapiAllocateIoBase (HwDeviceExtension, + userArgumentString, + ConfigInfo, + FindState, + cmdLogicalBasePort, + ctrlLogicalBasePort, + bmLogicalBasePort, + &numIdeChannel, + &preConfig)) { + + // only use user argument string once + userArgumentString = NULL; + + ConfigInfo->NumberOfBuses = 1; + ConfigInfo->MaximumNumberOfTargets = (UCHAR) (2 * numIdeChannel); + deviceExtension->NumberChannels = numIdeChannel; + + for (i = 0; i < 4; i++) { + deviceExtension->DeviceFlags[i] &= ~(DFLAGS_ATAPI_DEVICE | DFLAGS_DEVICE_PRESENT | DFLAGS_TAPE_DEVICE); + } + + for (ideChannel = 0; ideChannel < numIdeChannel; ideChannel++) { + + retryCount = 4; + + retryIdentifier: + + // + // Select master. + // + + ScsiPortWritePortUchar(&cmdLogicalBasePort[ideChannel]->DriveSelect, 0xA0); + + // + // Check if card at this address. + // + + ScsiPortWritePortUchar(&cmdLogicalBasePort[ideChannel]->CylinderLow, 0xAA); + + // + // Check if indentifier can be read back. + // + + if ((statusByte = ScsiPortReadPortUchar(&cmdLogicalBasePort[ideChannel]->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindController: Identifier read back from Master (%x)\n", + statusByte)); + + statusByte = ScsiPortReadPortUchar(&cmdLogicalBasePort[ideChannel]->Command); + + if (statusByte & IDE_STATUS_BUSY) { + + i = 0; + + // + // Could be the TEAC in a thinkpad. Their dos driver puts it in a sleep-mode that + // warm boots don't clear. + // + + do { + ScsiPortStallExecution(1000); + statusByte = ScsiPortReadPortUchar(&cmdLogicalBasePort[ideChannel]->Command); + DebugPrint((3, + "AtapiFindController: First access to status %x\n", + statusByte)); + } while ((statusByte & IDE_STATUS_BUSY) && ++i < 10); + + if (retryCount-- && (!(statusByte & IDE_STATUS_BUSY))) { + goto retryIdentifier; + } + } + + // + // Select slave. + // + + ScsiPortWritePortUchar(&cmdLogicalBasePort[ideChannel]->DriveSelect, 0xB0); + + // + // See if slave is present. + // + + ScsiPortWritePortUchar(&cmdLogicalBasePort[ideChannel]->CylinderLow, 0xAA); + + if ((statusByte = ScsiPortReadPortUchar(&cmdLogicalBasePort[ideChannel]->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindController: Identifier read back from Slave (%x)\n", + statusByte)); + + // + // + // No controller at this base address. + // + continue; + } + } + + // + // Record base IO address. + // + + deviceExtension->BaseIoAddress1[ideChannel] = cmdLogicalBasePort[ideChannel]; + deviceExtension->BaseIoAddress2[ideChannel] = ctrlLogicalBasePort[ideChannel]; + deviceExtension->BusMasterPortBase[ideChannel] = bmLogicalBasePort[ideChannel]; + if (bmLogicalBasePort[ideChannel]) { + deviceExtension->ControllerFlags |= CFLAGS_BUS_MASTERING; + } else { + deviceExtension->ControllerFlags &= ~CFLAGS_BUS_MASTERING; + } + + DebugPrint ((2, "atapi: command register logical base port: 0x%x\n", deviceExtension->BaseIoAddress1[ideChannel])); + DebugPrint ((2, "atapi: control register logical base port: 0x%x\n", deviceExtension->BaseIoAddress2[ideChannel])); + DebugPrint ((2, "atapi: busmaster register logical base port: 0x%x\n", deviceExtension->BusMasterPortBase[ideChannel])); + + // + // Indicate maximum transfer length is 64k. + // + ConfigInfo->MaximumTransferLength = MAX_TRANSFER_SIZE_PER_SRB; + + DebugPrint((1, + "AtapiFindController: Found IDE at %x\n", + deviceExtension->BaseIoAddress1[ideChannel])); + + + // + // For Daytona, the atdisk driver gets the first shot at the + // primary and secondary controllers. + // + + if (preConfig == FALSE) { + + if (ConfigInfo->AtdiskPrimaryClaimed || ConfigInfo->AtdiskSecondaryClaimed) { + + // + // Determine whether this driver is being initialized by the + // system or as a crash dump driver. + // + + if (ArgumentString) { + + if (AtapiParseArgumentString(ArgumentString, "dump") == 1) { + DebugPrint((3, + "AtapiFindController: Crash dump\n")); + atapiOnly = FALSE; + deviceExtension->DriverMustPoll = TRUE; + } else { + DebugPrint((3, + "AtapiFindController: Atapi Only\n")); + atapiOnly = TRUE; + deviceExtension->DriverMustPoll = FALSE; + } + } else { + + DebugPrint((3, + "AtapiFindController: Atapi Only\n")); + atapiOnly = TRUE; + deviceExtension->DriverMustPoll = FALSE; + } + + } else { + atapiOnly = FALSE; + } + + // + // If this is a PCI machine, pick up all devices. + // + + + pciData = (PPCI_COMMON_CONFIG)&pciBuffer; + + slotData.u.bits.DeviceNumber = 0; + slotData.u.bits.FunctionNumber = 0; + + if (ScsiPortGetBusData(deviceExtension, + PCIConfiguration, + 0, // BusNumber + slotData.u.AsULONG, + pciData, + sizeof(ULONG))) { + + atapiOnly = FALSE; + + // + // Wait on doing this, until a reliable method + // of determining support is found. + // + + #if 0 + deviceExtension->DWordIO = TRUE; + #endif + + } else { + deviceExtension->DWordIO = FALSE; + } + + } else { + + atapiOnly = FALSE; + deviceExtension->DriverMustPoll = FALSE; + + }// preConfig check + + // + // Save the Interrupe Mode for later use + // + deviceExtension->InterruptMode = ConfigInfo->InterruptMode; + + // + // Search for devices on this controller. + // + + if (FindDevices(HwDeviceExtension, atapiOnly, ideChannel)) { + foundDevice = TRUE; + } + } + + if (!foundDevice) { + AtapiFreeIoBase (HwDeviceExtension, + ConfigInfo, + FindState, + cmdLogicalBasePort, + ctrlLogicalBasePort, + bmLogicalBasePort); + } else { + + ULONG deviceNumber; + + DeviceSpecificInitialize(HwDeviceExtension); + + for (deviceNumber = 0; deviceNumber < 4; deviceNumber++) { + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_USE_DMA) { + ConfigInfo->NeedPhysicalAddresses = TRUE; + } else { + ConfigInfo->MapBuffers = TRUE; + } + break; + } + } + if (ConfigInfo->NeedPhysicalAddresses) { + if (!AtapiAllocatePRDT(HwDeviceExtension, ConfigInfo)) { + // Unable to get buffer descriptor table, + // go back to PIO mode + deviceExtension->ControllerFlags &= ~CFLAGS_BUS_MASTERING; + DeviceSpecificInitialize(HwDeviceExtension); + ConfigInfo->NeedPhysicalAddresses = FALSE; + ConfigInfo->MapBuffers = TRUE; + } + } + + if (!AtapiPlaySafe && + (deviceExtension->ControllerFlags & CFLAGS_BUS_MASTERING) && + (deviceExtension->BMTimingControl)) { + (*deviceExtension->BMTimingControl) (deviceExtension); + } + + return(SP_RETURN_FOUND); + } + } + + // + // The entire table has been searched and no adapters have been found. + // There is no need to call again and the device base can now be freed. + // Clear the adapter count for the next bus. + // + + *Again = FALSE; + + return(SP_RETURN_NOT_FOUND); + +} // end AtapiFindController() + + +VOID +DeviceSpecificInitialize( + IN PVOID HwDeviceExtension + ) +/*++ + +Routine Description: + + software-initialize devices on the ide bus + + figure out + if the attached devices are dma capable + if the attached devices are LBA ready + + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + none + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG deviceNumber; + BOOLEAN pioDevicePresent; + IDENTIFY_DATA2 * identifyData; + struct _DEVICE_PARAMETERS * deviceParameters; + BOOLEAN dmaCapable; + ULONG pioCycleTime; + ULONG standardPioCycleTime; + ULONG dmaCycleTime; + ULONG standardDmaCycleTime; + + // + // Figure out who can do DMA and who cannot + // + for (deviceNumber = 0; deviceNumber < 4; deviceNumber++) { + + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { + + // + // check LBA capabilities + // + deviceExtension->DeviceFlags[deviceNumber] &= ~DFLAGS_LBA; + + // Some drives lie about their ability to do LBA + // we don't want to do LBA unless we have to (>8G drive) +// if (deviceExtension->IdentifyData[targetId].Capabilities & IDENTIFY_CAPABILITIES_LBA_SUPPORTED) { +// deviceExtension->DeviceFlags[targetId] |= DFLAGS_LBA; +// } + if (deviceExtension->IdentifyData[deviceNumber].UserAddressableSectors > MAX_NUM_CHS_ADDRESSABLE_SECTORS) { + // some device has a bogus value in the UserAddressableSectors field + // make sure these 3 fields are max. out as defined in ATA-3 (X3T10 Rev. 6) + if ((deviceExtension->IdentifyData[deviceNumber].NumberOfCylinders == 16383) && + (deviceExtension->IdentifyData[deviceNumber].NumberOfHeads == 16) && + (deviceExtension->IdentifyData[deviceNumber].SectorsPerTrack == 63)) { + deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_LBA; + } + } + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_LBA) { + DebugPrint ((1, "atapi: target %d supports LBA\n", deviceNumber)); + } + + // + // Try to enable DMA + // + dmaCapable = FALSE; + + if (deviceExtension->ControllerFlags & CFLAGS_BUS_MASTERING) { + UCHAR dmaStatus; + dmaStatus = ScsiPortReadPortUchar (&deviceExtension->BusMasterPortBase[deviceNumber >> 1]->Status); + if (deviceNumber & 1) { + if (dmaStatus & BUSMASTER_DEVICE1_DMA_OK) { + DebugPrint ((1, "atapi: target %d busmaster status 0x%x DMA capable bit is set\n", deviceNumber, dmaStatus)); + dmaCapable = TRUE; + } + } else { + if (dmaStatus & BUSMASTER_DEVICE0_DMA_OK) { + DebugPrint ((1, "atapi: target %d busmaster status 0x%x DMA capable bit is set\n", deviceNumber, dmaStatus)); + dmaCapable = TRUE; + } + } + } + + // + // figure out the shortest PIO cycle time the deivce supports + // + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = INVALID_PIO_MODE; + deviceExtension->DeviceParameters[deviceNumber].BestSingleWordDMAMode = INVALID_SWDMA_MODE; + deviceExtension->DeviceParameters[deviceNumber].BestMultiWordDMAMode = INVALID_MWDMA_MODE; + pioCycleTime = standardPioCycleTime = UNINITIALIZED_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].IoReadyEnabled = FALSE; + + if (deviceExtension->IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 1)) { + if (deviceExtension->IdentifyData[deviceNumber].MinimumPIOCycleTimeIORDY) { + pioCycleTime = deviceExtension->IdentifyData[deviceNumber].MinimumPIOCycleTimeIORDY; + } + } + + if (deviceExtension->IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 1)) { + if (deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes & (1 << 1)) { + standardPioCycleTime = PIO_MODE4_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = 4; + } else if (deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes & (1 << 0)) { + standardPioCycleTime = PIO_MODE3_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = 3; + } + if (pioCycleTime == UNINITIALIZED_CYCLE_TIME) { + pioCycleTime = standardPioCycleTime; + } + + } else { + + if ((deviceExtension->IdentifyData[deviceNumber].PioCycleTimingMode & 0x00ff) == 2) { + standardPioCycleTime = PIO_MODE2_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = 2; + } else if ((deviceExtension->IdentifyData[deviceNumber].PioCycleTimingMode & 0x00ff) == 1) { + standardPioCycleTime = PIO_MODE1_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = 1; + } else { + standardPioCycleTime = PIO_MODE0_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestPIOMode = 0; + } + + if (pioCycleTime == UNINITIALIZED_CYCLE_TIME) { + pioCycleTime = standardPioCycleTime; + } + } + + deviceExtension->DeviceParameters[deviceNumber].PioCycleTime = pioCycleTime; + if (deviceExtension->IdentifyData[deviceNumber].Capabilities & IDENTIFY_CAPABILITIES_IOREADY_SUPPORTED) { + deviceExtension->DeviceParameters[deviceNumber].IoReadyEnabled = TRUE; + } + + // + // figure out the shortest DMA cycle time the device supports + // + // check min cycle time + // + dmaCycleTime = standardDmaCycleTime = UNINITIALIZED_CYCLE_TIME; + if (deviceExtension->IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 1)) { + DebugPrint ((1, "atapi: target %d IdentifyData word 64-70 are valid\n", deviceNumber)); + + if (deviceExtension->IdentifyData[deviceNumber].MinimumMWXferCycleTime && + deviceExtension->IdentifyData[deviceNumber].RecommendedMWXferCycleTime) { + DebugPrint ((1, + "atapi: target %d IdentifyData MinimumMWXferCycleTime = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].MinimumMWXferCycleTime)); + DebugPrint ((1, + "atapi: target %d IdentifyData RecommendedMWXferCycleTime = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].RecommendedMWXferCycleTime)); + dmaCycleTime = deviceExtension->IdentifyData[deviceNumber].RecommendedMWXferCycleTime; + } + } + // + // check mulitword DMA timing + // + if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport) { + DebugPrint ((1, + "atapi: target %d IdentifyData MultiWordDMASupport = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport)); + DebugPrint ((1, + "atapi: target %d IdentifyData MultiWordDMAActive = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].MultiWordDMAActive)); + + if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport & (1 << 2)) { + standardDmaCycleTime = MWDMA_MODE2_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestMultiWordDMAMode = 2; + + } else if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport & (1 << 1)) { + standardDmaCycleTime = MWDMA_MODE1_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestMultiWordDMAMode = 1; + + } else if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport & (1 << 0)) { + standardDmaCycleTime = MWDMA_MODE0_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestMultiWordDMAMode = 0; + } + if (dmaCycleTime == UNINITIALIZED_CYCLE_TIME) { + dmaCycleTime = standardDmaCycleTime; + } + } + + // + // check singleword DMA timing + // + if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport) { + DebugPrint ((1, + "atapi: target %d IdentifyData SingleWordDMASupport = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport)); + DebugPrint ((1, + "atapi: target %d IdentifyData SingleWordDMAActive = 0x%x\n", + deviceNumber, + deviceExtension->IdentifyData[deviceNumber].SingleWordDMAActive)); + + if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport & (1 << 2)) { + standardDmaCycleTime = SWDMA_MODE2_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestSingleWordDMAMode = 2; + + } else if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport & (1 << 1)) { + standardDmaCycleTime = SWDMA_MODE1_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestSingleWordDMAMode = 1; + + } else if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport & (1 << 0)) { + standardDmaCycleTime = SWDMA_MODE0_CYCLE_TIME; + deviceExtension->DeviceParameters[deviceNumber].BestSingleWordDMAMode = 0; + } + if (dmaCycleTime == UNINITIALIZED_CYCLE_TIME) { + dmaCycleTime = standardDmaCycleTime; + } + } + + deviceExtension->DeviceParameters[deviceNumber].DmaCycleTime = dmaCycleTime; + +// +// Study shows that even dma cycle time may be larger than pio cycle time, dma +// can still give better data throughput +// +// if (dmaCycleTime > pioCycleTime) { +// DebugPrint ((0, "atapi: target %d can do PIO (%d) faster than DMA (%d). Turning off DMA...\n", deviceNumber, pioCycleTime, dmaCycleTime)); +// dmaCapable = FALSE; +// } else { +// if (!AtapiPlaySafe) { +// if (dmaCapable == FALSE) { +// DebugPrint ((0, "atapi: If we play safe, we would NOT detect target %d is DMA capable\n", deviceNumber)); +// } +// dmaCapable = TRUE; +// } +// } + + if ((deviceExtension->IdentifyData[deviceNumber].SingleWordDMAActive == 0) && + (deviceExtension->IdentifyData[deviceNumber].MultiWordDMAActive == 0)) { + dmaCapable = FALSE; + } else { + if (!AtapiPlaySafe) { + if (dmaCapable == FALSE) { + DebugPrint ((0, "atapi: If we play safe, we would NOT detect target %d is DMA capable\n", deviceNumber)); + } + dmaCapable = TRUE; + } + } + + // + // Check for bad devices + // + if (AtapiDeviceDMACapable (deviceExtension, deviceNumber) == FALSE) { + dmaCapable = FALSE; + } + + if ((deviceExtension->ControllerFlags & CFLAGS_BUS_MASTERING) && dmaCapable) { + deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_USE_DMA; + } else { + deviceExtension->DeviceFlags[deviceNumber] &= ~DFLAGS_USE_DMA; + } + } + } + + + + + // + // Default everyone to pio if anyone of them cannot do DMA + // We can remove this if it is ok to mix DMA and PIO devices on the same channel + // + // If we are going to allow mixing DMA and PIO, we need to change SCSIPORT + // to allow setting both NeedPhysicalAddresses and MapBuffers to TRUE in + // PORT_CONFIGURATION_INFORMATION + // + pioDevicePresent = FALSE; + for (deviceNumber = 0; deviceNumber < 4 && !pioDevicePresent; deviceNumber++) { + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { + if (!(deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_USE_DMA)) { + pioDevicePresent = TRUE; // bummer! + } + } + } + + if (pioDevicePresent) { + for (deviceNumber = 0; deviceNumber < 4; deviceNumber++) { + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { + deviceExtension->DeviceFlags[deviceNumber] &= ~DFLAGS_USE_DMA; + } + } + } + + + // + // pick out the ATA or ATAPI r/w command we are going to use + // + for (deviceNumber = 0; deviceNumber < 4; deviceNumber++) { + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { + + DebugPrint ((0, "ATAPI: Base=0x%x Device %d is going to do ", deviceExtension->BaseIoAddress1[deviceNumber >> 1], deviceNumber)); + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_USE_DMA) { + DebugPrint ((0, "DMA\n")); + } else { + DebugPrint ((0, "PIO\n")); + } + + + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) { + + deviceExtension->DeviceParameters[deviceNumber].MaxWordPerInterrupt = 256; + + } else { + + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_USE_DMA) { + + DebugPrint ((2, "ATAPI: ATA Device (%d) is going to do DMA\n", deviceNumber)); + deviceExtension->DeviceParameters[deviceNumber].IdeReadCommand = IDE_COMMAND_READ_DMA; + deviceExtension->DeviceParameters[deviceNumber].IdeWriteCommand = IDE_COMMAND_WRITE_DMA; + deviceExtension->DeviceParameters[deviceNumber].MaxWordPerInterrupt = MAX_TRANSFER_SIZE_PER_SRB / 2; + + } else { + + if (deviceExtension->MaximumBlockXfer[deviceNumber]) { + + DebugPrint ((2, "ATAPI: ATA Device (%d) is going to do PIO Multiple\n", deviceNumber)); + deviceExtension->DeviceParameters[deviceNumber].IdeReadCommand = IDE_COMMAND_READ_MULTIPLE; + deviceExtension->DeviceParameters[deviceNumber].IdeWriteCommand = IDE_COMMAND_WRITE_MULTIPLE; + deviceExtension->DeviceParameters[deviceNumber].MaxWordPerInterrupt = + deviceExtension->MaximumBlockXfer[deviceNumber] * 256; + } else { + + DebugPrint ((2, "ATAPI: ATA Device (%d) is going to do PIO Single\n", deviceNumber)); + deviceExtension->DeviceParameters[deviceNumber].IdeReadCommand = IDE_COMMAND_READ; + deviceExtension->DeviceParameters[deviceNumber].IdeWriteCommand = IDE_COMMAND_WRITE; + deviceExtension->DeviceParameters[deviceNumber].MaxWordPerInterrupt = 256; + } + } + } + } + } + +} + + +ULONG +Atapi2Scsi( + IN PSCSI_REQUEST_BLOCK Srb, + IN char *DataBuffer, + IN ULONG ByteCount + ) +/*++ + +Routine Description: + + Convert atapi cdb and mode sense data to scsi format + +Arguments: + + Srb - SCSI request block + DataBuffer - mode sense data + ByteCount - mode sense data length + +Return Value: + + word adjust + +--*/ +{ + ULONG bytesAdjust = 0; + if (Srb->Cdb[0] == ATAPI_MODE_SENSE) { + + PMODE_PARAMETER_HEADER_10 header_10 = (PMODE_PARAMETER_HEADER_10)DataBuffer; + PMODE_PARAMETER_HEADER header = (PMODE_PARAMETER_HEADER)DataBuffer; + + header->ModeDataLength = header_10->ModeDataLengthLsb; + header->MediumType = header_10->MediumType; + + // + // ATAPI Mode Parameter Header doesn't have these fields. + // + + header->DeviceSpecificParameter = header_10->Reserved[0]; + header->BlockDescriptorLength = header_10->Reserved[1]; + + ByteCount -= sizeof(MODE_PARAMETER_HEADER_10); + if (ByteCount > 0) + ScsiPortMoveMemory(DataBuffer+sizeof(MODE_PARAMETER_HEADER), + DataBuffer+sizeof(MODE_PARAMETER_HEADER_10), + ByteCount); + + // + // Change ATAPI_MODE_SENSE opcode back to SCSIOP_MODE_SENSE + // so that we don't convert again. + // + + Srb->Cdb[0] = SCSIOP_MODE_SENSE; + + bytesAdjust = sizeof(MODE_PARAMETER_HEADER_10) - + sizeof(MODE_PARAMETER_HEADER); + + + } + + // + // Convert to words. + // + + return bytesAdjust >> 1; +} + + +VOID +AtapiCallBack( + IN PVOID HwDeviceExtension + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb; + PATAPI_REGISTERS_1 baseIoAddress1; + UCHAR statusByte; + + // + // If the last command was DSC restrictive, see if it's set. If so, the device is + // ready for a new request. Otherwise, reset the timer and come back to here later. + // + + if (srb && (!(deviceExtension->ExpectingInterrupt))) { +#if DBG + if (!IS_RDP((srb->Cdb[0]))) { + DebugPrint((1, + "AtapiCallBack: Invalid CDB marked as RDP - %x\n", + srb->Cdb[0])); + } +#endif + + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[srb->TargetId >> 1]; + if (deviceExtension->RDP) { + GetStatus(baseIoAddress1, statusByte); + if (statusByte & IDE_STATUS_DSC) { + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + deviceExtension->RDP = FALSE; + + // + // Ask for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + + return; + + } else { + + DebugPrint((3, + "AtapiCallBack: Requesting another timer for Op %x\n", + deviceExtension->CurrentSrb->Cdb[0])); + + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 1000); + return; + } + } + } + + DebugPrint((2, + "AtapiCallBack: Calling ISR directly due to BUSY\n")); + AtapiInterrupt(HwDeviceExtension); +} + + +BOOLEAN +AtapiInterrupt( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This is the interrupt service routine for ATAPI IDE miniport driver. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE if expecting an interrupt. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb; + PATAPI_REGISTERS_1 baseIoAddress1; + PATAPI_REGISTERS_2 baseIoAddress2; + ULONG wordCount = 0, wordsThisInterrupt = 256; + ULONG status; + ULONG i; + UCHAR statusByte,interruptReason; + BOOLEAN commandComplete = FALSE; + BOOLEAN atapiDev = FALSE; + UCHAR dmaStatus; + + if (srb) { + // PCI Busmaster IDE Controller spec defines a bit in its status + // register which indicates pending interrupt. However, + // CMD 646 (maybe some other one, too) doesn't always do that if + // the interrupt is from a atapi device. (strange, but true!) + // Since we have to look at the interrupt bit only if we are sharing + // interrupt, we will do just that + if (deviceExtension->InterruptMode == LevelSensitive) { + if (deviceExtension->ControllerFlags & CFLAGS_BUS_MASTERING) { + dmaStatus = ScsiPortReadPortUchar (&deviceExtension->BusMasterPortBase[srb->TargetId >> 1]->Status); + if ((dmaStatus & BUSMASTER_INTERRUPT) == 0) { + return FALSE; + } + } + } + + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[srb->TargetId >> 1]; + baseIoAddress2 = (PATAPI_REGISTERS_2)deviceExtension->BaseIoAddress2[srb->TargetId >> 1]; + } else { + DebugPrint((2, + "AtapiInterrupt: CurrentSrb is NULL\n")); + // + // We can only support one ATAPI IDE master on Carolina, so find + // the base address that is non NULL and clear its interrupt before + // returning. + // + +#ifdef _PPC_ + + if ((PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0]; + } else { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[1]; + } + + GetBaseStatus(baseIoAddress1, statusByte); +#else + + if (deviceExtension->InterruptMode == LevelSensitive) { + if (deviceExtension->BaseIoAddress1[0] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0]; + GetBaseStatus(baseIoAddress1, statusByte); + } + if (deviceExtension->BaseIoAddress1[1] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[1]; + GetBaseStatus(baseIoAddress1, statusByte); + } + } +#endif + return FALSE; + } + + if (!(deviceExtension->ExpectingInterrupt)) { + + DebugPrint((3, + "AtapiInterrupt: Unexpected interrupt.\n")); + return FALSE; + } + + // + // Clear interrupt by reading status. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + DebugPrint((3, + "AtapiInterrupt: Entered with status (%x)\n", + statusByte)); + + + if (statusByte & IDE_STATUS_BUSY) { + if (deviceExtension->DriverMustPoll) { + + // + // Crashdump is polling and we got caught with busy asserted. + // Just go away, and we will be polled again shortly. + // + + DebugPrint((3, + "AtapiInterrupt: Hit BUSY while polling during crashdump.\n")); + + return TRUE; + } + + // + // Ensure BUSY is non-asserted. + // + + for (i = 0; i < 10; i++) { + + GetBaseStatus(baseIoAddress1, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + ScsiPortStallExecution(5000); + } + + if (i == 10) { + + DebugPrint((2, + "AtapiInterrupt: BUSY on entry. Status %x, Base IO %x\n", + statusByte, + baseIoAddress1)); + + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 500); + return TRUE; + } + } + + + if (deviceExtension->DMAInProgress) { + deviceExtension->DMAInProgress = FALSE; + dmaStatus = ScsiPortReadPortUchar (&deviceExtension->BusMasterPortBase[srb->TargetId >> 1]->Status); + ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[srb->TargetId >> 1]->Command, + 0); // disable BusMastering + ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[srb->TargetId >> 1]->Status, + (UCHAR) (dmaStatus | BUSMASTER_INTERRUPT | BUSMASTER_ERROR)); // clear interrupt/error + + deviceExtension->WordsLeft = 0; + + if ((dmaStatus & (BUSMASTER_INTERRUPT | BUSMASTER_ERROR | BUSMASTER_ACTIVE)) != BUSMASTER_INTERRUPT) { // dma ok? + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } else { + deviceExtension->WordsLeft = 0; + } + + } + + // + // Check for error conditions. + // + + if (statusByte & IDE_STATUS_ERROR) { + + if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + } + + // + // check reason for this interrupt. + // + + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + interruptReason = (ScsiPortReadPortUchar(&baseIoAddress1->InterruptReason) & 0x3); + atapiDev = TRUE; + wordsThisInterrupt = 256; + + } else { + + if (statusByte & IDE_STATUS_DRQ) { + + if (deviceExtension->MaximumBlockXfer[srb->TargetId]) { + wordsThisInterrupt = 256 * deviceExtension->MaximumBlockXfer[srb->TargetId]; + + } + + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + interruptReason = 0x2; + + } else if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + interruptReason = 0x0; + + } else { + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + } else if (statusByte & IDE_STATUS_BUSY) { + + return FALSE; + + } else { + + if (deviceExtension->WordsLeft) { + + ULONG k; + + // + // Funky behaviour seen with PCI IDE (not all, just one). + // The ISR hits with DRQ low, but comes up later. + // + + for (k = 0; k < 5000; k++) { + GetStatus(baseIoAddress1,statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + ScsiPortStallExecution(100); + } else { + break; + } + } + + if (k == 5000) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to DRQ not up. Status %x, Base IO %x\n", + statusByte, + baseIoAddress1)); + + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } else { + + interruptReason = (srb->SrbFlags & SRB_FLAGS_DATA_IN) ? 0x2 : 0x0; + } + + } else { + + // + // Command complete - verify, write, or the SMART enable/disable. + // + // Also get_media_status + + interruptReason = 0x3; + } + } + } + + if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) { + + // + // Write the packet. + // + + DebugPrint((2, + "AtapiInterrupt: Writing Atapi packet.\n")); + + // + // Send CDB to device. + // + + WriteBuffer(baseIoAddress1, + (PUSHORT)srb->Cdb, + 6); + + switch (srb->Cdb[0]) { + + case SCSIOP_RECEIVE: + case SCSIOP_SEND: + case SCSIOP_READ: + case SCSIOP_WRITE: + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_USE_DMA) { + EnableBusMastering(HwDeviceExtension, srb); + } + break; + + default: + break; + } + + return TRUE; + + } else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) { + + // + // Write the data. + // + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Pick up bytes to transfer and convert to words. + // + + wordCount = + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + + wordCount |= + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh) << 8; + + // + // Covert bytes to words. + // + + wordCount >>= 1; + + if (wordCount != deviceExtension->WordsLeft) { + DebugPrint((3, + "AtapiInterrupt: %d words requested; %d words xferred\n", + deviceExtension->WordsLeft, + wordCount)); + } + + // + // Verify this makes sense. + // + + if (wordCount > deviceExtension->WordsLeft) { + wordCount = deviceExtension->WordsLeft; + } + + } else { + + // + // IDE path. Check if words left is at least 256. + // + + if (deviceExtension->WordsLeft < wordsThisInterrupt) { + + // + // Transfer only words requested. + // + + wordCount = deviceExtension->WordsLeft; + + } else { + + // + // Transfer next block. + // + + wordCount = wordsThisInterrupt; + } + } + + // + // Ensure that this is a write command. + // + + if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + + DebugPrint((3, + "AtapiInterrupt: Write interrupt\n")); + + WaitOnBusy(baseIoAddress1,statusByte); + + if (atapiDev || !deviceExtension->DWordIO) { + + WriteBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + } else { + + PIDE_REGISTERS_3 address3 = (PIDE_REGISTERS_3)baseIoAddress1; + + WriteBuffer2(address3, + (PULONG)(deviceExtension->DataBuffer), + wordCount / 2); + } + } else { + + DebugPrint((1, + "AtapiInterrupt: Int reason %x, but srb is for a write %x.\n", + interruptReason, + srb)); + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + + // + // Advance data buffer pointer and bytes left. + // + + deviceExtension->DataBuffer += wordCount; + deviceExtension->WordsLeft -= wordCount; + + return TRUE; + + } else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) { + + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Pick up bytes to transfer and convert to words. + // + + wordCount = + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + + wordCount |= + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh) << 8; + + // + // Covert bytes to words. + // + + wordCount >>= 1; + + if (wordCount != deviceExtension->WordsLeft) { + DebugPrint((3, + "AtapiInterrupt: %d words requested; %d words xferred\n", + deviceExtension->WordsLeft, + wordCount)); + } + + // + // Verify this makes sense. + // + + if (wordCount > deviceExtension->WordsLeft) { + wordCount = deviceExtension->WordsLeft; + } + + } else { + + // + // Check if words left is at least 256. + // + + if (deviceExtension->WordsLeft < wordsThisInterrupt) { + + // + // Transfer only words requested. + // + + wordCount = deviceExtension->WordsLeft; + + } else { + + // + // Transfer next block. + // + + wordCount = wordsThisInterrupt; + } + } + + // + // Ensure that this is a read command. + // + + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + DebugPrint((3, + "AtapiInterrupt: Read interrupt\n")); + + WaitOnBusy(baseIoAddress1,statusByte); + + if (atapiDev || !deviceExtension->DWordIO) { + ReadBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + + } else { + PIDE_REGISTERS_3 address3 = (PIDE_REGISTERS_3)baseIoAddress1; + + ReadBuffer2(address3, + (PULONG)(deviceExtension->DataBuffer), + wordCount / 2); + } + } else { + + DebugPrint((1, + "AtapiInterrupt: Int reason %x, but srb is for a read %x.\n", + interruptReason, + srb)); + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + // + // Translate ATAPI data back to SCSI data if needed + // + + if (srb->Cdb[0] == ATAPI_MODE_SENSE && + deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + //convert and adjust the wordCount + // + + wordCount -= Atapi2Scsi(srb, (char *)deviceExtension->DataBuffer, + wordCount << 1); + } + // + // Advance data buffer pointer and bytes left. + // + + deviceExtension->DataBuffer += wordCount; + deviceExtension->WordsLeft -= wordCount; + + // + // Check for read command complete. + // + + if (deviceExtension->WordsLeft == 0) { + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Work around to make many atapi devices return correct sector size + // of 2048. Also certain devices will have sector count == 0x00, check + // for that also. + // + + if ((srb->Cdb[0] == 0x25) && + ((deviceExtension->IdentifyData[srb->TargetId].GeneralConfiguration >> 8) & 0x1f) == 0x05) { + + deviceExtension->DataBuffer -= wordCount; + if (deviceExtension->DataBuffer[0] == 0x00) { + + *((ULONG *) &(deviceExtension->DataBuffer[0])) = 0xFFFFFF7F; + + } + + *((ULONG *) &(deviceExtension->DataBuffer[2])) = 0x00080000; + deviceExtension->DataBuffer += wordCount; + } + } else { + + // + // Completion for IDE drives. + // + + + if (deviceExtension->WordsLeft) { + + status = SRB_STATUS_DATA_OVERRUN; + + } else { + + status = SRB_STATUS_SUCCESS; + + } + + goto CompleteRequest; + + } + } + + return TRUE; + + } else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ)) { + + // + // Command complete. + // + + if (deviceExtension->WordsLeft) { + + status = SRB_STATUS_DATA_OVERRUN; + + } else { + + status = SRB_STATUS_SUCCESS; + + } + +CompleteRequest: + + // + // Check and see if we are processing our secret (mechanism status/request sense) srb + // + if (deviceExtension->OriginalSrb) { + + ULONG srbStatus; + + if (srb->Cdb[0] == SCSIOP_MECHANISM_STATUS) { + + if (status == SRB_STATUS_SUCCESS) { + // Bingo!! + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) srb->DataBuffer); + + // Get ready to issue the original srb + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + + } else { + // failed! Get the sense key and maybe try again + srb = deviceExtension->CurrentSrb = BuildRequestSenseSrb ( + HwDeviceExtension, + deviceExtension->OriginalSrb->PathId, + deviceExtension->OriginalSrb->TargetId); + } + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return TRUE; + } + + } else { // srb->Cdb[0] == SCSIOP_REQUEST_SENSE) + + PSENSE_DATA senseData = (PSENSE_DATA) srb->DataBuffer; + + if (status == SRB_STATUS_DATA_OVERRUN) { + // Check to see if we at least get mininum number of bytes + if ((srb->DataTransferLength - deviceExtension->WordsLeft) > + (offsetof (SENSE_DATA, AdditionalSenseLength) + sizeof(senseData->AdditionalSenseLength))) { + status = SRB_STATUS_SUCCESS; + } + } + + if (status == SRB_STATUS_SUCCESS) { + if ((senseData->SenseKey != SCSI_SENSE_ILLEGAL_REQUEST) && + deviceExtension->MechStatusRetryCount) { + + // The sense key doesn't say the last request is illegal, so try again + deviceExtension->MechStatusRetryCount--; + srb = deviceExtension->CurrentSrb = BuildMechanismStatusSrb ( + HwDeviceExtension, + deviceExtension->OriginalSrb->PathId, + deviceExtension->OriginalSrb->TargetId); + } else { + + // last request was illegal. No point trying again + + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + + // Get ready to issue the original srb + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return TRUE; + } + } + } + + // If we get here, it means AtapiSendCommand() has failed + // Can't recover. Pretend the original srb has failed and complete it. + + if (deviceExtension->OriginalSrb) { + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + // fake an error and read no data + status = SRB_STATUS_ERROR; + srb->ScsiStatus = 0; + deviceExtension->DataBuffer = srb->DataBuffer; + deviceExtension->WordsLeft = srb->DataTransferLength; + deviceExtension->RDP = FALSE; + + } else if (status == SRB_STATUS_ERROR) { + + // + // Map error to specific SRB status and handle request sense. + // + + status = MapError(deviceExtension, + srb); + + deviceExtension->RDP = FALSE; + + } else { + + // + // Wait for busy to drop. + // + + for (i = 0; i < 30; i++) { + GetStatus(baseIoAddress1,statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + ScsiPortStallExecution(500); + } + + if (i == 30) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to BSY still up - %x. Base Io %x\n", + statusByte, + baseIoAddress1)); + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } + + // + // Check to see if DRQ is still up. + // + + if (statusByte & IDE_STATUS_DRQ) { + + for (i = 0; i < 500; i++) { + GetStatus(baseIoAddress1,statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + break; + } + ScsiPortStallExecution(100); + + } + + if (i == 500) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to DRQ still up - %x\n", + statusByte)); + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } + + } + } + + + // + // Clear interrupt expecting flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + + // + // Sanity check that there is a current request. + // + + if (srb != NULL) { + + // + // Set status in SRB. + // + + srb->SrbStatus = (UCHAR)status; + + // + // Check for underflow. + // + + if (deviceExtension->WordsLeft) { + + // + // Subtract out residual words and update if filemark hit, + // setmark hit , end of data, end of media... + // + + if (!(deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_TAPE_DEVICE)) { + if (status == SRB_STATUS_DATA_OVERRUN) { + srb->DataTransferLength -= deviceExtension->WordsLeft; + } else { + srb->DataTransferLength = 0; + } + } else { + srb->DataTransferLength -= deviceExtension->WordsLeft; + } + } + + if (srb->Function != SRB_FUNCTION_IO_CONTROL) { + + // + // Indicate command complete. + // + + if (!(deviceExtension->RDP)) { + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + } + } else { + + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + UCHAR error = 0; + + if (status != SRB_STATUS_SUCCESS) { + error = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + } + + // + // Build the SMART status block depending upon the completion status. + // + + cmdOutParameters->cBufferSize = wordCount; + cmdOutParameters->DriverStatus.bDriverError = (error) ? SMART_IDE_ERROR : 0; + cmdOutParameters->DriverStatus.bIDEError = error; + + // + // If the sub-command is return smart status, jam the value from cylinder low and high, into the + // data buffer. + // + + if (deviceExtension->SmartCommand == RETURN_SMART_STATUS) { + cmdOutParameters->bBuffer[0] = RETURN_SMART_STATUS; + cmdOutParameters->bBuffer[1] = ScsiPortReadPortUchar(&baseIoAddress1->InterruptReason); + cmdOutParameters->bBuffer[2] = ScsiPortReadPortUchar(&baseIoAddress1->Unused1); + cmdOutParameters->bBuffer[3] = ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + cmdOutParameters->bBuffer[4] = ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh); + cmdOutParameters->bBuffer[5] = ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect); + cmdOutParameters->bBuffer[6] = SMART_CMD; + cmdOutParameters->cBufferSize = 8; + } + + // + // Indicate command complete. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + } + + } else { + + DebugPrint((1, + "AtapiInterrupt: No SRB!\n")); + } + + // + // Indicate ready for next request. + // + + if (!(deviceExtension->RDP)) { + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } else { + + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 2000); + } + + return TRUE; + + } else { + + // + // Unexpected int. + // + + DebugPrint((3, + "AtapiInterrupt: Unexpected interrupt. InterruptReason %x. Status %x.\n", + interruptReason, + statusByte)); + return FALSE; + } + + return TRUE; + +} // end AtapiInterrupt() + + +ULONG +IdeSendSmartCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles SMART enable, disable, read attributes and threshold commands. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + PIDEREGS regs = &cmdInParameters.irDriveRegs; + ULONG i; + UCHAR statusByte,targetId; + + + if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) { + + targetId = cmdInParameters.bDriveNumber; + + //TODO optimize this check + + if ((!(deviceExtension->DeviceFlags[targetId] & DFLAGS_DEVICE_PRESENT)) || + (deviceExtension->DeviceFlags[targetId] & DFLAGS_ATAPI_DEVICE)) { + + return SRB_STATUS_SELECTION_TIMEOUT; + } + + deviceExtension->SmartCommand = cmdInParameters.irDriveRegs.bFeaturesReg; + + // + // Determine which of the commands to carry out. + // + + if ((cmdInParameters.irDriveRegs.bFeaturesReg == READ_ATTRIBUTES) || + (cmdInParameters.irDriveRegs.bFeaturesReg == READ_THRESHOLDS)) { + + WaitOnBusy(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeSendSmartCommand: Returning BUSY status\n")); + return SRB_STATUS_BUSY; + } + + // + // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same). + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer; + deviceExtension->WordsLeft = READ_ATTRIBUTE_BUFFER_SIZE / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,(UCHAR)(((targetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,regs->bFeaturesReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,regs->bSectorCountReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,regs->bSectorNumberReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,regs->bCylLowReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,regs->bCylHighReg); + ScsiPortWritePortUchar(&baseIoAddress1->Command,regs->bCommandReg); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + + } else if ((cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_SMART) || + (cmdInParameters.irDriveRegs.bFeaturesReg == DISABLE_SMART) || + (cmdInParameters.irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) || + (cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTOSAVE) || + (cmdInParameters.irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS) || + (cmdInParameters.irDriveRegs.bFeaturesReg == SAVE_ATTRIBUTE_VALUES)) { + + WaitOnBusy(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeSendSmartCommand: Returning BUSY status\n")); + return SRB_STATUS_BUSY; + } + + // + // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same). + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Set data buffer pointer and indicate no data transfer. + // + + deviceExtension->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer; + deviceExtension->WordsLeft = 0; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,(UCHAR)(((targetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,regs->bFeaturesReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,regs->bSectorCountReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,regs->bSectorNumberReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,regs->bCylLowReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,regs->bCylHighReg); + ScsiPortWritePortUchar(&baseIoAddress1->Command,regs->bCommandReg); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + } + } + + return SRB_STATUS_INVALID_REQUEST; + +} // end IdeSendSmartCommand() + + +ULONG +IdeReadWrite( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles IDE read and writes. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG startingSector,i; + ULONG wordCount; + UCHAR statusByte,statusByte2; + UCHAR cylinderHigh,cylinderLow,drvSelect,sectorNumber; + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + + WaitOnBusy(baseIoAddress1,statusByte2); + + if (statusByte2 & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeReadWrite: Returning BUSY status\n")); + return SRB_STATUS_BUSY; + } + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA) { + if (!PrepareForBusMastering(HwDeviceExtension, Srb)) + return SRB_STATUS_ERROR; + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + // + // Set up sector count register. Round up to next block. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)((Srb->DataTransferLength + 0x1FF) / 0x200)); + + // + // Get starting sector number from CDB. + // + + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + DebugPrint((2, + "IdeReadWrite: Starting sector is %x, Number of bytes %x\n", + startingSector, + Srb->DataTransferLength)); + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) { + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR) (((Srb->TargetId & 0x1) << 4) | + 0xA0 | + IDE_LBA_MODE | + ((startingSector & 0x0f000000) >> 24))); + + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber, + (UCHAR) ((startingSector & 0x000000ff) >> 0)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, + (UCHAR) ((startingSector & 0x0000ff00) >> 8)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, + (UCHAR) ((startingSector & 0x00ff0000) >> 16)); + + } else { //CHS + + // + // Set up sector number register. + // + + sectorNumber = (UCHAR)((startingSector % deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) + 1); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,sectorNumber); + + // + // Set up cylinder low register. + // + + cylinderLow = (UCHAR)(startingSector / (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,cylinderLow); + + // + // Set up cylinder high register. + // + + cylinderHigh = (UCHAR)((startingSector / (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)) >> 8); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,cylinderHigh); + + // + // Set up head and drive select register. + // + + drvSelect = (UCHAR)(((startingSector / deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads) |((Srb->TargetId & 0x1) << 4) | 0xA0); + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,drvSelect); + + DebugPrint((2, + "IdeReadWrite: Cylinder %x Head %x Sector %x\n", + startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads), + (startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack + 1)); + } + + // + // Check if write request. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + // + // Send read command. + // + ScsiPortWritePortUchar(&baseIoAddress1->Command, + deviceExtension->DeviceParameters[Srb->TargetId].IdeReadCommand); + + } else { + + + // + // Send write command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + deviceExtension->DeviceParameters[Srb->TargetId].IdeWriteCommand); + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA)) { + + if (deviceExtension->WordsLeft < deviceExtension->DeviceParameters[Srb->TargetId].MaxWordPerInterrupt) { + wordCount = deviceExtension->WordsLeft; + } else { + wordCount = deviceExtension->DeviceParameters[Srb->TargetId].MaxWordPerInterrupt; + } + // + // Wait for BSY and DRQ. + // + + WaitOnBaseBusy(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + + DebugPrint((1, + "IdeReadWrite 2: Returning BUSY status %x\n", + statusByte)); + return SRB_STATUS_BUSY; + } + + for (i = 0; i < 1000; i++) { + GetBaseStatus(baseIoAddress1, statusByte); + if (statusByte & IDE_STATUS_DRQ) { + break; + } + ScsiPortStallExecution(200); + + } + + if (!(statusByte & IDE_STATUS_DRQ)) { + + DebugPrint((1, + "IdeReadWrite: DRQ never asserted (%x) original status (%x)\n", + statusByte, + statusByte2)); + + deviceExtension->WordsLeft = 0; + + // + // Clear interrupt expecting flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + return SRB_STATUS_TIMEOUT; + } + + // + // Write next 256 words. + // + + WriteBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + + // + // Adjust buffer address and words left count. + // + + deviceExtension->WordsLeft -= wordCount; + deviceExtension->DataBuffer += wordCount; + + } + } + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA) { + EnableBusMastering(HwDeviceExtension, Srb); + } + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + +} // end IdeReadWrite() + + + +ULONG +IdeVerify( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles IDE Verify. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG startingSector; + ULONG sectors; + ULONG endSector; + USHORT sectorCount; + + // + // Drive has these number sectors. + // + + sectors = deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders; + + DebugPrint((3, + "IdeVerify: Total sectors %x\n", + sectors)); + + // + // Get starting sector number from CDB. + // + + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + DebugPrint((3, + "IdeVerify: Starting sector %x. Number of blocks %x\n", + startingSector, + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb)); + + sectorCount = (USHORT)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 | + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb ); + endSector = startingSector + sectorCount; + + DebugPrint((3, + "IdeVerify: Ending sector %x\n", + endSector)); + + if (endSector > sectors) { + + // + // Too big, round down. + // + + DebugPrint((1, + "IdeVerify: Truncating request to %x blocks\n", + sectors - startingSector - 1)); + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)(sectors - startingSector - 1)); + + } else { + + // + // Set up sector count register. Round up to next block. + // + + if (sectorCount > 0xFF) { + sectorCount = (USHORT)0xFF; + } + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,(UCHAR)sectorCount); + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) { // LBA + + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber, + (UCHAR) ((startingSector & 0x000000ff) >> 0)); + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, + (UCHAR) ((startingSector & 0x0000ff00) >> 8)); + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, + (UCHAR) ((startingSector & 0x00ff0000) >> 16)); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR) (((Srb->TargetId & 0x1) << 4) | + 0xA0 | + IDE_LBA_MODE | + (startingSector & 0x0f000000 >> 24))); + + DebugPrint((2, + "IdeVerify: LBA: startingSector %x\n", + startingSector)); + + } else { //CHS + + // + // Set up sector number register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber, + (UCHAR)((startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) + 1)); + + // + // Set up cylinder low register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, + (UCHAR)(startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads))); + + // + // Set up cylinder high register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, + (UCHAR)((startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)) >> 8)); + + // + // Set up head and drive select register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads) | + ((Srb->TargetId & 0x1) << 4) | 0xA0)); + + DebugPrint((2, + "IdeVerify: CHS: Cylinder %x Head %x Sector %x\n", + startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads), + (startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack + 1)); + } + + // + // Send verify command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_VERIFY); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + +} // end IdeVerify() + + +VOID +Scsi2Atapi( + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Convert SCSI packet command to Atapi packet command. + +Arguments: + + Srb - IO request packet + +Return Value: + + None + +--*/ +{ + // + // Change the cdb length + // + + Srb->CdbLength = 12; + + switch (Srb->Cdb[0]) { + case SCSIOP_MODE_SENSE: { + PMODE_SENSE_10 modeSense10 = (PMODE_SENSE_10)Srb->Cdb; + UCHAR PageCode = ((PCDB)Srb->Cdb)->MODE_SENSE.PageCode; + UCHAR Length = ((PCDB)Srb->Cdb)->MODE_SENSE.AllocationLength; + + AtapiZeroMemory(Srb->Cdb,MAXIMUM_CDB_SIZE); + + modeSense10->OperationCode = ATAPI_MODE_SENSE; + modeSense10->PageCode = PageCode; + modeSense10->ParameterListLengthMsb = 0; + modeSense10->ParameterListLengthLsb = Length; + break; + } + + case SCSIOP_MODE_SELECT: { + PMODE_SELECT_10 modeSelect10 = (PMODE_SELECT_10)Srb->Cdb; + UCHAR Length = ((PCDB)Srb->Cdb)->MODE_SELECT.ParameterListLength; + + // + // Zero the original cdb + // + + AtapiZeroMemory(Srb->Cdb,MAXIMUM_CDB_SIZE); + + modeSelect10->OperationCode = ATAPI_MODE_SELECT; + modeSelect10->PFBit = 1; + modeSelect10->ParameterListLengthMsb = 0; + modeSelect10->ParameterListLengthLsb = Length; + break; + } + + case SCSIOP_FORMAT_UNIT: + Srb->Cdb[0] = ATAPI_FORMAT_UNIT; + break; + } +} + + + +ULONG +AtapiSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Send ATAPI packet command to device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PATAPI_REGISTERS_1 baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PATAPI_REGISTERS_2 baseIoAddress2 = (PATAPI_REGISTERS_2)deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG i; + ULONG flags; + UCHAR statusByte,byteCountLow,byteCountHigh; + + // + // We need to know how many platters our atapi cd-rom device might have. + // Before anyone tries to send a srb to our target for the first time, + // we must "secretly" send down a separate mechanism status srb in order to + // initialize our device extension changer data. That's how we know how + // many platters our target has. + // + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_CHANGER_INITED) && + !deviceExtension->OriginalSrb) { + + ULONG srbStatus; + + // + // Set this flag now. If the device hangs on the mech. status + // command, we will not have the change to set it. + // + deviceExtension->DeviceFlags[Srb->TargetId] |= DFLAGS_CHANGER_INITED; + + deviceExtension->MechStatusRetryCount = 3; + deviceExtension->CurrentSrb = BuildMechanismStatusSrb ( + HwDeviceExtension, + Srb->PathId, + Srb->TargetId); + deviceExtension->OriginalSrb = Srb; + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return srbStatus; + } else { + deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + AtapiHwInitializeChanger (HwDeviceExtension, + Srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + // fall out + } + } + + DebugPrint((2, + "AtapiSendCommand: Command %x to TargetId %d lun %d\n", + Srb->Cdb[0], + Srb->TargetId, + Srb->Lun)); + + // + // Make sure command is to ATAPI device. + // + + flags = deviceExtension->DeviceFlags[Srb->TargetId]; + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + if ((Srb->Lun) > (deviceExtension->DiscsPresent[Srb->TargetId] - 1)) { + + // + // Indicate no device found at this address. + // + + return SRB_STATUS_SELECTION_TIMEOUT; + } + } else if (Srb->Lun > 0) { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + if (!(flags & DFLAGS_ATAPI_DEVICE)) { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + + // + // Verify that controller is ready for next command. + // + + GetStatus(baseIoAddress1,statusByte); + + DebugPrint((2, + "AtapiSendCommand: Entered with status %x\n", + statusByte)); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "AtapiSendCommand: Device busy (%x)\n", + statusByte)); + return SRB_STATUS_BUSY; + + } + + if (statusByte & IDE_STATUS_ERROR) { + if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + + DebugPrint((1, + "AtapiSendCommand: Error on entry: (%x)\n", + statusByte)); + // + // Read the error reg. to clear it and fail this request. + // + + return MapError(deviceExtension, + Srb); + } + } + + // + // If a tape drive has doesn't have DSC set and the last command is restrictive, don't send + // the next command. See discussion of Restrictive Delayed Process commands in QIC-157. + // + + if ((!(statusByte & IDE_STATUS_DSC)) && + (flags & DFLAGS_TAPE_DEVICE) && deviceExtension->RDP) { + ScsiPortStallExecution(1000); + DebugPrint((2,"AtapiSendCommand: DSC not set. %x\n",statusByte)); + return SRB_STATUS_BUSY; + } + + if (IS_RDP(Srb->Cdb[0])) { + + deviceExtension->RDP = TRUE; + + DebugPrint((3, + "AtapiSendCommand: %x mapped as DSC restrictive\n", + Srb->Cdb[0])); + + } else { + + deviceExtension->RDP = FALSE; + } + + if (statusByte & IDE_STATUS_DRQ) { + + DebugPrint((1, + "AtapiSendCommand: Entered with status (%x). Attempting to recover.\n", + statusByte)); + // + // Try to drain the data that one preliminary device thinks that it has + // to transfer. Hopefully this random assertion of DRQ will not be present + // in production devices. + // + + for (i = 0; i < 0x10000; i++) { + + GetStatus(baseIoAddress1, statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + ScsiPortReadPortUshort(&baseIoAddress1->Data); + + } else { + + break; + } + } + + if (i == 0x10000) { + + DebugPrint((1, + "AtapiSendCommand: DRQ still asserted.Status (%x)\n", + statusByte)); + + AtapiSoftReset(baseIoAddress1,Srb->TargetId, FALSE); + + DebugPrint((1, + "AtapiSendCommand: Issued soft reset to Atapi device. \n")); + + // + // Re-initialize Atapi device. + // + + IssueIdentify(HwDeviceExtension, + (Srb->TargetId & 0x1), + (Srb->TargetId >> 1), + IDE_COMMAND_ATAPI_IDENTIFY, + FALSE); + + // + // Inform the port driver that the bus has been reset. + // + + ScsiPortNotification(ResetDetected, HwDeviceExtension, 0); + + // + // Clean up device extension fields that AtapiStartIo won't. + // + + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->RDP = FALSE; + + return SRB_STATUS_BUS_RESET; + + } + } + + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + + // + // As the cdrom driver sets the LUN field in the cdb, it must be removed. + // + + Srb->Cdb[1] &= ~0xE0; + + if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) { + + // + // Torisan changer. TUR's are overloaded to be platter switches. + // + + Srb->Cdb[7] = Srb->Lun; + + } + } + + switch (Srb->Cdb[0]) { + + // + // Convert SCSI to ATAPI commands if needed + // + case SCSIOP_MODE_SENSE: + case SCSIOP_MODE_SELECT: + case SCSIOP_FORMAT_UNIT: + if (!(flags & DFLAGS_TAPE_DEVICE)) { + Scsi2Atapi(Srb); + } + + break; + + case SCSIOP_RECEIVE: + case SCSIOP_SEND: + case SCSIOP_READ: + case SCSIOP_WRITE: + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA) { + if (!PrepareForBusMastering(HwDeviceExtension, Srb)) + return SRB_STATUS_ERROR; + } + break; + + default: + break; + } + + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + WaitOnBusy(baseIoAddress1,statusByte); + + // + // Write transfer byte count to registers. + // + + byteCountLow = (UCHAR)(Srb->DataTransferLength & 0xFF); + byteCountHigh = (UCHAR)(Srb->DataTransferLength >> 8); + + if (Srb->DataTransferLength >= 0x10000) { + byteCountLow = byteCountHigh = 0xFF; + } + + ScsiPortWritePortUchar(&baseIoAddress1->ByteCountLow,byteCountLow); + ScsiPortWritePortUchar(&baseIoAddress1->ByteCountHigh, byteCountHigh); + + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,0); + if ((Srb->Cdb[0] == SCSIOP_READ) || + (Srb->Cdb[0] == SCSIOP_WRITE) || + (Srb->Cdb[0] == SCSIOP_SEND) || + (Srb->Cdb[0] == SCSIOP_RECEIVE)) { + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA) { + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1, 0x1); + } + } + + if (flags & DFLAGS_INT_DRQ) { + + // + // This device interrupts when ready to receive the packet. + // + // Write ATAPI packet command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_ATAPI_PACKET); + + DebugPrint((3, + "AtapiSendCommand: Wait for int. to send packet. Status (%x)\n", + statusByte)); + + deviceExtension->ExpectingInterrupt = TRUE; + + return SRB_STATUS_PENDING; + + } else { + + // + // Write ATAPI packet command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_ATAPI_PACKET); + + // + // Wait for DRQ. + // + + WaitOnBusy(baseIoAddress1, statusByte); + WaitForDrq(baseIoAddress1, statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) { + + DebugPrint((1, + "AtapiSendCommand: DRQ never asserted (%x)\n", + statusByte)); + return SRB_STATUS_ERROR; + } + } + + // + // Need to read status register. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + // + // Send CDB to device. + // + + WaitOnBusy(baseIoAddress1,statusByte); + + WriteBuffer(baseIoAddress1, + (PUSHORT)Srb->Cdb, + 6); + + // + // Indicate expecting an interrupt and wait for it. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + switch (Srb->Cdb[0]) { + + case SCSIOP_RECEIVE: + case SCSIOP_SEND: + case SCSIOP_READ: + case SCSIOP_WRITE: + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA) { + EnableBusMastering(HwDeviceExtension, Srb); + } + break; + + default: + break; + } + + return SRB_STATUS_PENDING; + +} // end AtapiSendCommand() + +ULONG +IdeSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Program ATA registers for IDE disk transfer. + +Arguments: + + HwDeviceExtension - ATAPI driver storage. + Srb - System request block. + +Return Value: + + SRB status (pending if all goes well). + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + PCDB cdb; + + UCHAR statusByte,errorByte; + ULONG status; + ULONG i; + PMODE_PARAMETER_HEADER modeData; + + DebugPrint((2, + "IdeSendCommand: Command %x to device %d\n", + Srb->Cdb[0], + Srb->TargetId)); + + + + switch (Srb->Cdb[0]) { + case SCSIOP_INQUIRY: + + // + // Filter out all TIDs but 0 and 1 since this is an IDE interface + // which support up to two devices. + // + + if ((Srb->Lun != 0) || + (!deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT)) { + + // + // Indicate no device found at this address. + // + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + + } else { + + PINQUIRYDATA inquiryData = Srb->DataBuffer; + PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData[Srb->TargetId]; + + // + // Zero INQUIRY data structure. + // + + for (i = 0; i < Srb->DataTransferLength; i++) { + ((PUCHAR)Srb->DataBuffer)[i] = 0; + } + + // + // Standard IDE interface only supports disks. + // + + inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; + + // + // Set the removable bit, if applicable. + // + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_REMOVABLE_DRIVE) { + inquiryData->RemovableMedia = 1; + } + + // + // Fill in vendor identification fields. + // + + for (i = 0; i < 20; i += 2) { + inquiryData->VendorId[i] = + ((PUCHAR)identifyData->ModelNumber)[i + 1]; + inquiryData->VendorId[i+1] = + ((PUCHAR)identifyData->ModelNumber)[i]; + } + + // + // 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] = + ((PUCHAR)identifyData->FirmwareRevision)[i+1]; + inquiryData->ProductRevisionLevel[i+1] = + ((PUCHAR)identifyData->FirmwareRevision)[i]; + } + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_MODE_SENSE: + + // + // This is used to determine of the media is write-protected. + // Since IDE does not support mode sense then we will modify just the portion we need + // so the higher level driver can determine if media is protected. + // + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_GET_MEDIA_STATUS); + WaitOnBusy(baseIoAddress1,statusByte); + + if (!(statusByte & IDE_STATUS_ERROR)){ + + // + // no error occured return success, media is not protected + // + + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + } else { + + // + // error occured, handle it locally, clear interrupt + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + + GetBaseStatus(baseIoAddress1, statusByte); + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + if (errorByte & IDE_ERROR_DATA_ERROR) { + + // + //media is write-protected, set bit in mode sense buffer + // + + modeData = (PMODE_PARAMETER_HEADER)Srb->DataBuffer; + + Srb->DataTransferLength = sizeof(MODE_PARAMETER_HEADER); + modeData->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT; + } + } + status = SRB_STATUS_SUCCESS; + } else { + status = SRB_STATUS_INVALID_REQUEST; + } + break; + + case SCSIOP_TEST_UNIT_READY: + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_GET_MEDIA_STATUS); + + // + // Wait for busy. If media has not changed, return success + // + + WaitOnBusy(baseIoAddress1,statusByte); + + if (!(statusByte & IDE_STATUS_ERROR)){ + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + } else { + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + if (errorByte == IDE_ERROR_DATA_ERROR){ + + // + // Special case: If current media is write-protected, + // the 0xDA command will always fail since the write-protect bit + // is sticky,so we can ignore this error + // + + GetBaseStatus(baseIoAddress1, statusByte); + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + } else { + + // + // Request sense buffer to be build + // + deviceExtension->ExpectingInterrupt = TRUE; + status = SRB_STATUS_PENDING; + } + } + } else { + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_READ_CAPACITY: + + // + // Claim 512 byte blocks (big-endian). + // + + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000; + + // + // Calculate last sector. + // + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) { + // LBA device + i = deviceExtension->IdentifyData[Srb->TargetId].UserAddressableSectors - 1; + + DebugPrint((1, + "IDE LBA disk %x - total # of sectors = 0x%x\n", + Srb->TargetId, + deviceExtension->IdentifyData[Srb->TargetId].UserAddressableSectors)); + + } else { + // CHS device + i = (deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders * + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) - 1; + + DebugPrint((1, + "IDE CHS disk %x - #sectors %x, #heads %x, #cylinders %x\n", + Srb->TargetId, + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack, + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders)); + + } + + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress = + (((PUCHAR)&i)[0] << 24) | (((PUCHAR)&i)[1] << 16) | + (((PUCHAR)&i)[2] << 8) | ((PUCHAR)&i)[3]; + + + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_VERIFY: + status = IdeVerify(HwDeviceExtension,Srb); + + break; + + case SCSIOP_READ: + case SCSIOP_WRITE: + + status = IdeReadWrite(HwDeviceExtension, + Srb); + break; + + case SCSIOP_START_STOP_UNIT: + + // + //Determine what type of operation we should perform + // + cdb = (PCDB)Srb->Cdb; + + if (cdb->START_STOP.LoadEject == 1){ + + // + // Eject media, + // first select device 0 or 1. + // + WaitOnBusy(baseIoAddress1,statusByte); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_MEDIA_EJECT); + } + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_MEDIUM_REMOVAL: + + cdb = (PCDB)Srb->Cdb; + + WaitOnBusy(baseIoAddress1,statusByte); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + if (cdb->MEDIA_REMOVAL.Prevent == TRUE) { + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_DOOR_LOCK); + } else { + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_DOOR_UNLOCK); + } + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_REQUEST_SENSE: + // this function makes sense buffers to report the results + // of the original GET_MEDIA_STATUS command + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + status = IdeBuildSenseBuffer(HwDeviceExtension,Srb); + break; + } + + // ATA_PASSTHORUGH + case SCSIOP_ATA_PASSTHROUGH: + { + PIDEREGS pIdeReg; + pIdeReg = (PIDEREGS) &(Srb->Cdb[2]); + + pIdeReg->bDriveHeadReg &= 0x0f; + pIdeReg->bDriveHeadReg |= (UCHAR) (((Srb->TargetId & 0x1) << 4) | 0xA0); + + if (pIdeReg->bReserved == 0) { // execute ATA command + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, pIdeReg->bDriveHeadReg); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1, pIdeReg->bFeaturesReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, pIdeReg->bSectorCountReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber, pIdeReg->bSectorNumberReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, pIdeReg->bCylLowReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, pIdeReg->bCylHighReg); + ScsiPortWritePortUchar(&baseIoAddress1->Command, pIdeReg->bCommandReg); + + ScsiPortStallExecution(1); // wait for busy to be set + WaitOnBusy(baseIoAddress1,statusByte); // wait for busy to be clear + GetBaseStatus(baseIoAddress1, statusByte); + if (statusByte & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) { + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + Srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID; + Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION; + } + status = SRB_STATUS_ERROR; + } else { + + if (statusByte & IDE_STATUS_DRQ) { + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ReadBuffer(baseIoAddress1, + (PUSHORT) Srb->DataBuffer, + Srb->DataTransferLength / 2); + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + WriteBuffer(baseIoAddress1, + (PUSHORT) Srb->DataBuffer, + Srb->DataTransferLength / 2); + } + } + status = SRB_STATUS_SUCCESS; + } + + } else { // read task register + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, pIdeReg->bDriveHeadReg); + + pIdeReg = (PIDEREGS) Srb->DataBuffer; + pIdeReg->bDriveHeadReg = ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect); + pIdeReg->bFeaturesReg = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + pIdeReg->bSectorCountReg = ScsiPortReadPortUchar(&baseIoAddress1->BlockCount); + pIdeReg->bSectorNumberReg = ScsiPortReadPortUchar(&baseIoAddress1->BlockNumber); + pIdeReg->bCylLowReg = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + pIdeReg->bCylHighReg = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + pIdeReg->bCommandReg = ScsiPortReadPortUchar(&baseIoAddress1->Command); + status = SRB_STATUS_SUCCESS; + } + } + break; + + default: + + DebugPrint((1, + "IdeSendCommand: Unsupported command %x\n", + Srb->Cdb[0])); + + status = SRB_STATUS_INVALID_REQUEST; + + } // end switch + + return status; + +} // end IdeSendCommand() + +VOID +IdeMediaStatus( + BOOLEAN EnableMSN, + IN PVOID HwDeviceExtension, + ULONG Channel + ) +/*++ + +Routine Description: + + Enables disables media status notification + +Arguments: + +HwDeviceExtension - ATAPI driver storage. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress = deviceExtension->BaseIoAddress1[Channel >> 1]; + UCHAR statusByte,errorByte; + + + if (EnableMSN == TRUE){ + + // + // If supported enable Media Status Notification support + // + + if ((deviceExtension->DeviceFlags[Channel] & DFLAGS_REMOVABLE_DRIVE)) { + + // + // enable + // + ScsiPortWritePortUchar(&baseIoAddress->DriveSelect, + (UCHAR)(((Channel & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress + 1,(UCHAR) (0x95)); + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_ENABLE_MEDIA_STATUS); + + WaitOnBaseBusy(baseIoAddress,statusByte); + + if (statusByte & IDE_STATUS_ERROR) { + // + // Read the error register. + // + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress + 1); + + DebugPrint((1, + "IdeMediaStatus: Error enabling media status. Status %x, error byte %x\n", + statusByte, + errorByte)); + } else { + deviceExtension->DeviceFlags[Channel] |= DFLAGS_MEDIA_STATUS_ENABLED; + DebugPrint((1,"IdeMediaStatus: Media Status Notification Supported\n")); + deviceExtension->ReturningMediaStatus = 0; + + } + + } + } else { // end if EnableMSN == TRUE + + // + // disable if previously enabled + // + if ((deviceExtension->DeviceFlags[Channel] & DFLAGS_MEDIA_STATUS_ENABLED)) { + + ScsiPortWritePortUchar(&baseIoAddress->DriveSelect, + (UCHAR)(((Channel & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress + 1,(UCHAR) (0x31)); + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_ENABLE_MEDIA_STATUS); + + WaitOnBaseBusy(baseIoAddress,statusByte); + deviceExtension->DeviceFlags[Channel] &= ~DFLAGS_MEDIA_STATUS_ENABLED; + } + + + } + + + +} + +ULONG +IdeBuildSenseBuffer( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Builts an artificial sense buffer to report the results of a GET_MEDIA_STATUS + command. This function is invoked to satisfy the SCSIOP_REQUEST_SENSE. +Arguments: + + HwDeviceExtension - ATAPI driver storage. + Srb - System request block. + +Return Value: + + SRB status (ALWAYS SUCCESS). + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG status; + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->DataBuffer; + + + if (senseBuffer){ + + + if(deviceExtension->ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE_REQ) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_END_OF_MEDIA) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_NOT_READY; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_DATA_ERROR) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_DATA_PROTECT; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } + return SRB_STATUS_SUCCESS; + } + return SRB_STATUS_ERROR; + +}// End of IdeBuildSenseBuffer + + + + +BOOLEAN +AtapiStartIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine is called from the SCSI port driver synchronized + with the kernel to start an IO request. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + TRUE + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG status; + + // + // Determine which function. + // + + switch (Srb->Function) { + + case SRB_FUNCTION_EXECUTE_SCSI: + + // + // Sanity check. Only one request can be outstanding on a + // controller. + // + + if (deviceExtension->CurrentSrb) { + + DebugPrint((1, + "AtapiStartIo: Already have a request!\n")); + Srb->SrbStatus = SRB_STATUS_BUSY; + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + return FALSE; + } + + // + // Indicate that a request is active on the controller. + // + + deviceExtension->CurrentSrb = Srb; + + // + // Send command to device. + // + + // ATA_PASSTHORUGH + if (Srb->Cdb[0] == SCSIOP_ATA_PASSTHROUGH) { + + status = IdeSendCommand(HwDeviceExtension, + Srb); + + } else if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + status = AtapiSendCommand(HwDeviceExtension, + Srb); + + } else if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) { + + status = IdeSendCommand(HwDeviceExtension, + Srb); + } else { + + status = SRB_STATUS_SELECTION_TIMEOUT; + } + + break; + + case SRB_FUNCTION_ABORT_COMMAND: + + // + // Verify that SRB to abort is still outstanding. + // + + if (!deviceExtension->CurrentSrb) { + + DebugPrint((1, "AtapiStartIo: SRB to abort already completed\n")); + + // + // Complete abort SRB. + // + + status = SRB_STATUS_ABORT_FAILED; + + break; + } + + // + // Abort function indicates that a request timed out. + // Call reset routine. Card will only be reset if + // status indicates something is wrong. + // Fall through to reset code. + // + + case SRB_FUNCTION_RESET_BUS: + + // + // Reset Atapi and SCSI bus. + // + + DebugPrint((1, "AtapiStartIo: Reset bus request received\n")); + + if (!AtapiResetController(deviceExtension, + Srb->PathId)) { + + DebugPrint((1,"AtapiStartIo: Reset bus failed\n")); + + // + // Log reset failure. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + 0, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 5 << 8 + ); + + status = SRB_STATUS_ERROR; + + } else { + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SRB_FUNCTION_IO_CONTROL: + + if (deviceExtension->CurrentSrb) { + + DebugPrint((1, + "AtapiStartIo: Already have a request!\n")); + Srb->SrbStatus = SRB_STATUS_BUSY; + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + return FALSE; + } + + // + // Indicate that a request is active on the controller. + // + + deviceExtension->CurrentSrb = Srb; + + if (AtapiStringCmp( ((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature,"SCSIDISK",strlen("SCSIDISK"))) { + + DebugPrint((1, + "AtapiStartIo: IoControl signature incorrect. Send %s, expected %s\n", + ((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature, + "SCSIDISK")); + + status = SRB_STATUS_INVALID_REQUEST; + break; + } + + switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) { + + case IOCTL_SCSI_MINIPORT_SMART_VERSION: { + + PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + UCHAR deviceNumber; + + // + // Version and revision per SMART 1.03 + // + + versionParameters->bVersion = 1; + versionParameters->bRevision = 1; + versionParameters->bReserved = 0; + + // + // Indicate that support for IDE IDENTIFY, ATAPI IDENTIFY and SMART commands. + // + + versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD); + + // + // This is done because of how the IOCTL_SCSI_MINIPORT + // determines 'targetid's'. Disk.sys places the real target id value + // in the DeviceMap field. Once we do some parameter checking, the value passed + // back to the application will be determined. + // + + deviceNumber = versionParameters->bIDEDeviceMap; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) || + (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE)) { + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + } + + // + // NOTE: This will only set the bit + // corresponding to this drive's target id. + // The bit mask is as follows: + // + // Sec Pri + // S M S M + // 3 2 1 0 + // + + if (deviceExtension->NumberChannels == 1) { + if (deviceExtension->PrimaryAddress) { + deviceNumber = 1 << Srb->TargetId; + } else { + deviceNumber = 4 << Srb->TargetId; + } + } else { + deviceNumber = 1 << Srb->TargetId; + } + + versionParameters->bIDEDeviceMap = deviceNumber; + + status = SRB_STATUS_SUCCESS; + break; + } + + case IOCTL_SCSI_MINIPORT_IDENTIFY: { + + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + ULONG i; + UCHAR targetId; + + + if (cmdInParameters.irDriveRegs.bCommandReg == ID_CMD) { + + // + // Extract the target. + // + + targetId = cmdInParameters.bDriveNumber; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) || + (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE)) { + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + } + + // + // Zero the output buffer + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Build status block. + // + + cmdOutParameters->cBufferSize = IDENTIFY_BUFFER_SIZE; + cmdOutParameters->DriverStatus.bDriverError = 0; + cmdOutParameters->DriverStatus.bIDEError = 0; + + // + // Extract the identify data from the device extension. + // + + ScsiPortMoveMemory (cmdOutParameters->bBuffer, &deviceExtension->IdentifyData[targetId], IDENTIFY_DATA_SIZE); + + status = SRB_STATUS_SUCCESS; + + + } else { + status = SRB_STATUS_INVALID_REQUEST; + } + break; + } + + case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS: + case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS: + case IOCTL_SCSI_MINIPORT_ENABLE_SMART: + case IOCTL_SCSI_MINIPORT_DISABLE_SMART: + case IOCTL_SCSI_MINIPORT_RETURN_STATUS: + case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE: + case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES: + case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS: + + status = IdeSendSmartCommand(HwDeviceExtension,Srb); + break; + + default : + + status = SRB_STATUS_INVALID_REQUEST; + break; + + } + + break; + + default: + + // + // Indicate unsupported command. + // + + status = SRB_STATUS_INVALID_REQUEST; + + break; + + } // end switch + + // + // Check if command complete. + // + + if (status != SRB_STATUS_PENDING) { + + DebugPrint((2, + "AtapiStartIo: Srb %x complete with status %x\n", + Srb, + status)); + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + // + // Set status in SRB. + // + + Srb->SrbStatus = (UCHAR)status; + + // + // Indicate command complete. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + + // + // Indicate ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } + + return TRUE; + +} // end AtapiStartIo() + + +ULONG +DriverEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + Installable driver initialization entry point for system. + +Arguments: + + Driver Object + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; +// ULONG adapterCount; + ULONG i; + ULONG statusToReturn, newStatus; + FIND_STATE findState; + ULONG AdapterAddresses[5] = {0x1F0, 0x170, 0x1e8, 0x168, 0}; + ULONG InterruptLevels[5] = { 14, 15, 11, 10, 0}; + BOOLEAN IoAddressUsed[5] = {FALSE, FALSE, FALSE, FALSE, 0}; + + DebugPrint((1,"\n\nATAPI IDE MiniPort Driver\n")); + + DebugPrintTickCount(); + + statusToReturn = 0xffffffff; + + // + // Zero out structure. + // + + AtapiZeroMemory(((PUCHAR)&hwInitializationData), sizeof(HW_INITIALIZATION_DATA)); + + AtapiZeroMemory(((PUCHAR)&findState), sizeof(FIND_STATE)); + + + findState.DefaultIoPort = AdapterAddresses; + findState.DefaultInterrupt = InterruptLevels; + findState.IoAddressUsed = IoAddressUsed; + + // + // Set size of hwInitializationData. + // + + hwInitializationData.HwInitializationDataSize = + sizeof(HW_INITIALIZATION_DATA); + + // + // Set entry points. + // + + hwInitializationData.HwInitialize = AtapiHwInitialize; + hwInitializationData.HwResetBus = AtapiResetController; + hwInitializationData.HwStartIo = AtapiStartIo; + hwInitializationData.HwInterrupt = AtapiInterrupt; + + // + // Specify size of extensions. + // + + hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION); + hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LU_EXTENSION); + + hwInitializationData.NumberOfAccessRanges = 6; + hwInitializationData.HwFindAdapter = AtapiFindController; + + hwInitializationData.AdapterInterfaceType = Isa; + findState.ControllerParameters = PciControllerParameters; + + newStatus = ScsiPortInitialize(DriverObject, + Argument2, + &hwInitializationData, + &findState); + if (newStatus < statusToReturn) + statusToReturn = newStatus; + + + // + // Set up for MCA + // + + hwInitializationData.AdapterInterfaceType = MicroChannel; + + newStatus = ScsiPortInitialize(DriverObject, + Argument2, + &hwInitializationData, + &findState); + if (newStatus < statusToReturn) + statusToReturn = newStatus; + + DebugPrintTickCount(); + + return statusToReturn; + +} // end DriverEntry() + + + +LONG +AtapiStringCmp ( + PCHAR FirstStr, + PCHAR SecondStr, + ULONG Count + ) +{ + UCHAR first ,last; + + if (Count) { + do { + + // + // Get next char. + // + + first = *FirstStr++; + last = *SecondStr++; + + if (first != last) { + + // + // If no match, try lower-casing. + // + + if (first>='A' && first<='Z') { + first = first - 'A' + 'a'; + } + if (last>='A' && last<='Z') { + last = last - 'A' + 'a'; + } + if (first != last) { + + // + // No match + // + + return first - last; + } + } + }while (--Count && first); + } + + return 0; +} + + +VOID +AtapiZeroMemory( + IN PCHAR Buffer, + IN ULONG Count + ) +{ + ULONG i; + + for (i = 0; i < Count; i++) { + Buffer[i] = 0; + } +} + + +VOID +AtapiHexToString ( + IN ULONG Value, + IN OUT PCHAR *Buffer + ) +{ + PCHAR string; + PCHAR firstdig; + CHAR temp; + ULONG i; + USHORT digval; + + string = *Buffer; + + firstdig = string; + + for (i = 0; i < 4; i++) { + digval = (USHORT)(Value % 16); + Value /= 16; + + // + // convert to ascii and store. Note this will create + // the buffer with the digits reversed. + // + + if (digval > 9) { + *string++ = (char) (digval - 10 + 'a'); + } else { + *string++ = (char) (digval + '0'); + } + + } + + // + // Reverse the digits. + // + + *string-- = '\0'; + + do { + temp = *string; + *string = *firstdig; + *firstdig = temp; + --string; + ++firstdig; + } while (firstdig < string); +} + + + +PSCSI_REQUEST_BLOCK +BuildMechanismStatusSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + + srb = &deviceExtension->InternalSrb; + + AtapiZeroMemory((PUCHAR) srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->PathId = (UCHAR) PathId; + srb->TargetId = (UCHAR) TargetId; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set flags to disable synchronous negociation. + // + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set timeout to 2 seconds. + // + srb->TimeOutValue = 4; + + srb->CdbLength = 6; + srb->DataBuffer = &deviceExtension->MechStatusData; + srb->DataTransferLength = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER); + + // + // Set CDB operation code. + // + cdb = (PCDB)srb->Cdb; + cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS; + cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER); + + return srb; +} + + +PSCSI_REQUEST_BLOCK +BuildRequestSenseSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + + srb = &deviceExtension->InternalSrb; + + AtapiZeroMemory((PUCHAR) srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->PathId = (UCHAR) PathId; + srb->TargetId = (UCHAR) TargetId; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set flags to disable synchronous negociation. + // + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set timeout to 2 seconds. + // + srb->TimeOutValue = 4; + + srb->CdbLength = 6; + srb->DataBuffer = &deviceExtension->MechStatusSense; + srb->DataTransferLength = sizeof(SENSE_DATA); + + // + // Set CDB operation code. + // + cdb = (PCDB)srb->Cdb; + cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; + cdb->CDB6INQUIRY.AllocationLength = sizeof(SENSE_DATA); + + return srb; +} + + +BOOLEAN +PrepareForBusMastering( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) +/*++ + +Routine Description: + + Get ready for IDE bus mastering + + init. PDRT + init. bus master controller but keep it disabled + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - scsi request block + +Return Value: + + TRUE if successful + FALSE if failed + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + SCSI_PHYSICAL_ADDRESS physAddr; + ULONG bytesMapped; + ULONG bytes; + PUCHAR buffer; + PPHYSICAL_REGION_DESCRIPTOR physAddrTablePtr; + ULONG physAddrTableIndex; + PIDE_BUS_MASTER_REGISTERS busMasterBase; + + busMasterBase = deviceExtension->BusMasterPortBase[Srb->TargetId >> 1]; + + buffer = Srb->DataBuffer; + physAddrTablePtr = deviceExtension->DataBufferDescriptionTablePtr; + physAddrTableIndex = 0; + bytesMapped = 0; + DebugPrint ((2, "ATAPI: Mapping 0x%x bytes\n", Srb->DataTransferLength)); + + // + // PDRT has these limitation + // each entry maps up to is 64K bytes + // each physical block mapped cannot cross 64K page boundary + // + while (bytesMapped < Srb->DataTransferLength) { + ULONG bytesLeft; + ULONG nextPhysicalAddr; + ULONG bytesLeftInCurrent64KPage; + + physAddr = ScsiPortGetPhysicalAddress(HwDeviceExtension, + Srb, + buffer, + &bytes); + + bytesLeft = bytes; + nextPhysicalAddr = ScsiPortConvertPhysicalAddressToUlong(physAddr); + while (bytesLeft > 0) { + physAddrTablePtr[physAddrTableIndex].PhyscialAddress = nextPhysicalAddr; + + bytesLeftInCurrent64KPage = (0x10000 - (nextPhysicalAddr & 0xffff)); + + if (bytesLeftInCurrent64KPage < bytesLeft) { + + // + // Are we crossing 64K page + // got to break it up. Map up to the 64k boundary + // + physAddrTablePtr[physAddrTableIndex].ByteCount = bytesLeftInCurrent64KPage; + bytesLeft -= bytesLeftInCurrent64KPage; + nextPhysicalAddr += bytesLeftInCurrent64KPage; + DebugPrint ((3, "PrepareForBusMastering: buffer crossing 64K Page!\n")); + + } else if (bytesLeft <= 0x10000) { + // + // got a perfect page, map all of it + // + physAddrTablePtr[physAddrTableIndex].ByteCount = bytesLeft & 0xfffe; + bytesLeft = 0; + nextPhysicalAddr += bytesLeft; + + } else { + // + // got a perfectly aligned 64k page, map all of it but the count + // need to be 0 + // + physAddrTablePtr[physAddrTableIndex].ByteCount = 0; // 64K + bytesLeft -= 0x10000; + nextPhysicalAddr += 0x10000; + } + physAddrTablePtr[physAddrTableIndex].EndOfTable = 0; // not end of table + physAddrTableIndex++; + } + bytesMapped += bytes; + buffer += bytes; + } + + // + // the bus master circutry need to know it hits the end of the PRDT + // + physAddrTablePtr[physAddrTableIndex - 1].EndOfTable = 1; // end of table + + // + // init bus master contoller, but keep it disabled + // + ScsiPortWritePortUchar (&busMasterBase->Command, 0); // disable BM + ScsiPortWritePortUchar (&busMasterBase->Status, 0x6); // clear errors + ScsiPortWritePortUlong (&busMasterBase->DescriptionTable, + ScsiPortConvertPhysicalAddressToUlong(deviceExtension->DataBufferDescriptionTablePhysAddr)); + + return TRUE; +} + + +BOOLEAN +EnableBusMastering( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) +/*++ + +Routine Description: + + Enable bus mastering contoller + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - scsi request block + +Return Value: + + always TRUE + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_BUS_MASTER_REGISTERS busMasterBase; + UCHAR bmStatus = 0; + + busMasterBase = deviceExtension->BusMasterPortBase[Srb->TargetId >> 1]; + + deviceExtension->DMAInProgress = TRUE; + + // + // inidcate we are doing DMA + // + if (Srb->TargetId == 0) + bmStatus = BUSMASTER_DEVICE0_DMA_OK; + else + bmStatus = BUSMASTER_DEVICE1_DMA_OK; + + // + // clear the status bit + // + bmStatus |= BUSMASTER_INTERRUPT | BUSMASTER_ERROR; + + ScsiPortWritePortUchar (&busMasterBase->Status, bmStatus); + + // + // on your mark...get set...go!! + // + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ScsiPortWritePortUchar (&busMasterBase->Command, 0x09); // enable BM read + } else { + ScsiPortWritePortUchar (&busMasterBase->Command, 0x01); // enable BM write + } + + DebugPrint ((2, "ATAPI: BusMaster Status = 0x%x\n", ScsiPortReadPortUchar (&busMasterBase->Status))); + + return TRUE; +} + + + +ULONG +GetPciBusData( + IN PVOID HwDeviceExtension, + IN ULONG SystemIoBusNumber, + IN PCI_SLOT_NUMBER SlotNumber, + OUT PVOID PciConfigBuffer, + IN ULONG NumByte + ) +/*++ + +Routine Description: + + read PCI bus data + + we can't always use ScsiPortSetBusDataByOffset directly because many Intel PIIXs + are "hidden" from the function. The PIIX is usually the second + function of some other pci device (PCI-ISA bridge). However, the + mulit-function bit of the PCI-Isa bridge is not set. ScsiportGetBusData + will not be able to find it. + + This function will try to figure out if we have the "bad" PCI-ISA bridge, + and read the PIIX PCI space directly if necessary + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + SystemIoBusNumber - bus number + SlotNumber - pci slot and function numbers + PciConfigBuffer = pci data pointer + +Return Value: + + byte returned + +--*/ +{ + ULONG byteRead; + PULONG pciAddrReg; + PULONG pciDataReg; + ULONG i; + ULONG j; + ULONG data; + PULONG dataBuffer; + + USHORT vendorId; + USHORT deviceId; + UCHAR headerType; + + // + // If we have a hidden PIIX, it is always a function 1 of + // some device (PCI-ISA bridge (0x8086\0x122e) + // If we are NOT looking at function 1, skip the extra work + // + if (!AtapiPlaySafe && (SlotNumber.u.bits.FunctionNumber == 1)) { + + pciAddrReg = (PULONG) ScsiPortGetDeviceBase(HwDeviceExtension, + PCIBus, + 0, + ScsiPortConvertUlongToPhysicalAddress(PCI_ADDR_PORT), + 4, + TRUE); + pciDataReg = (PULONG) ScsiPortGetDeviceBase(HwDeviceExtension, + PCIBus, + 0, + ScsiPortConvertUlongToPhysicalAddress(PCI_DATA_PORT), + 4, + TRUE); + } else { + pciAddrReg = pciDataReg = NULL; + } + + if (pciAddrReg && pciDataReg) { + // + // get the vendor id and device id of the previous function + // + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber - 1, // looking at last function + 0)); + data = ScsiPortReadPortUlong(pciDataReg); + vendorId = (USHORT) ((data >> 0) & 0xffff); + deviceId = (USHORT) ((data >> 16) & 0xffff); + + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber - 1, // looking at last function + 3)); + data = ScsiPortReadPortUlong(pciDataReg); + headerType = (UCHAR) ((data >> 16) & 0xff); + + } else { + vendorId = PCI_INVALID_VENDORID; + } + + // + // The hidden PIIX is the pci function after the PCI-ISA bridge + // When it is hidden, the PCI-ISA bridge PCI_MULTIFUNCTION bit is not set + // + byteRead = 0; + if ((vendorId == 0x8086) && // Intel + (deviceId == 0x122e) && // PCI-ISA Bridge + !(headerType & PCI_MULTIFUNCTION)) { + + DebugPrint ((1, "ATAPI: found the hidden PIIX\n")); + + if (pciDataReg && pciAddrReg) { + + for (i=0, dataBuffer = (PULONG) PciConfigBuffer; + i < NumByte / 4; + i++, dataBuffer++) { + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber, + i)); + dataBuffer[0] = ScsiPortReadPortUlong(pciDataReg); + } + + if (NumByte % 4) { + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber, + i)); + data = ScsiPortReadPortUlong(pciDataReg); + + for (j=0; j <NumByte%4; j++) { + ((PUCHAR)dataBuffer)[j] = (UCHAR) (data & 0xff); + data = data >> 8; + } + } + byteRead = NumByte; + } + + if ((((PPCI_COMMON_CONFIG)PciConfigBuffer)->VendorID != 0x8086) || // Intel + (((PPCI_COMMON_CONFIG)PciConfigBuffer)->DeviceID != 0x1230)) { // PIIX + + // + // If the hidden device is not Intel PIIX, don't + // show it + // + byteRead = 0; + } else { + DebugPrint ((0, "If we play safe, we would NOT detect hidden PIIX controller\n")); + } + + } + + if (!byteRead) { + // + // Didn't find any hidden PIIX. Get the PCI + // data via the normal call (ScsiPortGetBusData) + // + byteRead = ScsiPortGetBusData(HwDeviceExtension, + PCIConfiguration, + SystemIoBusNumber, + SlotNumber.u.AsULONG, + PciConfigBuffer, + NumByte); + } + + if (pciAddrReg) + ScsiPortFreeDeviceBase(HwDeviceExtension, pciAddrReg); + if (pciDataReg) + ScsiPortFreeDeviceBase(HwDeviceExtension, pciDataReg); + + return byteRead; +} + + +ULONG +SetPciBusData( + IN PVOID HwDeviceExtension, + IN ULONG SystemIoBusNumber, + IN PCI_SLOT_NUMBER SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ) +/*++ + +Routine Description: + + set PCI bus data + + we can't always use ScsiPortSetBusDataByOffset directly because many Intel PIIXs + are "hidden" from the function. The PIIX is usually the second + function of some other pci device (PCI-ISA bridge). However, the + mulit-function bit of the PCI-Isa bridge is not set. ScsiPortSetBusDataByOffset + will not be able to find it. + + This function will try to figure out if we have the "bad" PCI-ISA bridge, + and write to the PIIX PCI space directly if necessary + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + SystemIoBusNumber - bus number + SlotNumber - pci slot and function numbers + Buffer - pci data buffer + Offset - byte offset into the pci space + Length - number of bytes to write + +Return Value: + + byte written + +--*/ +{ + ULONG byteWritten; + PULONG pciAddrReg; + PULONG pciDataReg; + ULONG i; + ULONG j; + ULONG data; + PULONG dataBuffer; + + USHORT vendorId; + USHORT deviceId; + UCHAR headerType; + + // + // If we have a hidden PIIX, it is always a function 1 of + // some device (PCI-ISA bridge (0x8086\0x122e) + // If we are NOT looking at function 1, skip the extra work + // + if (!AtapiPlaySafe && (SlotNumber.u.bits.FunctionNumber == 1)) { + + pciAddrReg = (PULONG) ScsiPortGetDeviceBase(HwDeviceExtension, + PCIBus, + 0, + ScsiPortConvertUlongToPhysicalAddress(PCI_ADDR_PORT), + 4, + TRUE); + pciDataReg = (PULONG) ScsiPortGetDeviceBase(HwDeviceExtension, + PCIBus, + 0, + ScsiPortConvertUlongToPhysicalAddress(PCI_DATA_PORT), + 4, + TRUE); + } else { + pciAddrReg = pciDataReg = NULL; + } + + if (pciAddrReg && pciDataReg) { + // + // get the vendor id and device id of the previous function + // + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber - 1, // looking at last function + 0)); + data = ScsiPortReadPortUlong(pciDataReg); + vendorId = (USHORT) ((data >> 0) & 0xffff); + deviceId = (USHORT) ((data >> 16) & 0xffff); + + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber - 1, // looking at last function + 3)); + data = ScsiPortReadPortUlong(pciDataReg); + headerType = (UCHAR) ((data >> 16) & 0xff); + + } else { + vendorId = PCI_INVALID_VENDORID; + } + + // + // The hidden PIIX is the pci function after the PCI-ISA bridge + // When it is hidden, the PCI-ISA bridge PCI_MULTIFUNCTION bit is not set + // + byteWritten = 0; + if ((vendorId == 0x8086) && // Intel + (deviceId == 0x122e) && // PCI-ISA Bridge + !(headerType & PCI_MULTIFUNCTION)) { + + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber, + 0)); + data = ScsiPortReadPortUlong(pciDataReg); + vendorId = (USHORT) ((data >> 0) & 0xffff); + deviceId = (USHORT) ((data >> 16) & 0xffff); + + if ((vendorId == 0x8086) && // Intel + (deviceId == 0x1230)) { // PIIX + + PCI_COMMON_CONFIG pciData; + + // + // read the same range of data in first + // + for (i=0, dataBuffer = (((PULONG) &pciData) + Offset/4); + i<(Length+3)/4; + i++, dataBuffer++) { + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber, + i + Offset/4)); + data = ScsiPortReadPortUlong(pciDataReg); + if (i < (sizeof(Length)/4)) { + dataBuffer[0] = data; + } else { + for (j=0; j <sizeof(Length)%4; j++) { + ((PUCHAR)dataBuffer)[j] = (UCHAR) (data & 0xff); + data = data >> 8; + } + } + } + + // + // Copy the new data over + // + for (i = 0; i<Length; i++) { + ((PUCHAR)&pciData)[i + Offset] = ((PUCHAR)Buffer)[i]; + } + + // + // write out the same range of data + // + for (i=0, dataBuffer = (((PULONG) &pciData) + Offset/4); + i<(Length+3)/4; + i++, dataBuffer++) { + ScsiPortWritePortUlong(pciAddrReg, PCI_ADDRESS(SystemIoBusNumber, + SlotNumber.u.bits.DeviceNumber, + SlotNumber.u.bits.FunctionNumber, + i + Offset/4)); + ScsiPortWritePortUlong(pciDataReg, dataBuffer[0]); + } + + byteWritten = Length; + + } else { + + // If the hidden device is not Intel PIIX, don't + // write to it + byteWritten = 0; + } + + } + + if (!byteWritten) { + // + // Didn't find any hidden PIIX. Write to the PCI + // space via the normal call (ScsiPortSetBusDataByOffset) + // + byteWritten = ScsiPortSetBusDataByOffset(HwDeviceExtension, + PCIConfiguration, + SystemIoBusNumber, + SlotNumber.u.AsULONG, + Buffer, + Offset, + Length); + } + + if (pciAddrReg) + ScsiPortFreeDeviceBase(HwDeviceExtension, pciAddrReg); + if (pciDataReg) + ScsiPortFreeDeviceBase(HwDeviceExtension, pciDataReg); + + return byteWritten; +} + +BOOLEAN +ChannelIsAlwaysEnabled ( + PPCI_COMMON_CONFIG PciData, + ULONG Channel) +/*++ + +Routine Description: + + dummy routine that always returns TRUE + +Arguments: + + PPCI_COMMON_CONFIG - pci config data + Channel - ide channel number + +Return Value: + + TRUE + +--*/ +{ + return TRUE; +} + +VOID +SetBusMasterDetectionLevel ( + IN PVOID HwDeviceExtension, + IN PCHAR userArgumentString + ) +/*++ + +Routine Description: + + check whether we should try to enable bus mastering + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + ArgumentString - register arguments + +Return Value: + + TRUE + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + BOOLEAN useBM; + + ULONG pciBusNumber; + PCI_SLOT_NUMBER pciSlot; + ULONG slotNumber; + ULONG logicalDeviceNumber; + PCI_COMMON_CONFIG pciData; + ULONG DMADetectionLevel; + + useBM = TRUE; + + DMADetectionLevel = AtapiParseArgumentString(userArgumentString, "DMADetectionLevel"); + if (DMADetectionLevel == DMADETECT_SAFE) { + AtapiPlaySafe = TRUE; + } else if (DMADetectionLevel == DMADETECT_UNSAFE) { + AtapiPlaySafe = FALSE; + } else { // default is no busmastering + useBM = FALSE; + } + + + // + // search for bad chip set + // + for (pciBusNumber=0; + pciBusNumber < 256 && useBM; + pciBusNumber++) { + + pciSlot.u.AsULONG = 0; + + for (slotNumber=0; + slotNumber < PCI_MAX_DEVICES && useBM; + slotNumber++) { + + pciSlot.u.bits.DeviceNumber = slotNumber; + + for (logicalDeviceNumber=0; + logicalDeviceNumber < PCI_MAX_FUNCTION && useBM; + logicalDeviceNumber++) { + + pciSlot.u.bits.FunctionNumber = logicalDeviceNumber; + + if (!GetPciBusData(HwDeviceExtension, + pciBusNumber, + pciSlot, + &pciData, + offsetof (PCI_COMMON_CONFIG, DeviceSpecific) + )) { + break; + } + + if (pciData.VendorID == PCI_INVALID_VENDORID) { + break; + } + + if ((pciData.VendorID == 0x8086) && // Intel + (pciData.DeviceID == 0x84c4) && // 82450GX/KX Pentium Pro Processor to PCI bridge + (pciData.RevisionID < 0x4)) { // Stepping less than 4 + + DebugPrint((1, + "atapi: Find a bad Intel processor-pci bridge. Disable PCI IDE busmastering...\n")); + useBM = FALSE; + } + } + } + } + + deviceExtension->UseBusMasterController = useBM; + DebugPrint ((0, "ATAPI: UseBusMasterController = %d\n", deviceExtension->UseBusMasterController)); + + if (deviceExtension->UseBusMasterController) { + DebugPrint ((0, "ATAPI: AtapiPlaySafe = %d\n", AtapiPlaySafe)); + } + + return; +} + + + +UCHAR PioDeviceModelNumber[][41] = { + {" Conner Peripherals 425MB - CFS425A "}, + {"MATSHITA CR-581 "}, + {"FX600S "}, + {"CD-44E "}, + {"QUANTUM TRB850A "}, + {"QUANTUM MARVERICK 540A "}, + {" MAXTOR MXT-540 AT "}, + {"Maxtor 71260 AT "}, + {"Maxtor 7850 AV "}, + {"Maxtor 7540 AV "}, + {"Maxtor 7213 AT "}, + {"Maxtor 7345 "}, + {"Maxtor 7245 AT "}, + {"Maxtor 7245 "}, + {"Maxtor 7211AU "}, + {"Maxtor 7171 AT "} +}; +#define NUMBER_OF_PIO_DEVICES (sizeof(PioDeviceModelNumber) / (sizeof(UCHAR) * 41)) + +UCHAR SpecialWDDevicesFWVersion[][9] = { + {"14.04E28"}, + {"25.26H35"}, + {"26.27J38"}, + {"27.25C38"}, + {"27.25C39"} +}; +#define NUMBER_OF_SPECIAL_WD_DEVICES (sizeof(SpecialWDDevicesFWVersion) / (sizeof (UCHAR) * 9)) + +BOOLEAN +AtapiDeviceDMACapable ( + IN PVOID HwDeviceExtension, + IN ULONG deviceNumber + ) +/*++ + +Routine Description: + + check the given device whether it is on our bad device list (non dma device) + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + deviceNumber - device number + +Return Value: + + TRUE if dma capable + FALSE if not dma capable + +--*/ +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + UCHAR modelNumber[41]; + UCHAR firmwareVersion[9]; + ULONG i; + BOOLEAN turnOffDMA = FALSE; + PCI_SLOT_NUMBER pciSlot; + PCI_COMMON_CONFIG pciData; + + if (!(deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT)) { + return FALSE; + } + + for (i=0; i<40; i+=2) { + modelNumber[i + 0] = deviceExtension->IdentifyData[deviceNumber].ModelNumber[i + 1]; + modelNumber[i + 1] = deviceExtension->IdentifyData[deviceNumber].ModelNumber[i + 0]; + } + modelNumber[i] = 0; + + for (i=0; i<NUMBER_OF_PIO_DEVICES; i++) { + if (!AtapiStringCmp(modelNumber, PioDeviceModelNumber[i], 40)) { + + DebugPrint ((0, "ATAPI: device on the hall of shame list. no DMA!\n")); + + turnOffDMA = TRUE; + } + } + + // + // if we have a Western Digial device + // if the best dma mode is multi word dma mode 1 + // if the identify data word offset 129 is not 0x5555 + // turn off dma unless + // if the device firmware version is on the list and + // it is the only drive on the bus + // + if (!AtapiStringCmp(modelNumber, "WDC", 3)) { + if (deviceExtension->DeviceParameters[deviceNumber].BestMultiWordDMAMode == 1) { + + for (i=0; i<8; i+=2) { + firmwareVersion[i + 0] = deviceExtension->IdentifyData[deviceNumber].FirmwareRevision[i + 1]; + firmwareVersion[i + 1] = deviceExtension->IdentifyData[deviceNumber].FirmwareRevision[i + 0]; + } + firmwareVersion[i] = 0; + + // + // Check the special flag. If not found, can't use dma + // + if (*(((PUSHORT)&deviceExtension->IdentifyData[deviceNumber]) + 129) != 0x5555) { + + DebugPrint ((0, "ATAPI: found mode 1 WD drive. no dma unless it is the only device\n")); + + turnOffDMA = TRUE; + + for (i=0; i<NUMBER_OF_SPECIAL_WD_DEVICES; i++) { + + if (!AtapiStringCmp(firmwareVersion, SpecialWDDevicesFWVersion[i], 8)) { + + ULONG otherDeviceNumber; + + // + // 0 becomes 1 + // 1 becomes 0 + // 2 becomes 3 + // 3 becomes 2 + // + otherDeviceNumber = ((deviceNumber & 0x2) | ((deviceNumber & 0x1) ^ 1)); + + // + // if the device is alone on the bus, we can use dma + // + if (!(deviceExtension->DeviceFlags[otherDeviceNumber] & DFLAGS_DEVICE_PRESENT)) { + turnOffDMA = FALSE; + break; + } + } + } + } + } + } + + // + // ALi IDE controller cannot busmaster with an ATAPI device + // + pciSlot.u.AsULONG = 0; + pciSlot.u.bits.DeviceNumber = deviceExtension->PciDeviceNumber; + pciSlot.u.bits.FunctionNumber = deviceExtension->PciLogDevNumber; + + if (GetPciBusData(HwDeviceExtension, + deviceExtension->PciBusNumber, + pciSlot, + &pciData, + offsetof (PCI_COMMON_CONFIG, DeviceSpecific) + )) { + + if ((pciData.VendorID == 0x10b9) && + (pciData.DeviceID == 0x5219)) { + + if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) { + + DebugPrint ((0, "ATAPI: Can't do DMA because we have a ALi controller and a ATAPI device\n")); + + turnOffDMA = TRUE; + + + + } + } + } + + if (turnOffDMA) { + return FALSE; + } else { + return TRUE; + } +} + diff --git a/private/ntos/miniport/atapi/atapi.h b/private/ntos/miniport/atapi/atapi.h new file mode 100644 index 000000000..2364537b3 --- /dev/null +++ b/private/ntos/miniport/atapi/atapi.h @@ -0,0 +1,857 @@ +/*++ + +Copyright (c) 1993-1996 Microsoft Corporation + +Module Name: + + atapi.h + +Abstract: + + This module contains the structures and definitions for the ATAPI + IDE miniport driver. + +Author: + + Mike Glass + + +Revision History: + +--*/ + +#include "scsi.h" +#include "stdio.h" +#include "string.h" + +// +// Function Prototypes +// +ULONG +GetPciBusData( + IN PVOID DeviceExtension, + IN ULONG SystemIoBusNumber, + IN PCI_SLOT_NUMBER SlotNumber, + OUT PVOID PciConfigBuffer, + IN ULONG NumByte + ); + +ULONG +SetPciBusData( + IN PVOID HwDeviceExtension, + IN ULONG SystemIoBusNumber, + IN PCI_SLOT_NUMBER SlotNumber, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length + ); + +BOOLEAN +ChannelIsAlwaysEnabled ( + IN PPCI_COMMON_CONFIG PciData, + IN ULONG Channel + ); + +// +// IDE register definition +// + +typedef struct _IDE_REGISTERS_1 { + USHORT Data; + UCHAR BlockCount; + UCHAR BlockNumber; + UCHAR CylinderLow; + UCHAR CylinderHigh; + UCHAR DriveSelect; + UCHAR Command; +} IDE_REGISTERS_1, *PIDE_REGISTERS_1; + +typedef struct _IDE_REGISTERS_2 { + UCHAR DeviceControl; + UCHAR DriveAddress; +} IDE_REGISTERS_2, *PIDE_REGISTERS_2; + +typedef struct _IDE_REGISTERS_3 { + ULONG Data; + UCHAR Others[4]; +} IDE_REGISTERS_3, *PIDE_REGISTERS_3; + +// +// Bus Master Controller Register +// +typedef struct _IDE_BUS_MASTER_REGISTERS { + UCHAR Command; + UCHAR Reserved1; + UCHAR Status; + UCHAR Reserved2; + ULONG DescriptionTable; +} IDE_BUS_MASTER_REGISTERS, *PIDE_BUS_MASTER_REGISTERS; + +// +// Device Extension Device Flags +// + +#define DFLAGS_DEVICE_PRESENT (1 << 0) // Indicates that some device is present. +#define DFLAGS_ATAPI_DEVICE (1 << 1) // Indicates whether Atapi commands can be used. +#define DFLAGS_TAPE_DEVICE (1 << 2) // Indicates whether this is a tape device. +#define DFLAGS_INT_DRQ (1 << 3) // Indicates whether device interrupts as DRQ is set after + // receiving Atapi Packet Command +#define DFLAGS_REMOVABLE_DRIVE (1 << 4) // Indicates that the drive has the 'removable' bit set in + // identify data (offset 128) +#define DFLAGS_MEDIA_STATUS_ENABLED (1 << 5) // Media status notification enabled +#define DFLAGS_ATAPI_CHANGER (1 << 6) // Indicates atapi 2.5 changer present. +#define DFLAGS_SANYO_ATAPI_CHANGER (1 << 7) // Indicates multi-platter device, not conforming to the 2.5 spec. +#define DFLAGS_CHANGER_INITED (1 << 8) // Indicates that the init path for changers has already been done. +#define DFLAGS_USE_DMA (1 << 9) // Indicates whether device can use DMA +#define DFLAGS_LBA (1 << 10) // support LBA addressing + +// +// Controller Flags +// +#define CFLAGS_BUS_MASTERING (1 << 0) // The Controller is capable of doing bus mastering + // defined by SFF-8038i + +// +// Used to disable 'advanced' features. +// + +#define MAX_ERRORS 4 + +// +// ATAPI command definitions +// + +#define ATAPI_MODE_SENSE 0x5A +#define ATAPI_MODE_SELECT 0x55 +#define ATAPI_FORMAT_UNIT 0x24 + +// +// ATAPI Command Descriptor Block +// + +typedef struct _MODE_SENSE_10 { + UCHAR OperationCode; + UCHAR Reserved1; + UCHAR PageCode : 6; + UCHAR Pc : 2; + UCHAR Reserved2[4]; + UCHAR ParameterListLengthMsb; + UCHAR ParameterListLengthLsb; + UCHAR Reserved3[3]; +} MODE_SENSE_10, *PMODE_SENSE_10; + +typedef struct _MODE_SELECT_10 { + UCHAR OperationCode; + UCHAR Reserved1 : 4; + UCHAR PFBit : 1; + UCHAR Reserved2 : 3; + UCHAR Reserved3[5]; + UCHAR ParameterListLengthMsb; + UCHAR ParameterListLengthLsb; + UCHAR Reserved4[3]; +} MODE_SELECT_10, *PMODE_SELECT_10; + +typedef struct _MODE_PARAMETER_HEADER_10 { + UCHAR ModeDataLengthMsb; + UCHAR ModeDataLengthLsb; + UCHAR MediumType; + UCHAR Reserved[5]; +}MODE_PARAMETER_HEADER_10, *PMODE_PARAMETER_HEADER_10; + +// +// IDE command definitions +// + +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_DRIVE_PARAMETERS 0x91 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_COMMAND_READ_MULTIPLE 0xC4 +#define IDE_COMMAND_WRITE_MULTIPLE 0xC5 +#define IDE_COMMAND_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_GET_MEDIA_STATUS 0xDA +#define IDE_COMMAND_ENABLE_MEDIA_STATUS 0xEF +#define IDE_COMMAND_IDENTIFY 0xEC +#define IDE_COMMAND_MEDIA_EJECT 0xED +#define IDE_COMMAND_DOOR_LOCK 0xDE +#define IDE_COMMAND_DOOR_UNLOCK 0xDF + +// +// IDE status definitions +// + +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_IDLE 0x50 +#define IDE_STATUS_BUSY 0x80 + +// +// IDE drive select/head definitions +// + +#define IDE_DRIVE_SELECT_1 0xA0 +#define IDE_DRIVE_SELECT_2 0x10 + +// +// IDE drive control definitions +// + +#define IDE_DC_DISABLE_INTERRUPTS 0x02 +#define IDE_DC_RESET_CONTROLLER 0x04 +#define IDE_DC_REENABLE_CONTROLLER 0x00 + +// +// IDE error definitions +// + +#define IDE_ERROR_BAD_BLOCK 0x80 +#define IDE_ERROR_DATA_ERROR 0x40 +#define IDE_ERROR_MEDIA_CHANGE 0x20 +#define IDE_ERROR_ID_NOT_FOUND 0x10 +#define IDE_ERROR_MEDIA_CHANGE_REQ 0x08 +#define IDE_ERROR_COMMAND_ABORTED 0x04 +#define IDE_ERROR_END_OF_MEDIA 0x02 +#define IDE_ERROR_ILLEGAL_LENGTH 0x01 + +// +// ATAPI register definition +// + +typedef struct _ATAPI_REGISTERS_1 { + USHORT Data; + UCHAR InterruptReason; + UCHAR Unused1; + UCHAR ByteCountLow; + UCHAR ByteCountHigh; + UCHAR DriveSelect; + UCHAR Command; +} ATAPI_REGISTERS_1, *PATAPI_REGISTERS_1; + +typedef struct _ATAPI_REGISTERS_2 { + UCHAR DeviceControl; + UCHAR DriveAddress; +} ATAPI_REGISTERS_2, *PATAPI_REGISTERS_2; + + +// +// ATAPI interrupt reasons +// + +#define ATAPI_IR_COD 0x01 +#define ATAPI_IR_IO 0x02 + +// +// IDENTIFY data +// +#pragma pack (1) +typedef struct _IDENTIFY_DATA { + USHORT GeneralConfiguration; // 00 00 + USHORT NumberOfCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumberOfHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT SectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + USHORT SerialNumber[10]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + UCHAR FirmwareRevision[8]; // 2E 23-26 + UCHAR ModelNumber[40]; // 36 27-46 + UCHAR MaximumBlockTransfer; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Capabilities; // 62 49 + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:2; // 6A 53 + USHORT Reserved3:14; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport : 8; // 62 + USHORT SingleWordDMAActive : 8; + USHORT MultiWordDMASupport : 8; // 63 + USHORT MultiWordDMAActive : 8; + USHORT AdvancedPIOModes : 8; // 64 + USHORT Reserved4 : 8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[11]; // 69-79 + USHORT MajorRevision; // 80 + USHORT MinorRevision; // 81 + USHORT Reserved6[45]; // 82-126 + USHORT SpecialFunctionsEnabled; // 127 + USHORT Reserved7[128]; // 128-255 +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +// +// Identify data without the Reserved4. +// + +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 00 + USHORT NumberOfCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumberOfHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT SectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + USHORT SerialNumber[10]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + UCHAR FirmwareRevision[8]; // 2E 23-26 + UCHAR ModelNumber[40]; // 36 27-46 + UCHAR MaximumBlockTransfer; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Capabilities; // 62 49 + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:2; // 6A 53 + USHORT Reserved3:14; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport : 8; // 62 + USHORT SingleWordDMAActive : 8; + USHORT MultiWordDMASupport : 8; // 63 + USHORT MultiWordDMAActive : 8; + USHORT AdvancedPIOModes : 8; // 64 + USHORT Reserved4 : 8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[11]; // 69-79 + USHORT MajorRevision; // 80 + USHORT MinorRevision; // 81 + USHORT Reserved6[45]; // 82-126 + USHORT SpecialFunctionsEnabled; // 127 + USHORT Reserved7[2]; // 128-129 +} IDENTIFY_DATA2, *PIDENTIFY_DATA2; +#pragma pack () + +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + +// +// IDENTIFY capability bit definitions. +// + +#define IDENTIFY_CAPABILITIES_DMA_SUPPORTED (1 << 8) +#define IDENTIFY_CAPABILITIES_LBA_SUPPORTED (1 << 9) +#define IDENTIFY_CAPABILITIES_IOREADY_CAN_BE_DISABLED (1 << 10) +#define IDENTIFY_CAPABILITIES_IOREADY_SUPPORTED (1 << 11) + + +// +// Select LBA mode when progran IDE device +// +#define IDE_LBA_MODE (1 << 6) + +// +// Beautification macros +// + +#define GetStatus(BaseIoAddress, Status) \ + Status = ScsiPortReadPortUchar(&BaseIoAddress->Command); + +#define GetBaseStatus(BaseIoAddress, Status) \ + Status = ScsiPortReadPortUchar(&BaseIoAddress->Command); + +#define WriteCommand(BaseIoAddress, Command) \ + ScsiPortWritePortUchar(&BaseIoAddress->Command, Command); + + + +#define ReadBuffer(BaseIoAddress, Buffer, Count) \ + ScsiPortReadPortBufferUshort(&BaseIoAddress->Data, \ + Buffer, \ + Count); + +#define WriteBuffer(BaseIoAddress, Buffer, Count) \ + ScsiPortWritePortBufferUshort(&BaseIoAddress->Data, \ + Buffer, \ + Count); + +#define ReadBuffer2(BaseIoAddress, Buffer, Count) \ + ScsiPortReadPortBufferUlong(&BaseIoAddress->Data, \ + Buffer, \ + Count); + +#define WriteBuffer2(BaseIoAddress, Buffer, Count) \ + ScsiPortWritePortBufferUlong(&BaseIoAddress->Data, \ + Buffer, \ + Count); + +#define WaitOnBusy(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<20000; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(150); \ + continue; \ + } else { \ + break; \ + } \ + if (i == 20000) \ + DebugPrint ((0, "WaitOnBusy failed in %s line %u. status = 0x%x\n", __FILE__, __LINE__, (ULONG) (Status))); \ + } \ +} + +#define WaitOnBaseBusy(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<20000; i++) { \ + GetBaseStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(150); \ + continue; \ + } else { \ + break; \ + } \ + } \ +} + +#define WaitForDrq(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<1000; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(100); \ + } else if (Status & IDE_STATUS_DRQ) { \ + break; \ + } else { \ + ScsiPortStallExecution(200); \ + } \ + } \ +} + + +#define WaitShortForDrq(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<2; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(100); \ + } else if (Status & IDE_STATUS_DRQ) { \ + break; \ + } else { \ + ScsiPortStallExecution(100); \ + } \ + } \ +} + +#define AtapiSoftReset(BaseIoAddress, DeviceNumber, interruptOff) \ +{\ + ULONG i;\ + UCHAR statusByte; \ + DebugPrintTickCount(); \ + ScsiPortWritePortUchar(&BaseIoAddress->DriveSelect,(UCHAR)(((DeviceNumber & 0x1) << 4) | 0xA0)); \ + ScsiPortStallExecution(500);\ + ScsiPortWritePortUchar(&BaseIoAddress->Command, IDE_COMMAND_ATAPI_RESET); \ + for (i = 0; i < 1000; i++) { \ + ScsiPortStallExecution(999); \ + }\ + ScsiPortWritePortUchar(&BaseIoAddress->DriveSelect,(UCHAR)((DeviceNumber << 4) | 0xA0)); \ + WaitOnBusy(BaseIoAddress, statusByte); \ + ScsiPortStallExecution(500);\ + if (interruptOff) { \ + ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS); \ + } \ + DebugPrintTickCount(); \ +} + +#define IdeHardReset(BaseIoAddress1, BaseIoAddress2, result) \ +{\ + UCHAR statusByte;\ + ULONG i;\ + ScsiPortWritePortUchar(&BaseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER );\ + ScsiPortStallExecution(50 * 1000);\ + ScsiPortWritePortUchar(&BaseIoAddress2->DeviceControl,IDE_DC_REENABLE_CONTROLLER);\ + for (i = 0; i < 1000 * 1000; i++) {\ + statusByte = ScsiPortReadPortUchar(&BaseIoAddress1->Command);\ + if (statusByte != IDE_STATUS_IDLE && statusByte != 0x0) {\ + ScsiPortStallExecution(5);\ + } else {\ + break;\ + }\ + }\ + if (i == 1000*1000) {\ + result = FALSE;\ + }\ + result = TRUE;\ +} + +#define IS_RDP(OperationCode)\ + ((OperationCode == SCSIOP_ERASE)||\ + (OperationCode == SCSIOP_LOAD_UNLOAD)||\ + (OperationCode == SCSIOP_LOCATE)||\ + (OperationCode == SCSIOP_REWIND) ||\ + (OperationCode == SCSIOP_SPACE)||\ + (OperationCode == SCSIOP_SEEK)||\ + (OperationCode == SCSIOP_WRITE_FILEMARKS)) + +struct _CONTROLLER_PARAMETERS; + +// +// Keep trap off DriverEntry status +// +typedef struct _FIND_STATE { + + ULONG BusNumber; + ULONG SlotNumber; + ULONG LogicalDeviceNumber; + ULONG IdeChannel; + + PULONG DefaultIoPort; + PULONG DefaultInterrupt; + PBOOLEAN IoAddressUsed; + + struct _CONTROLLER_PARAMETERS * ControllerParameters; + +} FIND_STATE, * PFIND_STATE; + +// +// Bus Master Physical Region Descriptor +// +#pragma pack (1) +typedef struct _PHYSICAL_REGION_DESCRIPTOR { + ULONG PhyscialAddress; + ULONG ByteCount:16; + ULONG Reserved:15; + ULONG EndOfTable:1; +} PHYSICAL_REGION_DESCRIPTOR, * PPHYSICAL_REGION_DESCRIPTOR; +#pragma pack () + +#define MAX_TRANSFER_SIZE_PER_SRB (0x20000) + +#define MAX_DEVICE (2) +#define MAX_CHANNEL (2) + +// +// Device extension +// +typedef struct _HW_DEVICE_EXTENSION { + + // + // Current request on controller. + // + + PSCSI_REQUEST_BLOCK CurrentSrb; + + // + // Base register locations + // + + PIDE_REGISTERS_1 BaseIoAddress1[2]; + PIDE_REGISTERS_2 BaseIoAddress2[2]; + PIDE_BUS_MASTER_REGISTERS BusMasterPortBase[2]; + + // + // Interrupt level + // + + ULONG InterruptLevel; + + // + // Interrupt Mode (Level or Edge) + // + + ULONG InterruptMode; + + // + // Data buffer pointer. + // + + PUSHORT DataBuffer; + + // + // Data words left. + // + + ULONG WordsLeft; + + // + // Number of channels being supported by one instantiation + // of the device extension. Normally (and correctly) one, but + // with so many broken PCI IDE controllers being sold, we have + // to support them. + // + + ULONG NumberChannels; + + // + // Count of errors. Used to turn off features. + // + + ULONG ErrorCount; + + // + // Indicates number of platters on changer-ish devices. + // + + ULONG DiscsPresent[MAX_DEVICE * MAX_CHANNEL]; + + // + // Flags word for each possible device. + // + + USHORT DeviceFlags[MAX_DEVICE * MAX_CHANNEL]; + + // + // Indicates the number of blocks transferred per int. according to the + // identify data. + // + + UCHAR MaximumBlockXfer[MAX_DEVICE * MAX_CHANNEL]; + + // + // Indicates expecting an interrupt + // + + BOOLEAN ExpectingInterrupt; + + // + // Indicates DMA is in progress + // + + BOOLEAN DMAInProgress; + + + // + // Indicate last tape command was DSC Restrictive. + // + + BOOLEAN RDP; + + // + // Driver is being used by the crash dump utility or ntldr. + // + + BOOLEAN DriverMustPoll; + + // + // Indicates use of 32-bit PIO + // + + BOOLEAN DWordIO; + + // + // Indicates whether '0x1f0' is the base address. Used + // in SMART Ioctl calls. + // + + BOOLEAN PrimaryAddress; + + // + // Placeholder for the sub-command value of the last + // SMART command. + // + + UCHAR SmartCommand; + + // + // Placeholder for status register after a GET_MEDIA_STATUS command + // + + UCHAR ReturningMediaStatus; + + UCHAR Reserved[1]; + + // + // Mechanism Status Srb Data + // + PSCSI_REQUEST_BLOCK OriginalSrb; + SCSI_REQUEST_BLOCK InternalSrb; + MECHANICAL_STATUS_INFORMATION_HEADER MechStatusData; + SENSE_DATA MechStatusSense; + ULONG MechStatusRetryCount; + + // + // Identify data for device + // + IDENTIFY_DATA2 IdentifyData[MAX_DEVICE * MAX_CHANNEL]; + + // + // Bus Master Data + // + // Physcial Region Table for bus mastering + PPHYSICAL_REGION_DESCRIPTOR DataBufferDescriptionTablePtr; + ULONG DataBufferDescriptionTableSize; + PHYSICAL_ADDRESS DataBufferDescriptionTablePhysAddr; + + // + // Controller Flags + // + USHORT ControllerFlags; + + // + // Control whether we ship try to enable busmastering + // + BOOLEAN UseBusMasterController; + + // + // Function to set bus master timing + // + BOOLEAN (*BMTimingControl) (struct _HW_DEVICE_EXTENSION * DeviceExtension); + + // + // Function to set check whether a PCI IDE channel is enabled + // + BOOLEAN (*IsChannelEnabled) (PPCI_COMMON_CONFIG PciData, ULONG Channel); + + + // PCI Address + ULONG PciBusNumber; + ULONG PciDeviceNumber; + ULONG PciLogDevNumber; + + // + // Device Specific Info. + // + struct _DEVICE_PARAMETERS { + + ULONG MaxWordPerInterrupt; + + UCHAR IdeReadCommand; + UCHAR IdeWriteCommand; + + BOOLEAN IoReadyEnabled; + ULONG PioCycleTime; + ULONG DmaCycleTime; + + ULONG BestPIOMode; + ULONG BestSingleWordDMAMode; + ULONG BestMultiWordDMAMode; + + } DeviceParameters[MAX_CHANNEL * MAX_DEVICE]; + +} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION; + + +// +// PCI IDE Controller definition +// +typedef struct _CONTROLLER_PARAMETERS { + + INTERFACE_TYPE AdapterInterfaceType; + + PUCHAR VendorId; + USHORT VendorIdLength; + PUCHAR DeviceId; + USHORT DeviceIdLength; + + ULONG NumberOfIdeBus; + + BOOLEAN SingleFIFO; + + BOOLEAN (*TimingControl) (PHW_DEVICE_EXTENSION DeviceExtension); + + BOOLEAN (*IsChannelEnabled) (PPCI_COMMON_CONFIG PciData, ULONG Channel); + +} CONTROLLER_PARAMETERS, * PCONTROLLER_PARAMETERS; + +// +// max number of CHS addressable sectors +// +#define MAX_NUM_CHS_ADDRESSABLE_SECTORS ((ULONG) (16515072 - 1)) + + +// +// IDE Cycle Timing +// +#define PIO_MODE0_CYCLE_TIME 600 +#define PIO_MODE1_CYCLE_TIME 383 +#define PIO_MODE2_CYCLE_TIME 240 +#define PIO_MODE3_CYCLE_TIME 180 +#define PIO_MODE4_CYCLE_TIME 120 + +#define SWDMA_MODE0_CYCLE_TIME 960 +#define SWDMA_MODE1_CYCLE_TIME 480 +#define SWDMA_MODE2_CYCLE_TIME 240 + +#define MWDMA_MODE0_CYCLE_TIME 480 +#define MWDMA_MODE1_CYCLE_TIME 150 +#define MWDMA_MODE2_CYCLE_TIME 120 + +#define UNINITIALIZED_CYCLE_TIME 0xffffffff + +// +// invalid mode values +// +#define INVALID_PIO_MODE 0xffffffff +#define INVALID_SWDMA_MODE 0xffffffff +#define INVALID_MWDMA_MODE 0xffffffff + + +// +// Bus Master Status Register +// +#define BUSMASTER_DMA_SIMPLEX_BIT ((UCHAR) (1 << 7)) +#define BUSMASTER_DEVICE1_DMA_OK ((UCHAR) (1 << 6)) +#define BUSMASTER_DEVICE0_DMA_OK ((UCHAR) (1 << 5)) +#define BUSMASTER_INTERRUPT ((UCHAR) (1 << 2)) +#define BUSMASTER_ERROR ((UCHAR) (1 << 1)) +#define BUSMASTER_ACTIVE ((UCHAR) (1 << 0)) + + +// +// PCI access port +// +#define PCI_ADDR_PORT (0x0cf8) +#define PCI_DATA_PORT (0x0cfc) +#define PCI_ADDRESS(bus, deviceNum, funcNum, offset) \ + ((1 << 31) | \ + ((bus & 0xff) << 16) | \ + ((deviceNum & 0x1f) << 11) | \ + ((funcNum & 0x7) << 8) | \ + ((offset & 0x3f) << 2)) + +// BUG BUG BUG +// GET PAGE_SIZE into miniport.h +#ifdef ALPHA +#define PAGE_SIZE (ULONG)0x2000 +#else // MIPS, PPC, I386 +#define PAGE_SIZE (ULONG)0x1000 +#endif + +#define SCSIOP_ATA_PASSTHROUGH (0xcc) + +// +// valid DMA detection level +// +#define DMADETECT_PIO 0 +#define DMADETECT_SAFE 1 +#define DMADETECT_UNSAFE 2 diff --git a/private/ntos/miniport/atapi/atapi.rc b/private/ntos/miniport/atapi/atapi.rc new file mode 100644 index 000000000..ffb61dd1b --- /dev/null +++ b/private/ntos/miniport/atapi/atapi.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "ATAPI IDE Miniport Driver" +#define VER_INTERNALNAME_STR "atapi.sys" +#define VER_ORIGINALFILENAME_STR "atapi.sys" + +#include "common.ver" + diff --git a/private/ntos/miniport/atapi/intel.c b/private/ntos/miniport/atapi/intel.c new file mode 100644 index 000000000..7ce65ee8b --- /dev/null +++ b/private/ntos/miniport/atapi/intel.c @@ -0,0 +1,37 @@ +#include "miniport.h" +#include "atapi.h" +#include "ntdddisk.h" +#include "ntddscsi.h" +#include "intel.h" + +BOOLEAN +PiixTimingControl ( + struct _HW_DEVICE_EXTENSION DeviceExtension + ) +{ + + + + + return TRUE; +} + +BOOLEAN IntelIsChannelEnabled ( + PPCI_COMMON_CONFIG PciData, + ULONG Channel) +{ + PUCHAR rawPciData = (PUCHAR) PciData; + ULONG pciDataOffset; + + if (Channel == 0) { + pciDataOffset = 0x41; + } else { + pciDataOffset = 0x43; + } + + return (rawPciData[pciDataOffset] & 0x80); +} + + + + diff --git a/private/ntos/miniport/atapi/intel.h b/private/ntos/miniport/atapi/intel.h new file mode 100644 index 000000000..329220572 --- /dev/null +++ b/private/ntos/miniport/atapi/intel.h @@ -0,0 +1,10 @@ +BOOLEAN +PiixTimingControl ( + struct _HW_DEVICE_EXTENSION DeviceExtension + ); + +BOOLEAN IntelIsChannelEnabled ( + PPCI_COMMON_CONFIG PciData, + ULONG Channel); + + diff --git a/private/ntos/miniport/atapi/makefile b/private/ntos/miniport/atapi/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/miniport/atapi/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/atapi/sources b/private/ntos/miniport/atapi/sources new file mode 100644 index 000000000..833938ef4 --- /dev/null +++ b/private/ntos/miniport/atapi/sources @@ -0,0 +1,42 @@ +!IF 0 + +Copyright (c) 1991 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=atapi +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=MINIPORT + +INCLUDES=..\..\inc + +TARGETLIBS=\nt\public\sdk\lib\*\scsiport.lib +!IF $(ALPHA) +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib $(BASEDIR)\public\sdk\lib\*\libcntpr.lib +!ENDIF + +SOURCES=atapi.c \ + atapi.rc \ + intel.c + |