/*++ Copyright (C) 1997-99 Microsoft Corporation Module Name: detect.c Abstract: This contain legacy detection routines Author: Joe Dai (joedai) Environment: kernel mode only Notes: Revision History: --*/ #include "ideport.h" #if !defined(NO_LEGACY_DRIVERS) #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, IdePortDetectLegacyController) #pragma alloc_text(INIT, IdePortCreateDetectionList) #pragma alloc_text(INIT, IdePortTranslateAddress) #pragma alloc_text(INIT, IdePortFreeTranslatedAddress) #pragma alloc_text(INIT, IdePortDetectAlias) #endif // ALLOC_PRAGMA NTSTATUS IdePortDetectLegacyController ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Detect legacy IDE controllers and report them to PnP Arguments: DriverObject - this driver's driver object RegistryPath - this driver's registry path Return Value: NT Status --*/ { ULONG cmResourceListSize; PCM_RESOURCE_LIST cmResourceList = NULL; PCM_FULL_RESOURCE_DESCRIPTOR cmFullResourceDescriptor; PCM_PARTIAL_RESOURCE_LIST cmPartialResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR cmPartialDescriptors; BOOLEAN conflictDetected; BOOLEAN resourceIsCmdPort; PDEVICE_OBJECT detectedPhysicalDeviceObject; PFDO_EXTENSION fdoExtension = NULL; UNICODE_STRING deviceClassName; NTSTATUS status; PDETECTION_PORT detectionPort; ULONG numPort; ULONG portNumber; ULONG deviceNumber; ULONG descriptorNumber; ULONG cmdAddressSpace; ULONG ctrlAddressSpace; PUCHAR cmdRegBase; PUCHAR ctrlRegBase; IDE_REGISTERS_1 baseIoAddress1; IDE_REGISTERS_2 baseIoAddress2; PHYSICAL_ADDRESS cmdRegMemoryBase; PHYSICAL_ADDRESS ctrlRegMemoryBase; UCHAR statusByte; ULONG baseIoAddress1Length; ULONG baseIoAddress2Length; ULONG maxIdeDevice; UCHAR altMasterStatus; UCHAR altSlaveStatus; #if !defined (ALWAYS_DO_LEGACY_DETECTION) if (!IdePortOkToDetectLegacy(DriverObject)) { // // legacy detection is not enabled // return STATUS_SUCCESS; } #endif // // make up a list of popular legacy I/O ports // status = IdePortCreateDetectionList ( DriverObject, &detectionPort, &numPort ); if (!NT_SUCCESS(status)) { goto GetOut; } // // Resource Requirement List // cmResourceListSize = sizeof (CM_RESOURCE_LIST) + sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR) * (((!IsNEC_98) ? 3 : 12) - 1); cmResourceList = ExAllocatePool (PagedPool, cmResourceListSize); if (cmResourceList == NULL){ status = STATUS_NO_MEMORY; goto GetOut; } RtlZeroMemory(cmResourceList, cmResourceListSize); RtlInitUnicodeString(&deviceClassName, L"ScsiAdapter"); for (portNumber=0; portNumberCount = 1; cmFullResourceDescriptor = cmResourceList->List; cmFullResourceDescriptor->InterfaceType = Isa; cmFullResourceDescriptor->BusNumber = 0; cmPartialResourceList = &cmFullResourceDescriptor->PartialResourceList; cmPartialResourceList->Version = 1; cmPartialResourceList->Revision = 1; cmPartialResourceList->Count = 3; cmPartialDescriptors = cmPartialResourceList->PartialDescriptors; cmPartialDescriptors[0].Type = CmResourceTypePort; cmPartialDescriptors[0].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[0].Flags = CM_RESOURCE_PORT_IO | (!Is98LegacyIde(&baseIoAddress1)? CM_RESOURCE_PORT_10_BIT_DECODE : CM_RESOURCE_PORT_16_BIT_DECODE); cmPartialDescriptors[0].u.Port.Length = baseIoAddress1Length; cmPartialDescriptors[0].u.Port.Start.QuadPart = detectionPort[portNumber].CommandRegisterBase; cmPartialDescriptors[1].Type = CmResourceTypePort; cmPartialDescriptors[1].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[1].Flags = CM_RESOURCE_PORT_IO | (!Is98LegacyIde(&baseIoAddress1)? CM_RESOURCE_PORT_10_BIT_DECODE : CM_RESOURCE_PORT_16_BIT_DECODE); cmPartialDescriptors[1].u.Port.Length = 1; cmPartialDescriptors[1].u.Port.Start.QuadPart = detectionPort[portNumber].ControlRegisterBase; cmPartialDescriptors[2].Type = CmResourceTypeInterrupt; cmPartialDescriptors[2].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[2].Flags = CM_RESOURCE_INTERRUPT_LATCHED; cmPartialDescriptors[2].u.Interrupt.Level = detectionPort[portNumber].IrqLevel; cmPartialDescriptors[2].u.Interrupt.Vector = detectionPort[portNumber].IrqLevel; cmPartialDescriptors[2].u.Interrupt.Affinity = -1; if (Is98LegacyIde(&baseIoAddress1)) { ULONG resourceCount; ULONG commandRegisters; commandRegisters = detectionPort[portNumber].CommandRegisterBase + 2; resourceCount = 3; while (commandRegisters < (IDE_NEC98_COMMAND_PORT_ADDRESS + 0x10)) { cmPartialDescriptors[resourceCount].Type = CmResourceTypePort; cmPartialDescriptors[resourceCount].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[resourceCount].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; cmPartialDescriptors[resourceCount].u.Port.Length = 1; cmPartialDescriptors[resourceCount].u.Port.Start.QuadPart = commandRegisters; resourceCount++; commandRegisters += 2; } cmPartialDescriptors[resourceCount].Type = CmResourceTypePort; cmPartialDescriptors[resourceCount].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[resourceCount].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; cmPartialDescriptors[resourceCount].u.Port.Length = 2; cmPartialDescriptors[resourceCount].u.Port.Start.QuadPart = (ULONG_PTR)SELECT_IDE_PORT; resourceCount++; cmPartialDescriptors[resourceCount].Type = CmResourceTypePort; cmPartialDescriptors[resourceCount].ShareDisposition = CmResourceShareDeviceExclusive; cmPartialDescriptors[resourceCount].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; cmPartialDescriptors[resourceCount].u.Port.Length = 1; cmPartialDescriptors[resourceCount].u.Port.Start.QuadPart = (ULONG_PTR)SELECT_IDE_PORT + 3; resourceCount++; cmPartialResourceList->Count = resourceCount; } // // check to see if the resource is available // if not, assume no legacy IDE controller // is at the this location // for (deviceNumber=0; deviceNumber<2; deviceNumber++) { status = IoReportResourceForDetection ( DriverObject, cmResourceList, cmResourceListSize, NULL, NULL, 0, &conflictDetected ); if (NT_SUCCESS(status) && !conflictDetected) { // // got our resources // break; } else { if (NT_SUCCESS(status)) { IoReportResourceForDetection ( DriverObject, NULL, 0, NULL, NULL, 0, &conflictDetected ); status = STATUS_UNSUCCESSFUL; } // // try 16 bit decode // cmPartialDescriptors[0].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; cmPartialDescriptors[1].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; if (Is98LegacyIde(&baseIoAddress1)) { ULONG k; for (k=3; k<12; k++) { cmPartialDescriptors[k].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; } } } } if (!NT_SUCCESS(status)) { continue; } // // translate the i/o port via Hal // status = STATUS_SUCCESS; if (Is98LegacyIde(&baseIoAddress1)) { for (descriptorNumber=3; descriptorNumber<12; descriptorNumber++) { cmdRegBase = NULL; cmdAddressSpace = IO_SPACE; status = IdePortTranslateAddress ( cmFullResourceDescriptor->InterfaceType, cmFullResourceDescriptor->BusNumber, cmPartialDescriptors[descriptorNumber].u.Port.Start, cmPartialDescriptors[descriptorNumber].u.Port.Length, &cmdAddressSpace, &cmdRegBase, &cmdRegMemoryBase ); if (!NT_SUCCESS(status)) { break; } } } if (NT_SUCCESS(status)) { cmdRegBase = NULL; ctrlRegBase = NULL; cmdAddressSpace = IO_SPACE; status = IdePortTranslateAddress ( cmFullResourceDescriptor->InterfaceType, cmFullResourceDescriptor->BusNumber, cmPartialDescriptors[0].u.Port.Start, cmPartialDescriptors[0].u.Port.Length, &cmdAddressSpace, &cmdRegBase, &cmdRegMemoryBase ); } if (NT_SUCCESS(status)) { ctrlRegBase = NULL; ctrlAddressSpace = IO_SPACE; status = IdePortTranslateAddress ( cmFullResourceDescriptor->InterfaceType, cmFullResourceDescriptor->BusNumber, cmPartialDescriptors[1].u.Port.Start, cmPartialDescriptors[1].u.Port.Length, &ctrlAddressSpace, &ctrlRegBase, &ctrlRegMemoryBase ); } if (NT_SUCCESS(status)) { // // 2nd build io address structure. // AtapiBuildIoAddress ( cmdRegBase, ctrlRegBase, &baseIoAddress1, &baseIoAddress2, &baseIoAddress1Length, &baseIoAddress2Length, &maxIdeDevice, NULL); // // The IBM Aptiva ide channel with the external cdrom doesn't power up with any device selected // we must select a device; otherwise, we get a 0xff from all IO ports // SelectIdeDevice(&baseIoAddress1, 0, 0); altMasterStatus = IdePortInPortByte(baseIoAddress2.DeviceControl); SelectIdeDevice(&baseIoAddress1, 1, 0); altSlaveStatus = IdePortInPortByte(baseIoAddress2.DeviceControl); if ((!Is98LegacyIde(&baseIoAddress1)) && (altMasterStatus == 0xff) && (altSlaveStatus == 0xff)) { // // the alternate status byte is 0xff, // guessing we have a SCSI adapter (DPT) that emulate IDE controller // say the channel is empty, let the real SCSI driver picks up // the controller // status = STATUS_UNSUCCESSFUL; // // Note: The IDE port on SB16/AWE32 does not have the alternate status // register. Because of this alternate status test, we will fail to // detect this IDE port. However, this IDE port should be enumerated // by ISA-PnP bus driver. // } else if (IdePortChannelEmpty (&baseIoAddress1, &baseIoAddress2, maxIdeDevice)) { // // channel looks empty // status = STATUS_UNSUCCESSFUL; } else { BOOLEAN deviceFound; IDENTIFY_DATA IdentifyData; ULONG i; for (i=0; iList; for (i=0; iCount; i++) { partialResourceList = &(fullResourceList->PartialResourceList); partialDescriptors = fullResourceList->PartialResourceList.PartialDescriptors; for (j=0; jCount; j++) { resourceIsCmdPort = FALSE; if (!Is98LegacyIde(&baseIoAddress1)) { if ((partialDescriptors[j].Type == CmResourceTypePort) && (partialDescriptors[j].u.Port.Length == baseIoAddress1Length)) { resourceIsCmdPort = TRUE; } } else { if ((partialDescriptors[j].Type == CmResourceTypePort) && (partialDescriptors[j].u.Port.Start.QuadPart == IDE_NEC98_COMMAND_PORT_ADDRESS)) { resourceIsCmdPort = TRUE; } else if ((partialDescriptors[j].Type == CmResourceTypePort) && (partialDescriptors[j].u.Port.Start.QuadPart != IDE_NEC98_COMMAND_PORT_ADDRESS) && (partialDescriptors[j].u.Port.Start.QuadPart != (IDE_NEC98_COMMAND_PORT_ADDRESS + 0x10C))) { // // This is not the base port address for Legacy ide on NEC98; // continue; } } if (resourceIsCmdPort) { if (cmdAddressSpace == MEMORY_SPACE) { partialDescriptors[j].Type = CmResourceTypeMemory; partialDescriptors[j].u.Memory.Start = cmdRegMemoryBase; partialDescriptors[j].u.Memory.Length = partialDescriptors[j].u.Port.Length; } else { partialDescriptors[j].u.Port.Start.QuadPart = (ULONG_PTR) cmdRegBase; } } else if ((partialDescriptors[j].Type == CmResourceTypePort) && (partialDescriptors[j].u.Port.Length == 1)) { if (ctrlAddressSpace == MEMORY_SPACE) { partialDescriptors[j].Type = CmResourceTypeMemory; partialDescriptors[j].u.Memory.Start = ctrlRegMemoryBase; partialDescriptors[j].u.Memory.Length = partialDescriptors[j].u.Port.Length; } else { partialDescriptors[j].u.Port.Start.QuadPart = (ULONG_PTR) ctrlRegBase; } } else if (partialDescriptors[j].Type == CmResourceTypeInterrupt) { partialDescriptors[j].u.Interrupt.Vector = HalGetInterruptVector(fullResourceList->InterfaceType, fullResourceList->BusNumber, partialDescriptors[j].u.Interrupt.Level, partialDescriptors[j].u.Interrupt.Vector, (PKIRQL) &partialDescriptors[j].u.Interrupt.Level, &partialDescriptors[j].u.Interrupt.Affinity); } } fullResourceList = (PCM_FULL_RESOURCE_DESCRIPTOR) (partialDescriptors + j); } // // start the FDO // status = ChannelStartChannel (fdoExtension, cmResourceList); // callee is keeping this if no error } if (!NT_SUCCESS (status)) { // // go through the remove sequence // if (fdoExtension) { ChannelRemoveChannel (fdoExtension); IoDetachDevice (fdoExtension->AttacheeDeviceObject); IoDeleteDevice (fdoExtension->DeviceObject); } DebugPrint ((0, "IdePort: Unable to start detected device\n")); ASSERT (FALSE); } else { IoInvalidateDeviceRelations ( fdoExtension->AttacheePdo, BusRelations ); } } } } GetOut: if (cmResourceList) { ExFreePool (cmResourceList); } if (detectionPort) { ExFreePool (detectionPort); } return status; } //IdePortDetectLegacyController NTSTATUS IdePortCreateDetectionList ( IN PDRIVER_OBJECT DriverObject, OUT PDETECTION_PORT *DetectionPort, OUT PULONG NumPort ) /*++ Routine Description: create a list of popular legacy ports Arguments: DriverObject - this driver's driver object DetectionPort - pointer to port list NumPort - number of ports in the list Return Value: NT Status --*/ { NTSTATUS status; CCHAR deviceBuffer[50]; ANSI_STRING ansiString; UNICODE_STRING subKeyPath; HANDLE subServiceKey; PDETECTION_PORT detectionPort; ULONG numDevices; ULONG i; ULONG j; CUSTOM_DEVICE_PARAMETER customDeviceParameter; PCONFIGURATION_INFORMATION configurationInformation = IoGetConfigurationInformation(); numDevices = 0; status = STATUS_SUCCESS; #ifdef DRIVER_PARAMETER_REGISTRY_SUPPORT // // look for non-standard legacy port setting in the registry // 9 do { sprintf (deviceBuffer, "Parameters\\Device%d", numDevices); RtlInitAnsiString(&ansiString, deviceBuffer); status = RtlAnsiStringToUnicodeString(&subKeyPath, &ansiString, TRUE); if (NT_SUCCESS(status)) { subServiceKey = IdePortOpenServiceSubKey ( DriverObject, &subKeyPath ); RtlFreeUnicodeString (&subKeyPath); if (subServiceKey) { numDevices++; IdePortCloseServiceSubKey ( subServiceKey ); } else { status = STATUS_UNSUCCESSFUL; } } } while (NT_SUCCESS(status)); #endif // DRIVER_PARAMETER_REGISTRY_SUPPORT // // always have at least 4 to return // detectionPort = ExAllocatePool ( PagedPool, (numDevices + 4) * sizeof (DETECTION_PORT) ); if (detectionPort) { for (i = j = 0; i < numDevices; i++) { #ifdef DRIVER_PARAMETER_REGISTRY_SUPPORT // // look for non-standard legacy port setting in the registry // sprintf (deviceBuffer, "Parameters\\Device%d", i); RtlInitAnsiString(&ansiString, deviceBuffer); status = RtlAnsiStringToUnicodeString(&subKeyPath, &ansiString, TRUE); if (NT_SUCCESS(status)) { subServiceKey = IdePortOpenServiceSubKey ( DriverObject, &subKeyPath ); RtlFreeUnicodeString (&subKeyPath); if (subServiceKey) { RtlZeroMemory ( &customDeviceParameter, sizeof (CUSTOM_DEVICE_PARAMETER) ); IdeParseDeviceParameters ( subServiceKey, &customDeviceParameter ); if (customDeviceParameter.CommandRegisterBase) { detectionPort[j].CommandRegisterBase = customDeviceParameter.CommandRegisterBase; detectionPort[j].ControlRegisterBase = customDeviceParameter.CommandRegisterBase + 0x206; detectionPort[j].IrqLevel = customDeviceParameter.IrqLevel; j++; } IdePortCloseServiceSubKey ( subServiceKey ); } } #endif // DRIVER_PARAMETER_REGISTRY_SUPPORT } // // populate the list with popular i/o ports // if ( !IsNEC_98 ) { if (configurationInformation->AtDiskPrimaryAddressClaimed == FALSE) { detectionPort[j].CommandRegisterBase = 0x1f0; detectionPort[j].ControlRegisterBase = 0x1f0 + 0x206; detectionPort[j].IrqLevel = 14; j++; } if (configurationInformation->AtDiskSecondaryAddressClaimed == FALSE) { detectionPort[j].CommandRegisterBase = 0x170; detectionPort[j].ControlRegisterBase = 0x170 + 0x206; detectionPort[j].IrqLevel = 15; j++; } detectionPort[j].CommandRegisterBase = 0x1e8; detectionPort[j].ControlRegisterBase = 0x1e8 + 0x206; detectionPort[j].IrqLevel = 11; // DEC Hi-Note hack // detectionPort[j].ControlRegisterBase = 0x1e8 + 0x1f - 0x2; // detectionPort[j].IrqLevel = 7; // DEC Hi-Note hack j++; detectionPort[j].CommandRegisterBase = 0x168; detectionPort[j].ControlRegisterBase = 0x168 + 0x206; detectionPort[j].IrqLevel = 10; j++; } else { // IsNEC_98 if ((configurationInformation->AtDiskPrimaryAddressClaimed == FALSE) && (configurationInformation->AtDiskSecondaryAddressClaimed == FALSE)) { detectionPort[j].CommandRegisterBase = 0x640; detectionPort[j].ControlRegisterBase = 0x640 + 0x10c; //0x74c detectionPort[j].IrqLevel = 9; j++; } } *NumPort = j; *DetectionPort = detectionPort; return STATUS_SUCCESS; } else { *NumPort = 0; *DetectionPort = NULL; return STATUS_INSUFFICIENT_RESOURCES; } } // IdePortCreateDetectionList NTSTATUS IdePortTranslateAddress ( IN INTERFACE_TYPE InterfaceType, IN ULONG BusNumber, IN PHYSICAL_ADDRESS StartAddress, IN LONG Length, IN OUT PULONG AddressSpace, OUT PVOID *TranslatedAddress, OUT PPHYSICAL_ADDRESS TranslatedMemoryAddress ) /*++ Routine Description: translate i/o address Arguments: InterfaceType - bus interface BusNumber - bus number StartAddress - address to translate Length - number of byte to translate AddressSpace - address space for the given address Return Value: AddressSpace - address space for the translated address TranslatedAddress - translated address TranslatedMemoryAddress - tranlated memory address if translated to memory space NT Status --*/ { PHYSICAL_ADDRESS translatedAddress; ASSERT (Length); ASSERT (AddressSpace); ASSERT (TranslatedAddress); *TranslatedAddress = NULL; TranslatedMemoryAddress->QuadPart = (ULONGLONG) NULL; if (HalTranslateBusAddress(InterfaceType, BusNumber, StartAddress, AddressSpace, &translatedAddress)) { if (*AddressSpace == IO_SPACE) { *TranslatedAddress = (PVOID) translatedAddress.u.LowPart; } else if (*AddressSpace == MEMORY_SPACE) { // // translated address is in memory space, // need to map it to I/O space. // *TranslatedMemoryAddress = translatedAddress; *TranslatedAddress = MmMapIoSpace( translatedAddress, Length, FALSE); } } if (*TranslatedAddress) { return STATUS_SUCCESS; } else { return STATUS_INVALID_PARAMETER; } } // IdePortTranslateAddress VOID IdePortFreeTranslatedAddress ( IN PVOID TranslatedAddress, IN LONG Length, IN ULONG AddressSpace ) /*++ Routine Description: free resources created for a translated address Arguments: TranslatedAddress - translated address Length - number of byte to translated AddressSpace - address space for the translated address Return Value: None --*/ { if (TranslatedAddress) { if (AddressSpace == MEMORY_SPACE) { MmUnmapIoSpace ( TranslatedAddress, Length ); } } return; } // IdePortFreeTranslatedAddress BOOLEAN IdePortDetectAlias ( PIDE_REGISTERS_1 CmdRegBase ) { PIDE_REGISTERS_1 cmdRegBaseAlias; PUCHAR cylinderHighAlias; PUCHAR cylinderLowAlias; // // alias port // cylinderHighAlias = (PUCHAR) ((ULONG_PTR) CmdRegBase->CylinderHigh | (1 << 15)); cylinderLowAlias = (PUCHAR) ((ULONG_PTR) CmdRegBase->CylinderLow | (1 << 15)); IdePortOutPortByte (CmdRegBase->CylinderHigh, SAMPLE_CYLINDER_HIGH_VALUE); IdePortOutPortByte (CmdRegBase->CylinderLow, SAMPLE_CYLINDER_LOW_VALUE); // // Check if indentifier can be read back via the alias port // if ((IdePortInPortByte (cylinderHighAlias) != SAMPLE_CYLINDER_HIGH_VALUE) || (IdePortInPortByte (cylinderLowAlias) != SAMPLE_CYLINDER_LOW_VALUE)) { return FALSE; } else { return TRUE; } } #endif // NO_LEGACY_DRIVERS