/*++ 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; iUseBusMasterController) { 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 > 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 > 8; } } } // // Copy the new data over // for (i = 0; i