windows-nt/Source/XPSP1/NT/base/busdrv/pccard/pcmcibus/detect.c

797 lines
23 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
detect.c
Abstract:
This module contains the code that controls the PCMCIA slots.
Authors:
Bob Rinne (BobRi) 3-Nov-1994
Neil Sandlin (neilsa) June 1 1999
Environment:
Kernel mode
Revision History:
Modified for plug'n'play support
Ravisankar Pudipeddi (ravisp) 1 Dec 1996
--*/
#include "pch.h"
NTSTATUS
PcmciaDetectControllers(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN PPCMCIA_DETECT_ROUTINE PcmciaDetectFn
);
NTSTATUS
PcmciaReportDetectedDevice(
IN PFDO_EXTENSION DeviceExtension
);
NTSTATUS
PcmciaAllocateOpenMemoryWindow(
IN PFDO_EXTENSION DeviceExtension,
IN PPHYSICAL_ADDRESS PhysicalAddress,
IN PULONG PhysicalAddressSize
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,PcmciaLegacyDetectionOk)
#pragma alloc_text(INIT,PcmciaDetectPcmciaControllers)
#pragma alloc_text(INIT,PcmciaDetectControllers)
#pragma alloc_text(INIT,PcmciaReportDetectedDevice)
#pragma alloc_text(INIT,PcmciaAllocateOpenMemoryWindow)
#endif
BOOLEAN
PcmciaLegacyDetectionOk(
VOID
)
/*++
Routine Description
Checks if legacy detection needs to be done for pcmcia controllers
Arguments
None
Return Value
TRUE - If legacy detection can be done
FALSE - If legacy detection should NOT be attempted
--*/
{
UNICODE_STRING unicodeKey, unicodeValue;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+
sizeof(ULONG)];
PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
ULONG length;
NTSTATUS status;
PAGED_CODE();
RtlInitUnicodeString(&unicodeKey,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Pnp");
RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&objectAttributes,
&unicodeKey,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
if (!NT_SUCCESS(ZwOpenKey(&handle,
KEY_QUERY_VALUE,
&objectAttributes))) {
//
// Key doesn't exist
//
return TRUE;
}
RtlInitUnicodeString(&unicodeValue, L"DisableFirmwareMapper");
status = ZwQueryValueKey(handle,
&unicodeValue,
KeyValuePartialInformation,
value,
sizeof(buffer),
&length);
ZwClose(handle);
if (!NT_SUCCESS(status)) {
//
// Value doesn't exist
//
return TRUE;
}
if (value->Type == REG_DWORD) {
//
// If value is non-zero don't do legacy detection
// otherwise it's ok
//
return ((ULONG) (*((PULONG)value->Data)) ? FALSE : TRUE);
}
return TRUE;
}
NTSTATUS
PcmciaDetectPcmciaControllers(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Detects appropriate PCMCIA controllers both ISA & PCI based
in the system.
Arguments:
DriverObject Just as passed in to DriverEntry
RegistryPath
Return Value:
STATUS_SUCCESS if any PCMCIA controllers were found
STATUS_NO_SUCH_DEVICE otherwise
--*/
{
NTSTATUS pcicIsaStatus = STATUS_UNSUCCESSFUL, tcicStatus = STATUS_UNSUCCESSFUL;
PAGED_CODE();
//
// We enumerate the PCI devices first to ensure that the ISA detect
// doesn't probe those address ports which are already claimed by
// detected PCI devices
//
pcicIsaStatus = PcmciaDetectControllers(DriverObject, RegistryPath, PcicIsaDetect);
tcicStatus = PcmciaDetectControllers(DriverObject, RegistryPath, TcicDetect);
//
// Indicate success if we found any controllers
//
return ((NT_SUCCESS(pcicIsaStatus) ||
NT_SUCCESS(tcicStatus) ) ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE);
}
NTSTATUS
PcmciaDetectControllers(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN PPCMCIA_DETECT_ROUTINE PcmciaDetectFn
)
/*++
Routine Description:
Detects PCMCIA controllers in the system and reports them. This is called
by PcmciaDetectPcmciaControllers. This reports bus specific controllers.
Arguments:
DriverObject, RegistryPath - See DriverEntry
PcmciaDetectFn - Pointer to the function that actually probes the hardware
to find PCMCIA controllers. So this routine can be called
with an ISA detect function or a PCI detect function for eg.
Return Value:
STATUS_SUCCESS Found one or more PCMCIA controllers
STATUS_NO_SUCH_DEVICE No controllers found.
STATUS_INSUFFICIENT_RESOURCES Pool allocation failures etc.
--*/
{
PFDO_EXTENSION deviceExtension = NULL;
NTSTATUS status = STATUS_SUCCESS;
NTSTATUS detectStatus;
BOOLEAN controllerDetected = FALSE;
PAGED_CODE();
//
// Allocate a dummy device extension which is used by the Pcic & Tcic detect modules
// Have to do this since the original detection code required device extensions
// Too painful to change this structure now.
//
deviceExtension = ExAllocatePool(NonPagedPool, sizeof(FDO_EXTENSION));
if (deviceExtension == NULL) {
DebugPrint((PCMCIA_DEBUG_FAIL, "Cannot allocate pool for FDO extension\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
do {
RtlZeroMemory(deviceExtension, sizeof(FDO_EXTENSION));
deviceExtension->RegistryPath = RegistryPath;
deviceExtension->DriverObject = DriverObject;
detectStatus = (*PcmciaDetectFn)(deviceExtension);
if (detectStatus != STATUS_SUCCESS) {
continue;
}
controllerDetected = TRUE;
status = PcmciaReportDetectedDevice(deviceExtension);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaDetectControllers: PcmciaReportDetectedDevice "
"failed, status %x\n", status));
continue;
}
} while (detectStatus != STATUS_NO_MORE_ENTRIES);
ExFreePool(deviceExtension);
if (controllerDetected) {
return STATUS_SUCCESS;
}
return (STATUS_NO_SUCH_DEVICE);
}
NTSTATUS
PcmciaReportDetectedDevice(
IN PFDO_EXTENSION DeviceExtension
)
/*++
Routine Description:
Reports the PCMCIA controllers detected to the IO subsystem which creates the
madeup devnodes for these DeviceObjects.
Arguments:
DeviceExtension - DeviceExtension for the DeviceObject (FDO) of the PCMCIA controller
being reported
Return Value:
--*/
{
PDEVICE_OBJECT pdo = NULL, fdo, lowerDevice;
PFDO_EXTENSION fdoExtension;
ULONG pcmciaInterruptVector;
KIRQL pcmciaInterruptLevel;
KAFFINITY pcmciaAffinity;
PSOCKET socket;
NTSTATUS status;
ULONG pcmciaIrq;
ULONG count, ioResourceReqSize;
PHYSICAL_ADDRESS halMemoryAddress;
ULONG addressSpace;
PIO_RESOURCE_REQUIREMENTS_LIST ioResourceReq=NULL;
PIO_RESOURCE_LIST ioResourceList;
PIO_RESOURCE_DESCRIPTOR ioResourceDesc;
PCM_RESOURCE_LIST allocatedResources;
PCM_RESOURCE_LIST scratchResources;
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmResourceDesc;
BOOLEAN translated;
UCHAR option;
PAGED_CODE();
//
// Do initial setup in our "fake" device extension
//
PcmciaGetControllerRegistrySettings(DeviceExtension);
DeviceExtension->Configuration.InterruptPin = 0;
DeviceExtension->Configuration.Interrupt.u.Interrupt.Vector = 0;
DeviceExtension->Configuration.Interrupt.u.Interrupt.Level = 0;
count=0;
//
// Get an 'open' memory window
//
status = PcmciaAllocateOpenMemoryWindow(DeviceExtension,
&DeviceExtension->PhysicalBase,
&DeviceExtension->AttributeMemorySize);
count++;
if (DeviceExtension->Configuration.UntranslatedPortAddress) {
count++;
}
ioResourceReqSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) + (count-1)*sizeof(IO_RESOURCE_DESCRIPTOR);
ioResourceReq = ExAllocatePool(PagedPool, ioResourceReqSize);
if (ioResourceReq == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(ioResourceReq, ioResourceReqSize);
ioResourceReq->ListSize = ioResourceReqSize;
ioResourceReq->InterfaceType = Isa; // DeviceExtension->Configuration.InterfaceType;
ioResourceReq->BusNumber = DeviceExtension->Configuration.BusNumber;
ioResourceReq->SlotNumber= DeviceExtension->Configuration.SlotNumber;
ioResourceReq->AlternativeLists=1;
ioResourceList = &(ioResourceReq->List[0]);
ioResourceList->Version = IO_RESOURCE_LIST_VERSION;
ioResourceList->Revision = IO_RESOURCE_LIST_REVISION;
ioResourceList->Count = count;
ioResourceDesc = ioResourceList->Descriptors;
//
//Request IO
//
if (DeviceExtension->Configuration.UntranslatedPortAddress) {
ioResourceDesc->Option = 0;
ioResourceDesc->Type = CmResourceTypePort;
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
ioResourceDesc->Flags = CM_RESOURCE_PORT_IO;
ioResourceDesc->u.Port.MinimumAddress.LowPart = (ULONG)(DeviceExtension->Configuration.UntranslatedPortAddress);
ioResourceDesc->u.Port.MaximumAddress.LowPart = (ULONG)(DeviceExtension->Configuration.UntranslatedPortAddress+
DeviceExtension->Configuration.PortSize - 1);
ioResourceDesc->u.Port.Length = DeviceExtension->Configuration.PortSize;
ioResourceDesc->u.Port.Alignment = 1;
ioResourceDesc++;
}
//
// Request memory
//
ioResourceDesc->Option = 0;
ioResourceDesc->Type = CmResourceTypeMemory;
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
if (DeviceExtension->PhysicalBase.QuadPart) {
ioResourceDesc->u.Memory.MinimumAddress = DeviceExtension->PhysicalBase;
ioResourceDesc->u.Memory.MaximumAddress.QuadPart = DeviceExtension->PhysicalBase.QuadPart+DeviceExtension->AttributeMemorySize-1;
ioResourceDesc->u.Memory.Length = DeviceExtension->AttributeMemorySize;
ioResourceDesc->u.Memory.Alignment = 1;
ioResourceDesc++;
} else {
//
ioResourceDesc->u.Memory.MinimumAddress.LowPart = DeviceExtension->AttributeMemoryLow;
ioResourceDesc->u.Memory.MaximumAddress.LowPart = DeviceExtension->AttributeMemoryHigh;
ioResourceDesc->u.Memory.Length = DeviceExtension->AttributeMemorySize;
switch (DeviceExtension->ControllerType) {
case PcmciaDatabook: {
ioResourceDesc->u.Memory.Alignment = TCIC_WINDOW_ALIGNMENT;
break;
}
default: {
ioResourceDesc->u.Memory.Alignment = PCIC_WINDOW_ALIGNMENT;
break;
}
}
ioResourceDesc++;
}
status = IoAssignResources(DeviceExtension->RegistryPath,
NULL,
DeviceExtension->DriverObject,
NULL,
ioResourceReq,
&allocatedResources
);
if (!NT_SUCCESS(status)) {
//
// Log an event here
//
PcmciaLogError(DeviceExtension, PCMCIA_NO_RESOURCES, 1, 0);
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaReportDetectedDevice: IoAssignResources failed status %x\n",
status));
ExFreePool(ioResourceReq);
return status;
}
//
// Fish out the Memory Base allocated to this controller from the
// nether depths of the CM_RESOURCE_LIST
//
count = allocatedResources->List[0].PartialResourceList.Count;
cmResourceDesc = &(allocatedResources->List[0].PartialResourceList.PartialDescriptors[0]);
while (count--) {
switch (cmResourceDesc->Type) {
case CmResourceTypeMemory: {
DeviceExtension->PhysicalBase = cmResourceDesc->u.Memory.Start;
DeviceExtension->AttributeMemorySize = cmResourceDesc->u.Memory.Length;
addressSpace=0;
translated = HalTranslateBusAddress(Isa,
0,
cmResourceDesc->u.Memory.Start,
&addressSpace,
&halMemoryAddress);
ASSERT(translated);
if (addressSpace) {
DeviceExtension->AttributeMemoryBase = (PUCHAR)(halMemoryAddress.QuadPart);
DeviceExtension->Flags &= ~PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
}
else {
DeviceExtension->AttributeMemoryBase = MmMapIoSpace(halMemoryAddress,
cmResourceDesc->u.Memory.Length,
FALSE);
DeviceExtension->Flags |= PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
}
DebugPrint((PCMCIA_DEBUG_INFO,
"Attribute Memory Physical Base: %x Virtual Addr: %x\n",
DeviceExtension->PhysicalBase,
DeviceExtension->AttributeMemoryBase));
break;
}
// Don't bother to parse IO, it was a fixed resource requirement which we already know about
}
cmResourceDesc++;
}
//
// Free resources so IoReportDetectedDevice can assign them for the PDO
//
IoAssignResources(DeviceExtension->RegistryPath,
NULL,
DeviceExtension->DriverObject,
NULL,
NULL,
&scratchResources
);
pdo = NULL;
status = IoReportDetectedDevice(
DeviceExtension->DriverObject,
InterfaceTypeUndefined,
-1,
-1,
allocatedResources,
ioResourceReq,
FALSE,
&pdo
);
ExFreePool(allocatedResources);
ExFreePool(ioResourceReq);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaReportDetectedDevice: IoReportDetectedDevice failed\n"));
return status;
}
//
// Set up registry params for the madeup pdo so we'll recognize it on the next boot
// when the PNP manager gives us an AddDevice/IRP_MN_START_DEVICE
//
PcmciaSetLegacyDetectedControllerType(pdo, DeviceExtension->ControllerType);
//
// The I/O subsystem has created the true PDO which we will use during this boot. So we
// have to attach to this PDO, and initialize our new FDO extension to values already set
// into our original (fake) FDO extension.
//
status = PcmciaAddDevice(DeviceExtension->DriverObject, pdo);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaReportDetectedDevice: AddDevice failed status %x\n", status));
return status;
}
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Head of list is our fdo
//
fdo = FdoList;
fdoExtension = fdo->DeviceExtension;
//
// Copy in the rest of the config. from the DeviceExtension
//
fdoExtension->SocketList = DeviceExtension->SocketList;
fdoExtension->Configuration = DeviceExtension->Configuration;
fdoExtension->PhysicalBase = DeviceExtension->PhysicalBase;
fdoExtension->AttributeMemoryBase = DeviceExtension->AttributeMemoryBase;
fdoExtension->AttributeMemorySize = DeviceExtension->AttributeMemorySize;
fdoExtension->Flags = DeviceExtension->Flags;
// Reinitialize the socket's device extensions
//
for (socket = fdoExtension->SocketList; socket!=NULL; socket=socket->NextSocket) {
socket->DeviceExtension = fdoExtension;
}
fdoExtension->Flags |= PCMCIA_DEVICE_STARTED;
//
// This is legacy detected..
//
fdoExtension->Flags |= PCMCIA_DEVICE_LEGACY_DETECTED;
status=PcmciaStartPcmciaController(fdo);
if (!NT_SUCCESS(status)) {
fdoExtension->Flags &= ~PCMCIA_DEVICE_STARTED;
}
return status;
}
NTSTATUS
PcmciaAllocateOpenMemoryWindow(
IN PFDO_EXTENSION DeviceExtension,
IN PPHYSICAL_ADDRESS PhysicalAddress,
IN PULONG PhysicalAddressSize
)
/*++
Routine Description:
Search the 640K to 1MB region for an open area to be used
for mapping PCCARD attribute memory.
Arguments:
Return Value:
A physical address for the window to the card or zero meaning
there is no opening.
--*/
{
#define NUMBER_OF_TEST_BYTES 25
PHYSICAL_ADDRESS physicalMemoryAddress;
PHYSICAL_ADDRESS halMemoryAddress;
BOOLEAN translated;
ULONG untranslatedAddress;
PUCHAR memoryAddress;
PUCHAR bogus;
ULONG addressSpace;
ULONG index;
UCHAR memory[NUMBER_OF_TEST_BYTES];
PCM_RESOURCE_LIST cmResourceList = NULL;
PCM_PARTIAL_RESOURCE_LIST cmPartialResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR cmResourceDesc;
BOOLEAN conflict = TRUE;
NTSTATUS status;
ULONG windowSize, windowAlignment;
PAGED_CODE();
cmResourceList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST));
if (!cmResourceList) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(cmResourceList, sizeof(CM_RESOURCE_LIST));
cmResourceList->Count = 1;
cmResourceList->List[0].InterfaceType = Isa;
cmPartialResourceList = &(cmResourceList->List[0].PartialResourceList);
cmPartialResourceList->Version = 1;
cmPartialResourceList->Revision = 1;
cmPartialResourceList->Count = 1;
cmResourceDesc = cmPartialResourceList->PartialDescriptors;
cmResourceDesc->Type = CmResourceTypeMemory;
cmResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
cmResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
//
// Size of the attr. memory window
//
switch (DeviceExtension->ControllerType) {
case PcmciaDatabook: {
windowSize = TCIC_WINDOW_SIZE;
windowAlignment = TCIC_WINDOW_ALIGNMENT;
break;
}
default: {
windowSize = PCIC_WINDOW_SIZE;
windowAlignment = PCIC_WINDOW_ALIGNMENT;
break;
}
}
for (untranslatedAddress = DeviceExtension->AttributeMemoryLow;
untranslatedAddress < DeviceExtension->AttributeMemoryHigh;
untranslatedAddress += windowAlignment) {
if (untranslatedAddress == 0xc0000) {
//
// This is VGA. Keep this test if the for loop should
// ever change.
//
continue;
}
//
// Check if it's available
//
cmResourceDesc->u.Memory.Start.LowPart = untranslatedAddress;
cmResourceDesc->u.Memory.Length = windowSize;
status=IoReportResourceForDetection(
DeviceExtension->DriverObject,
cmResourceList,
sizeof(CM_RESOURCE_LIST),
NULL,
NULL,
0,
&conflict);
if (!NT_SUCCESS(status) || conflict) {
//
// This range's already taken. Move on to the next
//
continue;
}
addressSpace = 0;
physicalMemoryAddress.LowPart = untranslatedAddress;
physicalMemoryAddress.HighPart = 0;
translated = HalTranslateBusAddress(Isa,
0,
physicalMemoryAddress,
&addressSpace,
&halMemoryAddress);
if (!translated) {
//
// HAL doesn't like this translation
//
continue;
}
if (addressSpace) {
memoryAddress = (PUCHAR)(halMemoryAddress.QuadPart);
} else {
memoryAddress = MmMapIoSpace(halMemoryAddress, windowSize, FALSE);
}
//
// Test the memory window to determine if it is a BIOS, video
// memory, or open memory. Only want to keep the window if it
// is not being used by something else.
//
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
memory[index] = READ_REGISTER_UCHAR(memoryAddress + index);
if (index) {
if (memory[index] != memory[index - 1]) {
break;
}
}
}
if (index == NUMBER_OF_TEST_BYTES) {
//
// There isn't a BIOS here
//
UCHAR memoryPattern[NUMBER_OF_TEST_BYTES];
BOOLEAN changed = FALSE;
//
// Check for video memory - open memory should always remain
// the same regardless what the changes are. Change the
// pattern previously found.
//
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
memoryPattern[index] = ~memory[index];
WRITE_REGISTER_UCHAR(memoryAddress + index,
memoryPattern[index]);
}
//
// See if the pattern in memory changed.
// Some system exhibit a problem where the memory pattern
// seems to be cached. If this code is debugged it will
// work as expected, but if it is run normally it will
// always return that the memory changed. This random
// wandering seems to remove this problem.
//
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
memoryPattern[index] = 0;
}
bogus = ExAllocatePool(PagedPool, 64 * 1024);
if (bogus) {
for (index = 0; index < 64 * 1024; index++) {
bogus[index] = 0;
}
ExFreePool(bogus);
}
//
// Now go off and do the actual check to see if the memory
// changed.
//
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
if ((memoryPattern[index] = READ_REGISTER_UCHAR(memoryAddress + index)) != memory[index]) {
//
// It changed - this is not an area of open memory
//
changed = TRUE;
}
WRITE_REGISTER_UCHAR(memoryAddress + index,
memory[index]);
}
if (!changed) {
//
// Area isn't a BIOS and didn't change when written.
// Use this region for the memory window to PCMCIA
// attribute memory.
//
PhysicalAddress->LowPart = untranslatedAddress;
PhysicalAddress->HighPart = 0;
*PhysicalAddressSize = windowSize;
if (!addressSpace) {
MmUnmapIoSpace(memoryAddress, windowSize);
}
ExFreePool(cmResourceList);
return STATUS_SUCCESS;
}
}
if (!addressSpace) {
MmUnmapIoSpace(memoryAddress, windowSize);
}
}
ExFreePool(cmResourceList);
return STATUS_UNSUCCESSFUL;
}