|
|
/*++
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
#if 0
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; } #endif
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; }
|