/*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: atapinit.c Abstract: This contain routine to enumrate IDE devices on the IDE bus Author: Joe Dai (joedai) Environment: kernel mode only Notes: Revision History: --*/ #include "ideport.h" extern PULONG InitSafeBootMode; // imported from NTOS (init.c), must use a pointer to reference the data #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, IdePortInitHwDeviceExtension) #pragma alloc_text(PAGE, AtapiDetectDevice) #pragma alloc_text(PAGE, IdePreAllocEnumStructs) #pragma alloc_text(PAGE, IdeFreeEnumStructs) #pragma alloc_text(NONPAGE, AtapiSyncSelectTransferMode) #pragma alloc_text(PAGESCAN, AnalyzeDeviceCapabilities) #pragma alloc_text(PAGESCAN, AtapiDMACapable) #pragma alloc_text(PAGESCAN, IdePortSelectCHS) #pragma alloc_text(PAGESCAN, IdePortScanBus) LONG IdePAGESCANLockCount = 0; #endif // ALLOC_PRAGMA #ifdef IDE_MEASURE_BUSSCAN_SPEED static PWCHAR IdePortBootTimeRegKey[6]= { L"IdeBusResetTime", L"IdeEmptyChannelCheckTime", L"IdeDetectMasterDeviceTime", L"IdeDetectSlaveDeviceTime", L"IdeCriticalSectionTime", L"IdeLastStageScanTime" }; #endif static PWCHAR IdePortRegistryDeviceTimeout[MAX_IDE_DEVICE * MAX_IDE_LINE] = { MASTER_DEVICE_TIMEOUT, SLAVE_DEVICE_TIMEOUT }; static PWCHAR IdePortRegistryDeviceTypeName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { MASTER_DEVICE_TYPE_REG_KEY, SLAVE_DEVICE_TYPE_REG_KEY, MASTER_DEVICE_TYPE2_REG_KEY, SLAVE_DEVICE_TYPE2_REG_KEY }; static PWCHAR IdePortRegistryDeviceTimingModeName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { MASTER_DEVICE_TIMING_MODE, SLAVE_DEVICE_TIMING_MODE, MASTER_DEVICE_TIMING_MODE2, SLAVE_DEVICE_TIMING_MODE2 }; static PWCHAR IdePortRegistryDeviceTimingModeAllowedName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { MASTER_DEVICE_TIMING_MODE_ALLOWED, SLAVE_DEVICE_TIMING_MODE_ALLOWED, MASTER_DEVICE_TIMING_MODE_ALLOWED2, SLAVE_DEVICE_TIMING_MODE_ALLOWED2 }; static PWCHAR IdePortRegistryIdentifyDataChecksum[MAX_IDE_DEVICE * MAX_IDE_LINE] = { MASTER_IDDATA_CHECKSUM, SLAVE_IDDATA_CHECKSUM, MASTER_IDDATA_CHECKSUM2, SLAVE_IDDATA_CHECKSUM2 }; static PWCHAR IdePortUserRegistryDeviceTypeName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { USER_MASTER_DEVICE_TYPE_REG_KEY, USER_SLAVE_DEVICE_TYPE_REG_KEY, USER_MASTER_DEVICE_TYPE2_REG_KEY, USER_SLAVE_DEVICE_TYPE2_REG_KEY }; static PWCHAR IdePortRegistryUserDeviceTimingModeAllowedName[MAX_IDE_DEVICE * MAX_IDE_LINE] = { USER_MASTER_DEVICE_TIMING_MODE_ALLOWED, USER_SLAVE_DEVICE_TIMING_MODE_ALLOWED, USER_MASTER_DEVICE_TIMING_MODE_ALLOWED2, USER_SLAVE_DEVICE_TIMING_MODE_ALLOWED2 }; VOID AnalyzeDeviceCapabilities( IN OUT PFDO_EXTENSION FdoExtension, IN BOOLEAN MustBePio[MAX_IDE_DEVICE * MAX_IDE_LINE] ) /*++ 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 - HW Device Extension Return Value: none --*/ { PHW_DEVICE_EXTENSION deviceExtension = FdoExtension->HwDeviceExtension; ULONG deviceNumber; BOOLEAN pioDevicePresent; PIDENTIFY_DATA identifyData; struct _DEVICE_PARAMETERS * deviceParameters; ULONG cycleTime; ULONG xferMode; ULONG bestXferMode; ULONG currentMode; ULONG tempMode; ULONG numberOfCylinders; ULONG numberOfHeads; ULONG sectorsPerTrack; PULONG TransferModeTimingTable=FdoExtension->TransferModeInterface.TransferModeTimingTable; ULONG transferModeTableLength=FdoExtension->TransferModeInterface.TransferModeTableLength; ASSERT(TransferModeTimingTable); // // Code is paged until locked down. // PAGED_CODE(); #ifdef ALLOC_PRAGMA ASSERT(IdePAGESCANLockCount > 0); #endif // // Figure out who can do DMA and who cannot // for (deviceNumber = 0; deviceNumber < deviceExtension->MaxIdeDevice; deviceNumber++) { if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) { // // check LBA capabilities // CLRMASK (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[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].NumCylinders == 16383) && (deviceExtension->IdentifyData[deviceNumber].NumHeads<= 16) && (deviceExtension->IdentifyData[deviceNumber].NumSectorsPerTrack== 63)) { deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_LBA; } if (!Is98LegacyIde(&deviceExtension->BaseIoAddress1)) { // // words 1, 3 and 6 // numberOfCylinders = deviceExtension->IdentifyData[deviceNumber].NumCylinders; numberOfHeads = deviceExtension->IdentifyData[deviceNumber].NumHeads; sectorsPerTrack = deviceExtension->IdentifyData[deviceNumber].NumSectorsPerTrack; if (deviceExtension->IdentifyData[deviceNumber].UserAddressableSectors > (numberOfCylinders * numberOfHeads * sectorsPerTrack)) { // // some ide driver has a 2G jumer to get around bios // problem. make sure we are not tricked the same way. // if ((numberOfCylinders <= 0xfff) && (numberOfHeads == 0x10) && (sectorsPerTrack == 0x3f)) { deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_LBA; } } } } #ifdef ENABLE_48BIT_LBA { USHORT commandSetSupport = deviceExtension->IdentifyData[deviceNumber].CommandSetSupport; USHORT commandSetActive = deviceExtension->IdentifyData[deviceNumber].CommandSetActive; if ((commandSetSupport & IDE_IDDATA_48BIT_LBA_SUPPORT) && (commandSetActive & IDE_IDDATA_48BIT_LBA_SUPPORT)) { ULONG maxLBA; // // get words 100-103 and make sure that it is the same or // greater than words 57-58. // ASSERT(deviceExtension->IdentifyData[deviceNumber].Max48BitLBA[0] != 0); maxLBA = deviceExtension->IdentifyData[deviceNumber].Max48BitLBA[0]; ASSERT(deviceExtension->IdentifyData[deviceNumber].Max48BitLBA[1] == 0); ASSERT(maxLBA >= deviceExtension->IdentifyData[deviceNumber].UserAddressableSectors); DebugPrint((0, "Max LBA supported is 0x%x\n", maxLBA )); if ((FdoExtension->EnableBigLba == 1) && (maxLBA >= MAX_28BIT_LBA)) { deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_48BIT_LBA; deviceExtension->DeviceFlags[deviceNumber] |= DFLAGS_LBA; } else { DebugPrint((1, "big lba disabled\n")); } } } #endif if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_LBA) { DebugPrint ((DBG_BUSSCAN, "atapi: target %d supports LBA\n", deviceNumber)); } xferMode = 0; cycleTime = UNINITIALIZED_CYCLE_TIME; bestXferMode = 0; // // check for IoReady Line // if (deviceExtension->IdentifyData[deviceNumber].Capabilities & IDENTIFY_CAPABILITIES_IOREADY_SUPPORTED) { deviceExtension->DeviceParameters[deviceNumber].IoReadyEnabled = TRUE; } else { deviceExtension->DeviceParameters[deviceNumber].IoReadyEnabled = FALSE; } // // Check for PIO mode // bestXferMode = (deviceExtension->IdentifyData[deviceNumber].PioCycleTimingMode & 0x00ff)+PIO0; if (bestXferMode > PIO2) { bestXferMode = PIO0; } ASSERT(bestXferMode < PIO3); cycleTime = TransferModeTimingTable[bestXferMode]; ASSERT(cycleTime); GenTransferModeMask(bestXferMode, xferMode); currentMode = 1<IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 1)) { if (deviceExtension->DeviceParameters[deviceNumber].IoReadyEnabled) { cycleTime = deviceExtension->IdentifyData[deviceNumber].MinimumPIOCycleTimeIORDY; } else { cycleTime = deviceExtension->IdentifyData[deviceNumber].MinimumPIOCycleTime; } if (deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes & (1 << 0)) { xferMode |= PIO_MODE3; bestXferMode = 3; currentMode = PIO_MODE3; } if (deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes & (1 << 1)) { xferMode |= PIO_MODE4; bestXferMode = 4; currentMode = PIO_MODE4; } // check if any of the bits > 1 are set. If so, default to PIO_MODE4 if (deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes) { GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].AdvancedPIOModes, bestXferMode); bestXferMode += PIO3; if (bestXferMode > PIO4) { DebugPrint((DBG_ALWAYS, "ATAPI: AdvancePIOMode > PIO_MODE4. Defaulting to PIO_MODE4. \n")); bestXferMode = PIO4; } currentMode = 1<IdentifyData[deviceNumber].AdvancedPIOModes)); } ASSERT (cycleTime != UNINITIALIZED_CYCLE_TIME); ASSERT (xferMode); ASSERT (currentMode); deviceExtension->DeviceParameters[deviceNumber].BestPioCycleTime = cycleTime; deviceExtension->DeviceParameters[deviceNumber].BestPioMode = bestXferMode; // // can't really figure out the current PIO mode // just use the best mode // deviceExtension->DeviceParameters[deviceNumber].TransferModeCurrent = currentMode; // // figure out all the DMA transfer mode this device supports // currentMode = 0; // // check singleword DMA timing // cycleTime = UNINITIALIZED_CYCLE_TIME; bestXferMode = UNINITIALIZED_TRANSFER_MODE; if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport) { DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData SingleWordDMASupport = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport)); DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData SingleWordDMAActive = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].SingleWordDMAActive)); GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].SingleWordDMASupport, bestXferMode); if ((bestXferMode+SWDMA0) > SWDMA2) { bestXferMode = SWDMA2-SWDMA0; } cycleTime = TransferModeTimingTable[bestXferMode+SWDMA0]; ASSERT(cycleTime); tempMode = 0; GenTransferModeMask(bestXferMode, tempMode); xferMode |= (tempMode << SWDMA0); if (deviceExtension->IdentifyData[deviceNumber].SingleWordDMAActive) { GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].SingleWordDMAActive, currentMode); if ((currentMode+SWDMA0) > SWDMA2) { currentMode = SWDMA2 - SWDMA0; } currentMode = 1 << (currentMode+SWDMA0); } } deviceExtension->DeviceParameters[deviceNumber].BestSwDmaCycleTime = cycleTime; deviceExtension->DeviceParameters[deviceNumber].BestSwDmaMode = bestXferMode; // // check multiword DMA timing // cycleTime = UNINITIALIZED_CYCLE_TIME; bestXferMode = UNINITIALIZED_TRANSFER_MODE; if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport) { DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData MultiWordDMASupport = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport)); DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData MultiWordDMAActive = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].MultiWordDMAActive)); GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].MultiWordDMASupport, bestXferMode); if ((bestXferMode+MWDMA0) > MWDMA2) { bestXferMode = MWDMA2 - MWDMA0; } cycleTime = TransferModeTimingTable[bestXferMode+MWDMA0]; ASSERT(cycleTime); tempMode = 0; GenTransferModeMask(bestXferMode, tempMode); xferMode |= (tempMode << MWDMA0); if (deviceExtension->IdentifyData[deviceNumber].MultiWordDMAActive) { GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].MultiWordDMAActive, currentMode); if ((currentMode+MWDMA0) > MWDMA2) { currentMode = MWDMA2 - MWDMA0; } currentMode = 1 << (currentMode+MWDMA0); } } if (deviceExtension->IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 1)) { DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData word 64-70 are valid\n", deviceNumber)); if (deviceExtension->IdentifyData[deviceNumber].MinimumMWXferCycleTime && deviceExtension->IdentifyData[deviceNumber].RecommendedMWXferCycleTime) { DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData MinimumMWXferCycleTime = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].MinimumMWXferCycleTime)); DebugPrint ((DBG_BUSSCAN, "atapi: target %d IdentifyData RecommendedMWXferCycleTime = 0x%x\n", deviceNumber, deviceExtension->IdentifyData[deviceNumber].RecommendedMWXferCycleTime)); cycleTime = deviceExtension->IdentifyData[deviceNumber].MinimumMWXferCycleTime; } } deviceExtension->DeviceParameters[deviceNumber].BestMwDmaCycleTime = cycleTime; deviceExtension->DeviceParameters[deviceNumber].BestMwDmaMode = bestXferMode; // // figure out the ultra DMA timing the device supports // cycleTime = UNINITIALIZED_CYCLE_TIME; bestXferMode = UNINITIALIZED_TRANSFER_MODE; tempMode = UNINITIALIZED_TRANSFER_MODE; // to set the current mode correctly // // Consult the channel driver for the UDMA modes that are supported. // This will allow new udma modes to be supported. Always trust this funtion. // if (FdoExtension->TransferModeInterface.UdmaModesSupported) { NTSTATUS status = FdoExtension->TransferModeInterface.UdmaModesSupported ( deviceExtension->IdentifyData[deviceNumber], &bestXferMode, &tempMode ); if (!NT_SUCCESS(status)) { bestXferMode = UNINITIALIZED_TRANSFER_MODE; tempMode = UNINITIALIZED_TRANSFER_MODE; } } else { // // No udma support funtions to interpret Identify data in the channel driver. // Interpret in the known way. // if (deviceExtension->IdentifyData[deviceNumber].TranslationFieldsValid & (1 << 2)) { if (deviceExtension->IdentifyData[deviceNumber].UltraDMASupport) { GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].UltraDMASupport, bestXferMode); } if (deviceExtension->IdentifyData[deviceNumber].UltraDMAActive) { GetHighestTransferMode( deviceExtension->IdentifyData[deviceNumber].UltraDMAActive, tempMode); } } } // // Use the current mode if we actually got one // if (tempMode != UNINITIALIZED_TRANSFER_MODE) { currentMode = tempMode; if (transferModeTableLength <= (currentMode + UDMA0)) { currentMode = transferModeTableLength-UDMA0-1; } currentMode = 1 << (currentMode+UDMA0); } // // make sure that bestXferMode is initialized. if not it indicates that // the device does not support udma. // if (bestXferMode != UNINITIALIZED_TRANSFER_MODE) { if (transferModeTableLength <= (bestXferMode + UDMA0)) { bestXferMode = transferModeTableLength-UDMA0-1; } cycleTime = TransferModeTimingTable[bestXferMode+UDMA0]; ASSERT(cycleTime); tempMode = 0; GenTransferModeMask(bestXferMode, tempMode); xferMode |= (tempMode << UDMA0); } // // Doesn't really know the ultra dma cycle time // deviceExtension->DeviceParameters[deviceNumber].BestUDmaCycleTime = cycleTime; deviceExtension->DeviceParameters[deviceNumber].BestUDmaMode = bestXferMode; deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported = xferMode; deviceExtension->DeviceParameters[deviceNumber].TransferModeCurrent |= currentMode; // // Check to see if the device is in the Hall of Shame! // if (MustBePio[deviceNumber] || !AtapiDMACapable (FdoExtension, deviceNumber) || (*InitSafeBootMode == SAFEBOOT_MINIMAL)) { DebugPrint((DBG_XFERMODE, "ATAPI: Reseting DMA Information\n" )); // // Remove all DMA info // deviceExtension->DeviceParameters[deviceNumber].BestSwDmaCycleTime = 0; deviceExtension->DeviceParameters[deviceNumber].BestMwDmaCycleTime = 0; deviceExtension->DeviceParameters[deviceNumber].BestUDmaCycleTime = 0; deviceExtension->DeviceParameters[deviceNumber].BestSwDmaMode = 0; deviceExtension->DeviceParameters[deviceNumber].BestMwDmaMode = 0; deviceExtension->DeviceParameters[deviceNumber].BestUDmaMode = 0; deviceExtension->DeviceParameters[deviceNumber].TransferModeCurrent &= PIO_SUPPORT; deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported &= PIO_SUPPORT; } // if DMADetectionLevel = 0, clear current DMA mode // if DMADetectionLevel = 1, set current mode // if DMADetectionLevel = 2, clear all current mode // pciidex takes care of this for non-acpi machines. // In acpi systems, it is better to trust the GTM settings. // // If a device supports any of the advanced PIO mode, we are assuming that // the device is a "newer" drive and IDE_COMMAND_READ_MULTIPLE should work. // Otherwise, we will turn off IDE_COMMAND_READ_MULTIPLE // if (deviceExtension->DeviceParameters[deviceNumber].BestPioMode > 2) { if (!Is98LegacyIde(&deviceExtension->BaseIoAddress1)) { deviceExtension->MaximumBlockXfer[deviceNumber] = (UCHAR)(deviceExtension->IdentifyData[deviceNumber].MaximumBlockTransfer & 0xFF); } else { // // MaximumBlockXfer is less or equal 16 // deviceExtension->MaximumBlockXfer[deviceNumber] = ((UCHAR)(deviceExtension->IdentifyData[deviceNumber].MaximumBlockTransfer & 0xFF) > 16)? 16 : (UCHAR)(deviceExtension->IdentifyData[deviceNumber].MaximumBlockTransfer & 0xFF); } } else { deviceExtension->MaximumBlockXfer[deviceNumber] = 0; } DebugPrint ((DBG_XFERMODE, "atapi: target %d transfer timing:\n" "atapi: PIO mode supported = %4x and best cycle time = %5d ns\n" "atapi: SWDMA mode supported = %4x and best cycle time = %5d ns\n" "atapi: MWDMA mode supported = %4x and best cycle time = %5d ns\n" "atapi: UDMA mode supported = %x and best cycle time = %5d ns\n" "atapi: Current mode bitmap = %4x\n", deviceNumber, deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported & PIO_SUPPORT, deviceExtension->DeviceParameters[deviceNumber].BestPioCycleTime, deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported & SWDMA_SUPPORT, deviceExtension->DeviceParameters[deviceNumber].BestSwDmaCycleTime, deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported & MWDMA_SUPPORT, deviceExtension->DeviceParameters[deviceNumber].BestMwDmaCycleTime, deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported & UDMA_SUPPORT, deviceExtension->DeviceParameters[deviceNumber].BestUDmaCycleTime, deviceExtension->DeviceParameters[deviceNumber].TransferModeCurrent )); } } } // AnalyzeDeviceCapabilities VOID AtapiSyncSelectTransferMode ( IN PFDO_EXTENSION FdoExtension, IN OUT PHW_DEVICE_EXTENSION DeviceExtension, IN ULONG TimingModeAllowed[MAX_IDE_TARGETID * MAX_IDE_LINE] ) /*++ Routine Description: query the best transfer mode for our devices Arguments: FdoExtension DeviceExtension - HW Device Extension TimingModeAllowed - Allowed transfer modes Return Value: none --*/ { PCIIDE_TRANSFER_MODE_SELECT transferModeSelect; ULONG i; NTSTATUS status; if (!IsNEC_98) { RtlZeroMemory (&transferModeSelect, sizeof(transferModeSelect)); for (i=0; iMaxIdeDevice; i++) { transferModeSelect.DevicePresent[i] = DeviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT ? TRUE : FALSE; // // ISSUE: 07/31/2000: How about atapi hard disk // We don't know of any. This would suffice for the time being. // transferModeSelect.FixedDisk[i] = !(DeviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE); transferModeSelect.BestPioCycleTime[i] = DeviceExtension->DeviceParameters[i].BestPioCycleTime; transferModeSelect.BestSwDmaCycleTime[i] = DeviceExtension->DeviceParameters[i].BestSwDmaCycleTime; transferModeSelect.BestMwDmaCycleTime[i] = DeviceExtension->DeviceParameters[i].BestMwDmaCycleTime; transferModeSelect.BestUDmaCycleTime[i] = DeviceExtension->DeviceParameters[i].BestUDmaCycleTime; transferModeSelect.IoReadySupported[i] = DeviceExtension->DeviceParameters[i].IoReadyEnabled; transferModeSelect.DeviceTransferModeSupported[i] = DeviceExtension->DeviceParameters[i].TransferModeSupported; transferModeSelect.DeviceTransferModeCurrent[i] = DeviceExtension->DeviceParameters[i].TransferModeCurrent; // // if we don't have a busmaster capable parent or // the device is a tape, stay with pio mode // // (tape may transfer fewer bytes than requested. // we can't figure exactly byte transfered with DMA) // // if ((!FdoExtension->BoundWithBmParent) || // (DeviceExtension->DeviceFlags[i] & DFLAGS_TAPE_DEVICE)) { if (!FdoExtension->BoundWithBmParent) { transferModeSelect.DeviceTransferModeSupported[i] &= PIO_SUPPORT; transferModeSelect.DeviceTransferModeCurrent[i] &= PIO_SUPPORT; } // // Some miniports need this // transferModeSelect.IdentifyData[i]=DeviceExtension->IdentifyData[i]; transferModeSelect.UserChoiceTransferMode[i] = FdoExtension->UserChoiceTransferMode[i]; // // honor user's choice and/or last knowen good mode // transferModeSelect.DeviceTransferModeSupported[i] &= TimingModeAllowed[i]; transferModeSelect.DeviceTransferModeCurrent[i] &= TimingModeAllowed[i]; // should look at dmadetectionlevel and set DeviceTransferModeDesired // we look at dmaDetectionlecel in TransferModeSelect function below. // the parameters set here should be honoured anyways, I feel. } transferModeSelect.TransferModeTimingTable= FdoExtension-> TransferModeInterface.TransferModeTimingTable; transferModeSelect.TransferModeTableLength= FdoExtension-> TransferModeInterface.TransferModeTableLength; ASSERT(FdoExtension->TransferModeInterface.TransferModeSelect); status = FdoExtension->TransferModeInterface.TransferModeSelect ( FdoExtension->TransferModeInterface.Context, &transferModeSelect ); } else { // // Always fail for nec98 machines // status = STATUS_UNSUCCESSFUL; } if (!NT_SUCCESS(status)) { // // Unable to get the mode select, default to current PIO mode // for (i=0; iMaxIdeDevice; i++) { DeviceExtension->DeviceParameters[i].TransferModeSelected = DeviceExtension->DeviceParameters[i].TransferModeCurrent & PIO_SUPPORT; DebugPrint ((DBG_XFERMODE, "Atapi: DEFAULT device %d transfer mode current 0x%x and selected bitmap 0x%x\n", i, DeviceExtension->DeviceParameters[i].TransferModeCurrent, DeviceExtension->DeviceParameters[i].TransferModeSelected)); } } else { for (i=0; iMaxIdeDevice; i++) { DeviceExtension->DeviceParameters[i].TransferModeSelected = transferModeSelect.DeviceTransferModeSelected[i]; DebugPrint ((DBG_XFERMODE, "Atapi: device %d transfer mode current 0x%x and selected bitmap 0x%x\n", i, DeviceExtension->DeviceParameters[i].TransferModeCurrent, DeviceExtension->DeviceParameters[i].TransferModeSelected)); } } return; } // AtapiSelectTransferMode UCHAR SpecialWDDevicesFWVersion[][9] = { {"14.04E28"}, {"25.26H35"}, {"26.27J38"}, {"27.25C38"}, {"27.25C39"} }; #define NUMBER_OF_SPECIAL_WD_DEVICES (sizeof(SpecialWDDevicesFWVersion) / (sizeof (UCHAR) * 9)) BOOLEAN AtapiDMACapable ( IN OUT PFDO_EXTENSION FdoExtension, IN ULONG deviceNumber ) /*++ Routine Description: check the given device whether it is on our bad device list (non dma device) Arguments: HwDeviceExtension - HBA miniport driver's adapter data storage deviceNumber - device number Return Value: TRUE if dma capable FALSE if not dma capable --*/ { PHW_DEVICE_EXTENSION deviceExtension = FdoExtension->HwDeviceExtension; UCHAR modelNumber[41]; UCHAR firmwareVersion[9]; ULONG i; BOOLEAN turnOffDMA = FALSE; // // Code is paged until locked down. // PAGED_CODE(); #ifdef ALLOC_PRAGMA ASSERT(IdePAGESCANLockCount > 0); #endif if (!(deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT)) { return FALSE; } // // byte swap model number // for (i=0; i<40; i+=2) { modelNumber[i + 0] = deviceExtension->IdentifyData[deviceNumber].ModelNumber[i + 1]; modelNumber[i + 1] = deviceExtension->IdentifyData[deviceNumber].ModelNumber[i + 0]; } modelNumber[i] = 0; // // if we have a Western Digial device // if the best dma mode is multi word dma mode 1 // if the identify data word offset 129 is not 0x5555 // turn off dma unless // if the device firmware version is on the list and // it is the only drive on the bus // if (3 == RtlCompareMemory(modelNumber, "WDC", 3)) { if ((deviceExtension->DeviceParameters[deviceNumber].TransferModeSupported & (MWDMA_MODE2 | MWDMA_MODE1)) == MWDMA_MODE1) { for (i=0; i<8; i+=2) { firmwareVersion[i + 0] = deviceExtension->IdentifyData[deviceNumber].FirmwareRevision[i + 1]; firmwareVersion[i + 1] = deviceExtension->IdentifyData[deviceNumber].FirmwareRevision[i + 0]; } firmwareVersion[i] = 0; // // Check the special flag. If not found, can't use dma // if (*(((PUSHORT)&deviceExtension->IdentifyData[deviceNumber]) + 129) != 0x5555) { DebugPrint ((0, "ATAPI: found mode 1 WD drive. no dma unless it is the only device\n")); turnOffDMA = TRUE; for (i=0; iDeviceFlags[otherDeviceNumber] & DFLAGS_DEVICE_PRESENT)) { turnOffDMA = FALSE; break; } } } } } } if (turnOffDMA) { return FALSE; } else { return TRUE; } } IDE_DEVICETYPE AtapiDetectDevice ( IN OUT PFDO_EXTENSION FdoExtension, IN OUT PPDO_EXTENSION PdoExtension, IN OUT PIDENTIFY_DATA IdentifyData, IN BOOLEAN MustSucceed ) /**++ Routine Description: Detect the device at this location. 1. Send "ec". 2. if success read the Identify data and return the device type 3. else send "a1" 4. if success read the Identify data and return the device type 5. else return no device Arguments: FdoExtension: PdoExtension: IdentifyData: Identify data is copied into this buffer if a device is detected. MustSucceed: TRUE if pre-alloced memory is to be used. Return Value: device type: ATA, ATAPI or NO Device --**/ { PATA_PASS_THROUGH ataPassThroughData; UCHAR ataPassThroughDataBuffer[sizeof(*ataPassThroughData) + sizeof (*IdentifyData)]; BOOLEAN foundIt; NTSTATUS status; PIDE_REGISTERS_1 cmdRegBase; UCHAR statusByte1; LONG i; ULONG j; IDEREGS identifyCommand[3]; IDE_DEVICETYPE deviceType; BOOLEAN resetController = FALSE; LARGE_INTEGER tickCount; ULONG timeDiff; ULONG timeoutValue = 0; ULONG retryCount = 0; BOOLEAN defaultTimeout = FALSE; HANDLE deviceHandle; PAGED_CODE(); ASSERT(FdoExtension); ASSERT(PdoExtension); ASSERT(PdoExtension->PathId == 0); ASSERT(PdoExtension->TargetId < FdoExtension->HwDeviceExtension->MaxIdeTargetId); #ifdef ENABLE_ATAPI_VERIFIER if (ViIdeFakeMissingDevice(FdoExtension, PdoExtension->TargetId)) { IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); return DeviceNotExist; } #endif //ENABLE_ATAPI_VERIFIER ataPassThroughData = (PATA_PASS_THROUGH)ataPassThroughDataBuffer; foundIt = FALSE; cmdRegBase = &FdoExtension->HwDeviceExtension->BaseIoAddress1; if (FdoExtension->UserChoiceDeviceType[PdoExtension->TargetId] == DeviceNotExist) { deviceType = DeviceNotExist; } else { // // Look into the registry for last boot configration // deviceType = DeviceUnknown; IdePortGetDeviceParameter( FdoExtension, IdePortRegistryDeviceTypeName[PdoExtension->TargetId], (PULONG)&deviceType ); DebugPrint((DBG_BUSSCAN, "AtapiDetectDevice - last boot config deviceType = 0x%x\n", deviceType)); // // Obtain the timeout value. // ISSUE: should not be in the class section. // /***** status = IoOpenDeviceRegistryKey( FdoExtension->AttacheePdo, PLUGPLAY_REGKEY_DEVICE, KEY_QUERY_VALUE, &deviceHandle); DebugPrint((0, "DetectDevice status = %x\n", status)); ZwClose(deviceHandle); ****/ IdePortGetDeviceParameter( FdoExtension, IdePortRegistryDeviceTimeout[PdoExtension->TargetId], (PULONG)&timeoutValue ); // // if there is no registry entry use the default // if (timeoutValue == 0) { timeoutValue = (PdoExtension->TargetId & 0x1)==0 ? 10 : 3; defaultTimeout = TRUE; } // // Use 3s timeout for slave devices in safe boot mode. Why?? // if (*InitSafeBootMode == SAFEBOOT_MINIMAL) { timeoutValue = (PdoExtension->TargetId & 0x1)==0 ? 10 : 3; } // // invalidate the last boot configuration // we will update it with a new setting if we // detect a device // IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTypeName[PdoExtension->TargetId], DeviceUnknown ); if ((PdoExtension->TargetId == 1) && (FdoExtension->MayHaveSlaveDevice == 0)) { deviceType = DeviceNotExist; } } if (Is98LegacyIde(cmdRegBase)) { UCHAR signatureLow; UCHAR signatureHigh; UCHAR statusByte1; if ((PdoExtension->TargetId & 0x1) == 1) { // // Slave device phese. // if (!EnhancedIdeSupport()) { // // Enhanced ide machine can not have the slave device. // IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); return DeviceNotExist; } if (FdoExtension->HwDeviceExtension->DeviceFlags[PdoExtension->TargetId - 1] & DFLAGS_WD_MODE) { // // WD-Mode cd-rom can not has slave device. // IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); return DeviceNotExist; } } #if 0 //NEC_98 if (deviceType == DeviceIsAtapi) { // // reset device type to determine WD-Mode or ATAPI. // deviceType = DeviceUnknown; } if (deviceType == DeviceUnknown) { RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->IdeReg.bCommandReg = IDE_COMMAND_ATAPI_RESET; ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_ENUM_PROBING; status = IssueSyncAtaPassThroughSafe ( FdoExtension, PdoExtension, ataPassThroughData, FALSE, FALSE, 15, mustSucceed ); statusByte1 = ataPassThroughData->IdeReg.bCommandReg; signatureLow = ataPassThroughData->IdeReg.bCylLowReg; signatureHigh = ataPassThroughData->IdeReg.bCylHighReg; if (signatureLow == 0x14 && signatureHigh == 0xEB) { deviceType = DeviceIsAtapi; } else { DebugPrint((DBG_BUSSCAN, "AtapiDetectDevice - ata %x status after soft reset %x\n",PdoExtension->TargetId, statusByte1)); if (statusByte1 & IDE_STATUS_ERROR) { deviceType = DeviceIsAta; } else { // // Device is WD-Mode cd-rom. // deviceType = DeviceIsAtapi; SETMASK (FdoExtension->HwDeviceExtension->DeviceFlags[PdoExtension->TargetId], DFLAGS_WD_MODE); } } } #endif //NEC_98 } #if ENABLE_ATAPI_VERIFIER if (!Is98LegacyIde(cmdRegBase)) { // // simulate device change // if (deviceType == DeviceIsAta) { deviceType = DeviceIsAtapi; } else if (deviceType == DeviceIsAtapi) { deviceType = DeviceIsAta; } } #endif // // command to issue // RtlZeroMemory (&identifyCommand, sizeof (identifyCommand)); if (deviceType == DeviceNotExist) { IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); return DeviceNotExist; } else if (deviceType == DeviceIsAta) { identifyCommand[0].bCommandReg = IDE_COMMAND_IDENTIFY; identifyCommand[0].bReserved = ATA_PTFLAGS_STATUS_DRDY_REQUIRED | ATA_PTFLAGS_ENUM_PROBING; identifyCommand[1].bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; identifyCommand[1].bReserved = ATA_PTFLAGS_ENUM_PROBING; } else { identifyCommand[0].bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; identifyCommand[0].bReserved = ATA_PTFLAGS_ENUM_PROBING; identifyCommand[1].bCommandReg = IDE_COMMAND_IDENTIFY; identifyCommand[1].bReserved = ATA_PTFLAGS_STATUS_DRDY_REQUIRED | ATA_PTFLAGS_ENUM_PROBING; } // // IDE HACK // // If we are talking to a non-existing device, the // status register value may be unstable. // Reading it a few time seems to stablize it. // RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_NO_OP | ATA_PTFLAGS_ENUM_PROBING; // // Repeat 10 times // ataPassThroughData->IdeReg.bSectorCountReg = 10; LogBusScanStartTimer(&tickCount); status = IssueSyncAtaPassThroughSafe ( FdoExtension, PdoExtension, ataPassThroughData, FALSE, FALSE, 3, MustSucceed ); timeDiff = LogBusScanStopTimer(&tickCount); DebugPrint((DBG_SPECIAL, "DetectDevice: Hack for device %d at %x took %u ms\n", PdoExtension->TargetId, FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); statusByte1 = ataPassThroughData->IdeReg.bCommandReg; if (Is98LegacyIde(cmdRegBase)) { UCHAR driveHeadReg; driveHeadReg = ataPassThroughData->IdeReg.bDriveHeadReg; if (driveHeadReg != ((PdoExtension->TargetId & 0x1) << 4 | 0xA0)) { // // Bad controller. // IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); return DeviceNotExist; } // // There are some H/W as follow... // if ((statusByte1 & 0xe8) == 0xa8) { IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); return DeviceNotExist; } } if (statusByte1 == 0xff) { // // nothing here // IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); return DeviceNotExist; } // // If the statusByte1 is 80 then try a reset // if (statusByte1 & IDE_STATUS_BUSY) { // // look like it is hung, try reset to bring it back // RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_BUS_RESET; LogBusScanStartTimer(&tickCount); status = IssueSyncAtaPassThroughSafe( FdoExtension, PdoExtension, ataPassThroughData, FALSE, FALSE, 30, MustSucceed ); timeDiff = LogBusScanStopTimer(&tickCount); LogBusScanTimeDiff(FdoExtension, IdePortBootTimeRegKey[0], timeDiff); DebugPrint((DBG_SPECIAL, "DtectDevice: Reset device %d ata %x took %u ms\n", PdoExtension->TargetId, FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); } LogBusScanStartTimer(&tickCount); retryCount = 0; for (i=0; i<2; i++) { BOOLEAN ataIdentify; ataIdentify = identifyCommand[i].bCommandReg == IDE_COMMAND_IDENTIFY ? TRUE : FALSE; if (ataIdentify) { // // IDE HACK // // If we are talking to a non-existing device, the // status register value may be unstable. // Reading it a few time seems to stablize it. // RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_NO_OP | ATA_PTFLAGS_ENUM_PROBING; // // Repeat 10 times // ataPassThroughData->IdeReg.bSectorCountReg = 10; status = IssueSyncAtaPassThroughSafe( FdoExtension, PdoExtension, ataPassThroughData, FALSE, FALSE, 3, MustSucceed ); statusByte1 = ataPassThroughData->IdeReg.bCommandReg; // // a real ATA device should never return this // if ((statusByte1 == 0x00) || (statusByte1 == 0x01)) { // // nothing here // continue; } deviceType = DeviceIsAta; if (Is98LegacyIde(cmdRegBase)) { UCHAR systemPortAData; // // dip-switch 2 read. // systemPortAData = IdePortInPortByte( (PUCHAR)SYSTEM_PORT_A ); DebugPrint((DBG_BUSSCAN, "atapi:AtapiFindNewDevices - ide dip switch %x\n",systemPortAData)); if (!(systemPortAData & 0x20)) { // // Internal-hd(ide) has been disabled with system-menu. // deviceType = DeviceNotExist; break; } } } else { RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->IdeReg.bReserved = ATA_PTFLAGS_NO_OP | ATA_PTFLAGS_ENUM_PROBING; status = IssueSyncAtaPassThroughSafe ( FdoExtension, PdoExtension, ataPassThroughData, FALSE, FALSE, 3, MustSucceed ); statusByte1 = ataPassThroughData->IdeReg.bCommandReg; deviceType = DeviceIsAtapi; } if ((statusByte1 == 0xff) || (statusByte1 == 0xfe)) { // // nothing here // IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); deviceType = DeviceNotExist; break; } // // build the ata pass through the id data command // RtlZeroMemory (ataPassThroughData, sizeof (*ataPassThroughData)); ataPassThroughData->DataBufferSize = sizeof (*IdentifyData); RtlMoveMemory (&ataPassThroughData->IdeReg, identifyCommand + i, sizeof(ataPassThroughData->IdeReg)); ASSERT(timeoutValue); // // Issue an id data command to the device // // some device (Kingston PCMCIA Datapak (non-flash)) takes a long time to response. we // can possibly timeout even an device exists. // // we have to make a compromise here. We want to detect slow devices without causing // many systems to boot slow. // // here is the logic: // // Since we are here (IsChannelEmpty() == FALSE), we are guessing we have at least // one device attached and it is a master device. It should be ok to allow longer // timeout when sending ID data to the master. We should never timeout unless // the channel has only a slave device. // // Yes, we will not detect slow slave device for now. If anyone complain, we will // fix it. // // You can never win as long as we have broken ATA devices! // status = IssueSyncAtaPassThroughSafe ( FdoExtension, PdoExtension, ataPassThroughData, TRUE, FALSE, timeoutValue, MustSucceed ); //(PdoExtension->TargetId & 0x1)==0 ? 10 : 1, if (NT_SUCCESS(status)) { if (!(ataPassThroughData->IdeReg.bCommandReg & IDE_STATUS_ERROR)) { DebugPrint ((DBG_BUSSCAN, "IdePort: Found a child on 0x%x target 0x%x\n", cmdRegBase->RegistersBaseAddress, PdoExtension->TargetId)); foundIt = TRUE; if (ataIdentify) { IdePortFudgeAtaIdentifyData( (PIDENTIFY_DATA) ataPassThroughData->DataBuffer ); } break; } else { DebugPrint ((DBG_BUSSCAN, "AtapiDetectDevice:Command %x, 0x%x target 0x%x failed 0x%x with status 0x%x\n", i, cmdRegBase->RegistersBaseAddress, PdoExtension->TargetId, identifyCommand[i].bCommandReg, ataPassThroughData->IdeReg.bCommandReg )); } } else { DebugPrint ((DBG_BUSSCAN, "AtapiDetectDevice:The irp with command %x, 0x%x target 0x%x failed 0x%x with status 0x%x\n", i, cmdRegBase->RegistersBaseAddress, PdoExtension->TargetId, identifyCommand[i].bCommandReg, status )); deviceType = DeviceNotExist; IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); if ((FdoExtension->HwDeviceExtension->DeviceFlags[PdoExtension->TargetId] & DFLAGS_DEVICE_PRESENT) && !(FdoExtension->HwDeviceExtension->DeviceFlags[PdoExtension->TargetId] & DFLAGS_ATAPI_DEVICE) && (retryCount < 2)) { // // BAD BAD BAD device // // SAMSUNG WU32543A (2.54GB) // // when it does a few UDMA transfers, it kind of forgets // how to do ATA identify data so it looks like the device // is gone. // // we better try harder to make sure if it is really gone // we will do that by issuing a hard reset and try identify // data again. // if (identifyCommand[i].bCommandReg == IDE_COMMAND_IDENTIFY) { // // ask for an "inline" hard reset before issuing identify command // identifyCommand[i].bReserved |= ATA_PTFLAGS_INLINE_HARD_RESET; // // redo the last command // i -= 1; resetController = TRUE; retryCount++; } } else { if (status == STATUS_IO_TIMEOUT) { // // looks like there is no device there // update the registry with a low timeout value if // this is the slave device. // if ((PdoExtension->TargetId & 0x1) && defaultTimeout) { // // Use the timeout value of 1s for the next boot. // DebugPrint((1, "Updating the registry with 1s value for device %d\n", PdoExtension->TargetId )); IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTimeout[PdoExtension->TargetId], 1 ); } break; } } } // // try the next command // } timeDiff = LogBusScanStopTimer(&tickCount); DebugPrint((DBG_SPECIAL, "DetectDevice: Identify Data for device %d at %x took %u ms\n", PdoExtension->TargetId, FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); // // save for the next boot // IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTypeName[PdoExtension->TargetId], deviceType == DeviceNotExist? DeviceUnknown : deviceType ); if (foundIt) { RtlMoveMemory (IdentifyData, ataPassThroughData->DataBuffer, sizeof (*IdentifyData)); #if DBG { ULONG i; UCHAR string[41]; for (i=0; i<8; i+=2) { string[i] = IdentifyData->FirmwareRevision[i + 1]; string[i + 1] = IdentifyData->FirmwareRevision[i]; } string[i] = 0; DebugPrint((DBG_BUSSCAN, "AtapiDetectDevice: firmware version: %s\n", string)); for (i=0; i<40; i+=2) { string[i] = IdentifyData->ModelNumber[i + 1]; string[i + 1] = IdentifyData->ModelNumber[i]; } string[i] = 0; DebugPrint((DBG_BUSSCAN, "AtapiDetectDevice: model number: %s\n", string)); for (i=0; i<20; i+=2) { string[i] = IdentifyData->SerialNumber[i + 1]; string[i + 1] = IdentifyData->SerialNumber[i]; } string[i] = 0; DebugPrint((DBG_BUSSCAN, "AtapiDetectDevice: serial number: %s\n", string)); } #endif // DBG } else { deviceType = DeviceNotExist; } if (deviceType == DeviceNotExist) { IdeLogDeadMeatEvent( PdoExtension->DeadmeatRecord.FileName, PdoExtension->DeadmeatRecord.LineNumber ); IdeLogDeadMeatTaskFile( PdoExtension->DeadmeatRecord.IdeReg, ataPassThroughData->IdeReg ); } return deviceType; } NTSTATUS IdePortSelectCHS ( IN OUT PFDO_EXTENSION FdoExtension, IN ULONG Device, IN PIDENTIFY_DATA IdentifyData ) { IN PHW_DEVICE_EXTENSION HwDeviceExtension; BOOLEAN skipSetParameters = FALSE; // // Code is paged until locked down. // PAGED_CODE(); #ifdef ALLOC_PRAGMA ASSERT(IdePAGESCANLockCount > 0); #endif ASSERT(FdoExtension); ASSERT(IdentifyData); HwDeviceExtension = FdoExtension->HwDeviceExtension; ASSERT (HwDeviceExtension); ASSERT(Device < HwDeviceExtension->MaxIdeDevice); // LBA??? // We set the LBA flag in AnalyzeDeviceCapabilities. // if (!((HwDeviceExtension->DeviceFlags[Device] & DFLAGS_DEVICE_PRESENT) && (!(HwDeviceExtension->DeviceFlags[Device] & DFLAGS_ATAPI_DEVICE)))) { return STATUS_SUCCESS; } if (!Is98LegacyIde(&HwDeviceExtension->BaseIoAddress1) && (((IdentifyData->NumberOfCurrentCylinders * IdentifyData->NumberOfCurrentHeads * IdentifyData->CurrentSectorsPerTrack) < (IdentifyData->NumCylinders * IdentifyData->NumHeads * IdentifyData->NumSectorsPerTrack)) || // discover a larger drive (IdentifyData->MajorRevision == 0) || ((IdentifyData->NumberOfCurrentCylinders == 0) || (IdentifyData->NumberOfCurrentHeads == 0) || (IdentifyData->CurrentSectorsPerTrack == 0))) ) { HwDeviceExtension->NumberOfCylinders[Device] = IdentifyData->NumCylinders; HwDeviceExtension->NumberOfHeads[Device] = IdentifyData->NumHeads; HwDeviceExtension->SectorsPerTrack[Device] = IdentifyData->NumSectorsPerTrack; } else { HwDeviceExtension->NumberOfCylinders[Device] = IdentifyData->NumberOfCurrentCylinders; HwDeviceExtension->NumberOfHeads[Device] = IdentifyData->NumberOfCurrentHeads; HwDeviceExtension->SectorsPerTrack[Device] = IdentifyData->CurrentSectorsPerTrack; } if ((IdentifyData->NumCylinders != IdentifyData->NumberOfCurrentCylinders) || (IdentifyData->NumHeads != IdentifyData->NumberOfCurrentHeads) || (IdentifyData->NumSectorsPerTrack != IdentifyData->CurrentSectorsPerTrack)) { DebugPrint (( DBG_ALWAYS, "0x%x device %d current CHS (%x,%x,%x) differs from default CHS (%x,%x,%x)\n", HwDeviceExtension->BaseIoAddress1.RegistersBaseAddress, Device, IdentifyData->NumberOfCurrentCylinders, IdentifyData->NumberOfCurrentHeads, IdentifyData->CurrentSectorsPerTrack, IdentifyData->NumCylinders, IdentifyData->NumHeads, IdentifyData->NumSectorsPerTrack )); } // // 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 (HwDeviceExtension->SectorsPerTrack[Device] == 0x35 && HwDeviceExtension->NumberOfHeads[Device] == 0x07) { DebugPrint((DBG_ALWAYS, "FindDevices: Found nasty Compaq ESDI!\n")); // // Change these values to something reasonable. // HwDeviceExtension->SectorsPerTrack[Device] = 0x34; HwDeviceExtension->NumberOfHeads[Device] = 0x0E; } if (HwDeviceExtension->SectorsPerTrack[Device] == 0x35 && HwDeviceExtension->NumberOfHeads[Device] == 0x0F) { DebugPrint((DBG_ALWAYS, "FindDevices: Found nasty Compaq ESDI!\n")); // // Change these values to something reasonable. // HwDeviceExtension->SectorsPerTrack[Device] = 0x34; HwDeviceExtension->NumberOfHeads[Device] = 0x0F; } if (HwDeviceExtension->SectorsPerTrack[Device] == 0x36 && HwDeviceExtension->NumberOfHeads[Device] == 0x07) { DebugPrint((DBG_ALWAYS, "FindDevices: Found nasty UltraStor ESDI!\n")); // // Change these values to something reasonable. // HwDeviceExtension->SectorsPerTrack[Device] = 0x3F; HwDeviceExtension->NumberOfHeads[Device] = 0x10; skipSetParameters = TRUE; } if (Is98LegacyIde(&HwDeviceExtension->BaseIoAddress1)) { skipSetParameters = TRUE; } if (!skipSetParameters) { PIDE_REGISTERS_1 baseIoAddress1 = &HwDeviceExtension->BaseIoAddress1; PIDE_REGISTERS_2 baseIoAddress2 = &HwDeviceExtension->BaseIoAddress2; UCHAR statusByte; DebugPrintTickCount (FindDeviceTimer, 0); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); // // Select the device. // SelectIdeDevice(baseIoAddress1, Device, 0); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); WaitOnBusy(baseIoAddress1,statusByte); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); if (statusByte & IDE_STATUS_BUSY) { ULONG waitCount = 20000; DebugPrintTickCount (FindDeviceTimer, 0); // // Reset the device. // DebugPrint((2, "FindDevices: Resetting controller before SetDriveParameters.\n")); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); IdeHardReset ( baseIoAddress1, baseIoAddress2, FALSE, TRUE ); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); DebugPrintTickCount (FindDeviceTimer, 0); } DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); WaitOnBusy(baseIoAddress1,statusByte); DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); DebugPrintTickCount (FindDeviceTimer, 0); DebugPrint((2, "FindDevices: Status before SetDriveParameters: (%x) (%x)\n", statusByte, IdePortInPortByte (baseIoAddress1->DriveSelect))); // // Use the IDENTIFY data to set drive parameters. // DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); if (!SetDriveParameters(HwDeviceExtension,Device,TRUE)) { DebugPrint((0, "IdePortFixUpCHS: Set drive parameters for device %d failed\n", Device)); // // Don't use this device as writes could cause corruption. // HwDeviceExtension->DeviceFlags[Device] = 0; } DebugPrint ((DBG_BUSSCAN, "IdePortSelectCHS: %s %d\n", __FILE__, __LINE__)); } return STATUS_SUCCESS; } NTSTATUS IdePortScanBus ( IN OUT PFDO_EXTENSION FdoExtension ) /**++ Routine Description: Scans the IDE bus (channel) for devices. It also configures the detected devices. The "safe" routines used in the procedure are not thread-safe, they use pre-allocated memory. The important steps in the enumeration of a channel are: 1. Detect the devices on the channel 2. Stop all the device queues 3. Determine and set the transfer modes and the other flags 4. Start all the device queues 5. IssueInquiry Arguments: FdoExtension: Functional device extension Return Value: STATUS_SUCCESS : if the operation succeeded Failure status : if the operation fails --**/ { NTSTATUS status; IDE_PATH_ID pathId; ULONG target; ULONG lun; PPDO_EXTENSION pdoExtension; PHW_DEVICE_EXTENSION hwDeviceExtension; PIDEDRIVER_EXTENSION ideDriverExtension; INQUIRYDATA InquiryData; IDENTIFY_DATA identifyData[MAX_IDE_TARGETID * MAX_IDE_LINE]; ULONG idDatacheckSum[MAX_IDE_TARGETID * MAX_IDE_LINE]; SPECIAL_ACTION_FLAG specialAction[MAX_IDE_TARGETID*MAX_IDE_LINE]; IDE_DEVICETYPE deviceType[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN mustBePio[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN pioByDefault[MAX_IDE_TARGETID * MAX_IDE_LINE]; UCHAR flushCommand[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN removableMedia[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN isLs120[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN noPowerDown[MAX_IDE_TARGETID * MAX_IDE_LINE]; BOOLEAN isSameDevice[MAX_IDE_TARGETID * MAX_IDE_LINE]; ULONG lastKnownGoodTimingMode[MAX_IDE_TARGETID * MAX_IDE_LINE]; ULONG savedTransferMode[MAX_IDE_TARGETID * MAX_IDE_LINE]; PULONG enableBigLba; ULONG numSlot=0; ULONG numPdoChildren; UCHAR targetModelNum[MAX_MODELNUM_SIZE+sizeof('\0')]; //extra bytes for '\0' HANDLE pageScanCodePageHandle; BOOLEAN newPdo; BOOLEAN check4EmptyChannel; BOOLEAN emptyChannel; BOOLEAN mustSucceed=TRUE; KIRQL currentIrql; BOOLEAN inSetup; PULONG waitOnPowerUp; LARGE_INTEGER tickCount; ULONG timeDiff; LARGE_INTEGER totalDeviceDetectionTime; totalDeviceDetectionTime.QuadPart = 0; // // This macro is used in IdePortScanBus // #define RefLuExt(pdoExtension, fdoExtension, pathId, removedOk, newPdo) {\ pdoExtension = RefLogicalUnitExtensionWithTag( \ fdoExtension, \ (UCHAR) pathId.b.Path, \ (UCHAR) pathId.b.TargetId, \ (UCHAR) pathId.b.Lun, \ removedOk, \ IdePortScanBus \ ); \ if (pdoExtension == NULL) { \ pdoExtension = AllocatePdoWithTag( \ fdoExtension, \ pathId, \ IdePortScanBus \ ); \ newPdo = TRUE; \ } \ } // // This macro is used in IdePortScanBus // #define UnRefLuExt(pdoExtension, fdoExtension, sync, callIoDeleteDevice, newPdo) { \ if (newPdo) { \ FreePdoWithTag( \ pdoExtension, \ sync, \ callIoDeleteDevice, \ IdePortScanBus \ ); \ } else { \ UnrefLogicalUnitExtensionWithTag ( \ fdoExtension, \ pdoExtension, \ IdePortScanBus \ ); \ } \ } // // Before getting in to this critical region, we must lock down // all the code and data because we may have stopped the paging // device! // // lock down all code that belongs to PAGESCAN // #ifdef ALLOC_PRAGMA pageScanCodePageHandle = MmLockPagableCodeSection( IdePortScanBus ); InterlockedIncrement(&IdePAGESCANLockCount); #endif ASSERT(FdoExtension); ASSERT(FdoExtension->PreAllocEnumStruct); hwDeviceExtension = FdoExtension->HwDeviceExtension; if (FdoExtension->InterruptObject == NULL) { // // we are started with no irq. it means // we have no children. it is ok to poke // at the ports directly // if (IdePortChannelEmpty (&hwDeviceExtension->BaseIoAddress1, &hwDeviceExtension->BaseIoAddress2, hwDeviceExtension->MaxIdeDevice) == FALSE) { // // this channel is started with out an irq // it was because the channel looked empty // but, now it doesn't look empty. we need // to restart with an irq resource // if (FdoExtension->RequestProperResourceInterface) { FdoExtension->RequestProperResourceInterface (FdoExtension->AttacheePdo); } else { DebugPrint((DBG_ALWAYS, "No interface to request resources. Probably a pcmcia parent\n")); } } goto done; } DebugPrint (( DBG_BUSSCAN, "IdePort: scan bus 0x%x\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress )); inSetup = IdePortInSetup(FdoExtension); DebugPrint((DBG_BUSSCAN, "ATAPI: insetup = 0x%x\n", inSetup? 1: 0 )); waitOnPowerUp = NULL; IdePortGetParameterFromServiceSubKey ( FdoExtension->DriverObject, L"WaitOnBusyOnPowerUp", REG_DWORD, TRUE, (PVOID) &waitOnPowerUp, 0 ); FdoExtension->WaitOnPowerUp = PtrToUlong(waitOnPowerUp); check4EmptyChannel = FALSE; FdoExtension->DeviceChanged = FALSE; pathId.l = 0; for (target = 0; target < hwDeviceExtension->MaxIdeTargetId; target++) { pathId.b.TargetId = target; pathId.b.Lun = 0; newPdo = FALSE; mustBePio[target] = FALSE; RefLuExt(pdoExtension, FdoExtension, pathId, TRUE, newPdo); if (!newPdo && (pdoExtension->PdoState & PDOS_DEADMEAT)) { // // device marked dead already // UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); ASSERT (FALSE); pdoExtension = NULL; } if (!pdoExtension) { DebugPrint ((DBG_ALWAYS, "ATAPI: IdePortScanBus() is unable to get pdo (%d,%d,%d)\n", pathId.b.Path, pathId.b.TargetId, pathId.b.Lun)); deviceType[target] = DeviceNotExist; continue; } if (!check4EmptyChannel) { ULONG i; NTSTATUS status; UCHAR statusByte1; ATA_PASS_THROUGH ataPassThroughData; check4EmptyChannel = TRUE; // // make sure the channel is not empty // RtlZeroMemory (&ataPassThroughData, sizeof (ataPassThroughData)); ataPassThroughData.IdeReg.bReserved = ATA_PTFLAGS_EMPTY_CHANNEL_TEST; LogBusScanStartTimer(&tickCount); status = IssueSyncAtaPassThroughSafe( FdoExtension, pdoExtension, &ataPassThroughData, FALSE, FALSE, 30, TRUE ); if (NT_SUCCESS(status)) { emptyChannel = TRUE; } else { emptyChannel = FALSE; } timeDiff = LogBusScanStopTimer(&tickCount); LogBusScanTimeDiff(FdoExtension, IdePortBootTimeRegKey[1], timeDiff); DebugPrint((DBG_SPECIAL, "BusScan: Empty Channel check for fdoe %x took %u ms\n", FdoExtension, timeDiff )); } LogBusScanStartTimer(&tickCount); if (!emptyChannel) { deviceType[target] = AtapiDetectDevice (FdoExtension, pdoExtension, identifyData + target, TRUE); if (deviceType[target] != DeviceNotExist) { SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_DEVICE_PRESENT); // // Check for special action requests for the device // GetTargetModelId((identifyData+target), targetModelNum); specialAction[target] = IdeFindSpecialDevice(targetModelNum, NULL); if (specialAction[target] == setFlagSonyMemoryStick ) { ULONG i; // SonyMemoryStick device. SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_SONY_MEMORYSTICK); // // Truncate the hardware id, so that the size of // the memory stick is not included in it. // for (i=strlen("MEMORYSTICK");iModelNumber);i++) { (identifyData+target)->ModelNumber[i+1]='\0'; } } } else { DebugPrint((DBG_BUSSCAN, "Didn't detect the device %d\n", target)); } } else { DebugPrint((DBG_SPECIAL, "BusScan: IdeDevicePresent %x detected no device %d\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, target )); // // invalidate the last boot configuration // IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTypeName[pdoExtension->TargetId], DeviceUnknown ); deviceType[target] = DeviceNotExist; } ASSERT (deviceType[target] <= DeviceNotExist); timeDiff = LogBusScanStopTimer(&tickCount); LogBusScanTimeDiff(FdoExtension, IdePortBootTimeRegKey[2+target], timeDiff); DebugPrint((DBG_SPECIAL, "BusScan: Detect device %d for %x took %u ms\n", target, FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); totalDeviceDetectionTime.QuadPart += timeDiff; ASSERT (deviceType[target] <= DeviceNotExist); ASSERT (deviceType[target] != DeviceUnknown); ASSERT (pdoExtension->TargetId == target); if (deviceType[target] != DeviceNotExist) { if (target & 1) { if (deviceType[(LONG) target - 1] != DeviceNotExist) { if (IdePortSlaveIsGhost ( FdoExtension, identifyData + target - 1, identifyData + target - 0)) { // // remove the slave device // deviceType[target] = DeviceNotExist; } } } } ASSERT (deviceType[target] <= DeviceNotExist); if (deviceType[target] == DeviceNotExist) { CLRMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_IDENTIFY_VALID); if (FdoExtension->HwDeviceExtension->DeviceFlags[target] & DFLAGS_DEVICE_PRESENT) { IDE_PATH_ID pathId; PPDO_EXTENSION pdoExtension; // // device went away // FdoExtension->DeviceChanged = TRUE; // // mark all PDOs for the missing device DEADMEAT // pathId.l = 0; pathId.b.TargetId = target; while (pdoExtension = NextLogUnitExtensionWithTag( FdoExtension, &pathId, TRUE, (PVOID) (~(ULONG_PTR)IdePortScanBus) )) { if (pdoExtension->TargetId == target) { DebugPrint((DBG_BUSSCAN, "Enum Failed for target=%d, PDOe=0x%x\n", target, pdoExtension)); KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); SETMASK (pdoExtension->PdoState, PDOS_DEADMEAT); IdeLogDeadMeatReason( pdoExtension->DeadmeatRecord.Reason, enumFailed; ); KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); UnrefPdoWithTag (pdoExtension, (PVOID) (~(ULONG_PTR)IdePortScanBus)); } else { UnrefPdoWithTag (pdoExtension, (PVOID) (~(ULONG_PTR)IdePortScanBus)); break; } } } } else { idDatacheckSum[target] = IdePortSimpleCheckSum ( 0, identifyData[target].ModelNumber, sizeof(identifyData[target].ModelNumber) ); idDatacheckSum[target] += IdePortSimpleCheckSum ( idDatacheckSum[target], identifyData[target].SerialNumber, sizeof(identifyData[target].SerialNumber) ); idDatacheckSum[target] += IdePortSimpleCheckSum ( idDatacheckSum[target], identifyData[target].FirmwareRevision, sizeof(identifyData[target].FirmwareRevision) ); if (newPdo) { // // new device // FdoExtension->DeviceChanged = TRUE; DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Found a new device. pdoe = x%x\n", pdoExtension)); } else { #ifdef ENABLE_ATAPI_VERIFIER idDatacheckSum[target] += ViIdeFakeDeviceChange(FdoExtension, target); #endif //ENABLE_ATAPI_VERIFIER if (idDatacheckSum[target] != pdoExtension->IdentifyDataCheckSum) { FdoExtension->DeviceChanged = TRUE; DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: bad bad bad user. a device is replaced by a different device. pdoe = x%x\n", pdoExtension)); // // mark the old device deadmeat // KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); SETMASK (pdoExtension->PdoState, PDOS_DEADMEAT | PDOS_NEED_RESCAN); IdeLogDeadMeatReason( pdoExtension->DeadmeatRecord.Reason, replacedByUser; ); KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); // // pretend we have seen the new device. // we will re-enum when the old one is removed. // deviceType[target] = DeviceNotExist; } } } ASSERT (deviceType[target] <= DeviceNotExist); if (deviceType[target] != DeviceNotExist) { ULONG savedIdDataCheckSum; mustBePio[target] = IdePortMustBePio ( FdoExtension, identifyData + target ); pioByDefault[target] = IdePortPioByDefaultDevice ( FdoExtension, identifyData + target ); ASSERT (deviceType[target] <= DeviceNotExist); LogBusScanStartTimer(&tickCount); flushCommand[target] = IDE_COMMAND_NO_FLUSH; // // we need this only for ata devices // if (deviceType[target] != DeviceIsAtapi) { flushCommand[target] = IdePortGetFlushCommand ( FdoExtension, pdoExtension, identifyData + target ); } timeDiff = LogBusScanStopTimer(&tickCount); DebugPrint((DBG_SPECIAL, "BusScan: Flush command for device %d at %x took %u ms\n", target, FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); ASSERT (deviceType[target] <= DeviceNotExist); removableMedia[target] = IdePortDeviceHasNonRemovableMedia ( FdoExtension, identifyData + target ); ASSERT (deviceType[target] <= DeviceNotExist); isLs120[target] = IdePortDeviceIsLs120 ( FdoExtension, identifyData + target ); ASSERT (deviceType[target] <= DeviceNotExist); noPowerDown[target] = IdePortNoPowerDown ( FdoExtension, identifyData + target ); ASSERT (deviceType[target] <= DeviceNotExist); // // init. the default value to an abnormal #. // FdoExtension->UserChoiceTransferMode[target] = 0x12345678; IdePortGetDeviceParameter( FdoExtension, IdePortRegistryUserDeviceTimingModeAllowedName[target], FdoExtension->UserChoiceTransferMode + target ); if (FdoExtension->UserChoiceTransferMode[target] == 0x12345678) { // // This value is used to decide whether a user choice was indicated // or not. This helps us to set the transfer mode to a default value // if the user doesn't choose a particular transfer mode. Otherwise we // honour the user choice. // FdoExtension->UserChoiceTransferMode[target] = UNINITIALIZED_TRANSFER_MODE; // // we know the register doesn't have what we are looking for // set the default atapi-override setting to a value // that will force pio only on atapi device // FdoExtension->UserChoiceTransferModeForAtapiDevice[target] = PIO_SUPPORT; } else { // // user acutally picked the transfer mode settings. // set the default atapi-override setting to a value (-1) // that will not affect the user choice. FdoExtension->UserChoiceTransferModeForAtapiDevice[target] = MAXULONG; } // // get the previous mode // IdePortGetDeviceParameter( FdoExtension, IdePortRegistryDeviceTimingModeName[target], savedTransferMode+target ); savedIdDataCheckSum = 0; IdePortGetDeviceParameter( FdoExtension, IdePortRegistryIdentifyDataChecksum[target], &savedIdDataCheckSum ); ASSERT (deviceType[target] <= DeviceNotExist); // // figure out what transfer mode we can use // if (savedIdDataCheckSum == idDatacheckSum[target]) { // // same device. if we program the same transfer mode then // we can skip the DMA test // isSameDevice[target] = TRUE; // // it is the same device, use // the lastKnownGoodTimingMode // in the registry // lastKnownGoodTimingMode[target] = MAXULONG; IdePortGetDeviceParameter( FdoExtension, IdePortRegistryDeviceTimingModeAllowedName[target], lastKnownGoodTimingMode + target ); } else { isSameDevice[target] = FALSE; lastKnownGoodTimingMode[target] = MAXULONG; } ASSERT (deviceType[target] <= DeviceNotExist); FdoExtension->TimingModeAllowed[target] = lastKnownGoodTimingMode[target] & FdoExtension->UserChoiceTransferMode[target]; // // TransferModeMask is initially 0. // FdoExtension->TimingModeAllowed[target] &= ~(hwDeviceExtension-> DeviceParameters[target].TransferModeMask); if (pdoExtension->CrcErrorCount >= PDO_UDMA_CRC_ERROR_LIMIT) { // //Reset the error count // pdoExtension->CrcErrorCount =0; } DebugPrint ((DBG_XFERMODE, "TMAllowed=%x, TMMask=%x, UserChoice=%x\n", FdoExtension->TimingModeAllowed[target], hwDeviceExtension->DeviceParameters[target].TransferModeMask, FdoExtension->UserChoiceTransferMode[target])); ASSERT (deviceType[target] <= DeviceNotExist); } UnRefLuExt(pdoExtension, FdoExtension, TRUE, TRUE, newPdo); pdoExtension = NULL; } #ifdef IDE_MEASURE_BUSSCAN_SPEED if (FdoExtension->BusScanTime == 0) { FdoExtension->BusScanTime = totalDeviceDetectionTime.LowPart; } #endif // IDE_MEASURE_BUSSCAN_SPEED #ifdef ENABLE_48BIT_LBA // // Enable big lba support by default // FdoExtension->EnableBigLba = 1; #endif // if (!deviceChanged && !DBG) { // // // // // didn't find anything different than before // // // return STATUS_SUCCESS; // } DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: detect a change of device...re-initializing\n")); // ISSUE: check if device got removed!!! // // Begin of a critial region // must stop all children, do the stuff, and restart all children // // // cycle through all children and stop their device queue // LogBusScanStartTimer(&tickCount); pathId.l = 0; numPdoChildren = 0; status = STATUS_SUCCESS; while (pdoExtension = NextLogUnitExtensionWithTag( FdoExtension, &pathId, TRUE, IdePortScanBus )) { DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: stopping pdo 0x%x\n", pdoExtension)); status = DeviceStopDeviceQueueSafe (pdoExtension, PDOS_QUEUE_FROZEN_BY_PARENT, TRUE); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: stopped pdo 0x%x\n", pdoExtension)); UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); numPdoChildren++; if (!NT_SUCCESS(status)) { break; } } if (NT_SUCCESS(status)) { BOOLEAN foundAtLeastOneIdeDevice = FALSE; BOOLEAN useDma; IDE_PATH_ID pathId; DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: all children are stoped\n")); pathId.l = 0; for (target = 0; target < hwDeviceExtension->MaxIdeTargetId; target++) { ASSERT (deviceType[target] <= DeviceNotExist); if (deviceType[target] != DeviceNotExist) { pathId.b.TargetId = target; newPdo = FALSE; RefLuExt(pdoExtension, FdoExtension, pathId, TRUE, newPdo); if (!pdoExtension) { continue; } ASSERT (pdoExtension); foundAtLeastOneIdeDevice = TRUE; SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_DEVICE_PRESENT); if (deviceType[target] == DeviceIsAtapi) { SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_ATAPI_DEVICE); useDma=FALSE; // // skip the dvd test, if ModeSense command is not to be // sent to the device or if we are in setup // if (inSetup) { mustBePio[target] = TRUE; } else if ((specialAction[target] != skipModeSense) && (!pioByDefault[target])) { useDma = IdePortDmaCdromDrive(FdoExtension, pdoExtension, TRUE ); } // // Don't force PIO if we have seen this before. If it was doing PIO // TimingModeAllowed would reflect that. // Set UserChoiceForAtapi to 0xffffffff // This won't anyway affect the user choice // if ( useDma) { DebugPrint((DBG_BUSSCAN, "IdePortScanBus: USE DMA FOR target %d\n", target )); FdoExtension->UserChoiceTransferModeForAtapiDevice[target] = MAXULONG; } FdoExtension->TimingModeAllowed[target] &= FdoExtension->UserChoiceTransferModeForAtapiDevice[target]; } // // allow LS-120 Format Command // if (isLs120[target]) { SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_LS120_FORMAT); } RtlMoveMemory ( hwDeviceExtension->IdentifyData + target, identifyData + target, sizeof (IDENTIFY_DATA) ); // // Always re-use identify data // This will get cleared if it is a removable media // The queue is stopped. Now I can safely set this flag. // SETMASK (hwDeviceExtension->DeviceFlags[target], DFLAGS_IDENTIFY_VALID); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Calling InitHwExtWithIdentify\n")); // // IdentifyValid flag should be cleared, if it is a removable media // InitHwExtWithIdentify( hwDeviceExtension, target, (UCHAR) (deviceType[target] == DeviceIsAtapi ? IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY), hwDeviceExtension->IdentifyData + target, removableMedia[target] ); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Calling IdePortSelectCHS\n")); IdePortSelectCHS ( FdoExtension, target, identifyData + target ); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: back from IdePortSelectCHS\n")); UnRefLuExt(pdoExtension, FdoExtension, TRUE, TRUE, newPdo); } else { hwDeviceExtension->DeviceFlags[target] = 0; } } if (foundAtLeastOneIdeDevice) { DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Calling AnalyzeDeviceCapabilities\n")); // // could move this out of the critial region // AnalyzeDeviceCapabilities ( FdoExtension, mustBePio ); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Calling AtapiSelectTransferMode\n")); // // could move this out of the critial region // AtapiSyncSelectTransferMode ( FdoExtension, hwDeviceExtension, FdoExtension->TimingModeAllowed ); DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: Calling AtapiHwInitialize\n")); AtapiHwInitialize ( FdoExtension->HwDeviceExtension, flushCommand ); } } else { // // unable to stop all children, so force an buscheck to try again // // IoInvalidateDeviceRelations ( // FdoExtension->AttacheePdo, // BusRelations // ); } // // cycle through all children and restart their device queue // pathId.l = 0; numPdoChildren = 0; while (pdoExtension = NextLogUnitExtensionWithTag( FdoExtension, &pathId, TRUE, IdePortScanBus )) { DebugPrint ((DBG_BUSSCAN, "IdePortScanBus: re-start pdo 0x%x\n", pdoExtension)); DeviceStartDeviceQueue (pdoExtension, PDOS_QUEUE_FROZEN_BY_PARENT); UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); numPdoChildren++; } timeDiff = LogBusScanStopTimer(&tickCount); LogBusScanTimeDiff(FdoExtension, IdePortBootTimeRegKey[4], timeDiff); DebugPrint((DBG_SPECIAL, "BusScan : Critical section %x took %u ms\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); if (NT_SUCCESS(status)) { // // second stage scanning // pathId.l = 0; LogBusScanStartTimer(&tickCount); for (target = 0; target < hwDeviceExtension->MaxIdeTargetId; target++) { if (deviceType[target] == DeviceNotExist) { continue; } pathId.b.TargetId = target; for (lun = 0; lun < FdoExtension->MaxLuCount; lun++) { LARGE_INTEGER tempTickCount; pathId.b.Lun = lun; newPdo = FALSE; RefLuExt(pdoExtension, FdoExtension, pathId, TRUE, newPdo); if (!pdoExtension) { continue; } ASSERT (pdoExtension); if (lun == 0) { #if defined (ALWAYS_VERIFY_DMA) #undef ALWAYS_VERIFY_DMA #define ALWAYS_VERIFY_DMA TRUE #else #define ALWAYS_VERIFY_DMA FALSE #endif ASSERT (ALL_MODE_SUPPORT != MAXULONG); if ((FdoExtension->HwDeviceExtension-> DeviceParameters[target].TransferModeSelected & DMA_SUPPORT) || ALWAYS_VERIFY_DMA) { ULONG mode = FdoExtension->HwDeviceExtension-> DeviceParameters[target].TransferModeSelected; // // if lastKnownGoodTimingMode is MAX_ULONG, it means // we have never seen this device before and the user // hasn't said anything about what dma mode to use. // // we have chosen to use dma because all the software // detectable parameters (identify dma, pci config data) // looks good for DMA. // // the only thing that can stop us from using DMA now // is bad device. before we go on, do a little test // to verify dma is ok // // could re-use the inquiry data obtained here. // // // skip the test if we have already seen the device // LogBusScanStartTimer(&tempTickCount); if (isSameDevice[target] && mode == savedTransferMode[target]) { DebugPrint((DBG_BUSSCAN, "Skip dma test for %d\n", target)); } else { IdePortVerifyDma ( pdoExtension, deviceType[target] ); } timeDiff = LogBusScanStopTimer(&tempTickCount); DebugPrint((DBG_SPECIAL, "ScanBus: VerifyDma for %x device %d took %u ms\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, target, timeDiff )); } // // Initialize numSlot to 0 // numSlot=0; // // If this is in the bad cd-rom drive list, then don't // send these Mode Sense command. The drive might lock up. // LogBusScanStartTimer(&tempTickCount); if (specialAction[target] != skipModeSense) { // // Non-CD device // numSlot = IdePortQueryNonCdNumLun ( FdoExtension, pdoExtension, FALSE ); } else { DebugPrint((DBG_BUSSCAN, "Skip modesense\n")); } timeDiff = LogBusScanStopTimer(&tempTickCount); DebugPrint((DBG_SPECIAL, "ScanBus: Initialize Luns for %x device %d took %u ms\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, target, timeDiff )); AtapiHwInitializeMultiLun ( FdoExtension->HwDeviceExtension, pdoExtension->TargetId, numSlot ); } if (pdoExtension) { LogBusScanStartTimer(&tempTickCount); status = IssueInquirySafe(FdoExtension, pdoExtension, &InquiryData, TRUE); timeDiff = LogBusScanStopTimer(&tempTickCount); DebugPrint((DBG_SPECIAL, "ScanBus: Inquiry %x for Lun %d device %d took %u ms\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, lun, target, timeDiff )); if (NT_SUCCESS(status) || (status == STATUS_DATA_OVERRUN)) { DeviceInitDeviceType ( pdoExtension, &InquiryData ); // // Init Ids String for PnP Query ID // DeviceInitIdStrings ( pdoExtension, deviceType[target], &InquiryData, identifyData + target ); // // Clear rescan flag. Since this LogicalUnit will not be freed, // the IOCTL_SCSI_MINIPORT requests can safely attach. // CLRMASK (pdoExtension->LuFlags, PD_RESCAN_ACTIVE); DebugPrint((DBG_BUSSCAN,"IdePortScanBus: Found device at ")); DebugPrint((DBG_BUSSCAN," Bus %d", pdoExtension->PathId)); DebugPrint((DBG_BUSSCAN," Target Id %d", pdoExtension->TargetId)); DebugPrint((DBG_BUSSCAN," LUN %d\n", pdoExtension->Lun)); if (noPowerDown[target] || (pdoExtension->ScsiDeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)) { KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); SETMASK (pdoExtension->PdoState, PDOS_NO_POWER_DOWN); KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); } pdoExtension->IdentifyDataCheckSum = idDatacheckSum[target]; // // done using the current logical unit extension // UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); pdoExtension = NULL; } } else { ASSERT (pdoExtension); DebugPrint ((DBG_ALWAYS, "IdePort: unable to create new pdo\n")); } if (pdoExtension) { if (!newPdo) { DebugPrint((DBG_BUSSCAN, "IdePortScanBus: pdoe 0x%x is missing. (physically removed)\n", pdoExtension)); } // // get the pdo states // KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); SETMASK (pdoExtension->PdoState, PDOS_DEADMEAT); KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); UnRefLuExt(pdoExtension, FdoExtension, TRUE, TRUE, newPdo); pdoExtension = NULL; } } } // // Get all PDOs ready // pathId.l = 0; while (pdoExtension = NextLogUnitExtensionWithTag( FdoExtension, &pathId, FALSE, IdePortScanBus )) { // // PO Idle Timer // DeviceRegisterIdleDetection ( pdoExtension, DEVICE_DEFAULT_IDLE_TIMEOUT, DEVICE_DEFAULT_IDLE_TIMEOUT ); CLRMASK (pdoExtension->DeviceObject->Flags, DO_DEVICE_INITIALIZING); UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); } // // Update the device map. // ideDriverExtension = IoGetDriverObjectExtension( FdoExtension->DriverObject, DRIVER_OBJECT_EXTENSION_ID ); IdeBuildDeviceMap(FdoExtension, &ideDriverExtension->RegistryPath); } timeDiff = LogBusScanStopTimer(&tickCount); LogBusScanTimeDiff(FdoExtension, IdePortBootTimeRegKey[5], timeDiff); DebugPrint((DBG_SPECIAL, "BusScan: Last Stage scanning for %x took %u ms\n", FdoExtension->IdeResource.TranslatedCommandBaseAddress, timeDiff )); // // save current transfer mode setting in the registry // for (target = 0; target < hwDeviceExtension->MaxIdeTargetId; target++) { ULONG mode; pdoExtension = RefLogicalUnitExtensionWithTag( FdoExtension, 0, (UCHAR) target, 0, TRUE, IdePortScanBus ); if (pdoExtension) { mode = FdoExtension->HwDeviceExtension->DeviceParameters[target].TransferModeSelected; if (pdoExtension->DmaTransferTimeoutCount >= PDO_DMA_TIMEOUT_LIMIT) { mode &= PIO_SUPPORT; lastKnownGoodTimingMode[target] &= PIO_SUPPORT; } UnrefLogicalUnitExtensionWithTag ( FdoExtension, pdoExtension, IdePortScanBus ); IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTimingModeName[target], mode ); IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTimingModeAllowedName[target], lastKnownGoodTimingMode[target] ); IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryIdentifyDataChecksum[target], idDatacheckSum[target] ); } else { IdePortSaveDeviceParameter( FdoExtension, IdePortRegistryDeviceTimingModeName[target], 0 ); } } done: // // unlock BUSSCAN code pages // #ifdef ALLOC_PRAGMA InterlockedDecrement(&IdePAGESCANLockCount); MmUnlockPagableImageSection( pageScanCodePageHandle ); #endif return STATUS_SUCCESS; } BOOLEAN IdePreAllocEnumStructs ( IN PFDO_EXTENSION FdoExtension ) /**++ Routine Description: Pre-Allocates Memory for structures used during enumertion. This is not protected by a lock. Thus if multiple threads cannot use the structures at the same time. Any routine using these structures should be aware of this fact. Arguments: FdoExtension : Functional Device Extension Return Value: TRUE: if allocations succeeded. FALSE: if any of the allocations failed --**/ { PENUMERATION_STRUCT enumStruct; PIRP irp1; ULONG deviceRelationsSize; PULONG DataBuffer; ULONG currsize=0; PIDE_WORK_ITEM_CONTEXT workItemContext; PAGED_CODE(); // // Lock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 1, 0) == 0); if (FdoExtension->PreAllocEnumStruct) { // // Unlock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 0, 1) == 1); return TRUE; } enumStruct = ExAllocatePool(NonPagedPool, sizeof(ENUMERATION_STRUCT)); if (enumStruct == NULL) { // // Unlock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 0, 1) == 1); ASSERT(FdoExtension->EnumStructLock == 0); return FALSE; } currsize += sizeof(ENUMERATION_STRUCT); RtlZeroMemory(enumStruct, sizeof(ENUMERATION_STRUCT)); // // Allocate ATaPassThru context // enumStruct->Context = ExAllocatePool(NonPagedPool, sizeof (ATA_PASSTHROUGH_CONTEXT)); if (enumStruct->Context == NULL) { goto getout; } currsize += sizeof(ATA_PASSTHROUGH_CONTEXT); // // Allocate the WorkItemContext for the enumeration // ASSERT(enumStruct->EnumWorkItemContext == NULL); enumStruct->EnumWorkItemContext = ExAllocatePool (NonPagedPool, sizeof(IDE_WORK_ITEM_CONTEXT) ); if (enumStruct->EnumWorkItemContext == NULL) { goto getout; } currsize += sizeof(IDE_WORK_ITEM_CONTEXT); // // Allocate the WorkItem // workItemContext = (PIDE_WORK_ITEM_CONTEXT) (enumStruct->EnumWorkItemContext); workItemContext->WorkItem = IoAllocateWorkItem(FdoExtension->DeviceObject); if (workItemContext->WorkItem == NULL) { goto getout; } // // StopQueu Context, used to stop the device queue // enumStruct->StopQContext = ExAllocatePool(NonPagedPool, sizeof (PDO_STOP_QUEUE_CONTEXT)); if (enumStruct->StopQContext == NULL) { goto getout; } currsize += sizeof(PDO_STOP_QUEUE_CONTEXT); // // Sense Info buffer // enumStruct->SenseInfoBuffer = ExAllocatePool( NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); if (enumStruct->SenseInfoBuffer == NULL) { goto getout; } currsize += SENSE_BUFFER_SIZE; // // Srb to send pass thru requests // enumStruct->Srb = ExAllocatePool (NonPagedPool, sizeof (SCSI_REQUEST_BLOCK)); if (enumStruct->Srb == NULL) { goto getout; } currsize += sizeof(SCSI_REQUEST_BLOCK); // // irp for pass thru requests // irp1 = IoAllocateIrp ( (CCHAR) (PREALLOC_STACK_LOCATIONS), FALSE ); if (irp1 == NULL) { goto getout; } enumStruct->Irp1 = irp1; // // Data buffer to hold inquiry data or Identify data // enumStruct->DataBufferSize = sizeof(ATA_PASS_THROUGH)+INQUIRYDATABUFFERSIZE+ sizeof(IDENTIFY_DATA); currsize += enumStruct->DataBufferSize; DataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, enumStruct->DataBufferSize); if (DataBuffer == NULL) { enumStruct->DataBufferSize=0; goto getout; } enumStruct->DataBuffer = DataBuffer; enumStruct->MdlAddress = IoAllocateMdl( DataBuffer, enumStruct->DataBufferSize, FALSE, FALSE, (PIRP) NULL ); if (enumStruct->MdlAddress == NULL) { goto getout; } MmBuildMdlForNonPagedPool(enumStruct->MdlAddress); FdoExtension->PreAllocEnumStruct=enumStruct; DebugPrint((DBG_BUSSCAN, "BusScan: TOTAL PRE_ALLOCED MEM=%x\n", currsize)); // // Unlock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 0, 1) == 1); return TRUE; getout: // // Some allocations failed. Free the already allocated ones. // IdeFreeEnumStructs(enumStruct); FdoExtension->PreAllocEnumStruct=NULL; // // Unlock // ASSERT(InterlockedCompareExchange(&(FdoExtension->EnumStructLock), 0, 1) == 1); ASSERT(FALSE); return FALSE; } VOID IdeFreeEnumStructs( PENUMERATION_STRUCT enumStruct ) /**++ Routine Description: Frees the pre-allocated memory. Arguments Pointer to the Enumeration Structure that is to be freed Return Value: None --**/ { PAGED_CODE(); if (enumStruct != NULL) { if (enumStruct->Context) { ExFreePool (enumStruct->Context); } if (enumStruct->SenseInfoBuffer) { ExFreePool (enumStruct->SenseInfoBuffer); } if (enumStruct->Srb) { ExFreePool(enumStruct->Srb); } if (enumStruct->StopQContext) { ExFreePool(enumStruct->StopQContext); } if (enumStruct->DataBuffer) { ExFreePool(enumStruct->DataBuffer); } if (enumStruct->Irp1) { IoFreeIrp(enumStruct->Irp1); } if (enumStruct->MdlAddress) { ExFreePool(enumStruct->MdlAddress); } if (enumStruct->EnumWorkItemContext) { PIDE_WORK_ITEM_CONTEXT workItemContext = (PIDE_WORK_ITEM_CONTEXT)enumStruct-> EnumWorkItemContext; if (workItemContext->WorkItem) { IoFreeWorkItem(workItemContext->WorkItem); } ExFreePool (enumStruct->EnumWorkItemContext); } ExFreePool(enumStruct); enumStruct = NULL; } }