657 lines
18 KiB
C
657 lines
18 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
detect.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code that controls the PCMCIA slots.
|
||
|
||
Author:
|
||
|
||
Bob Rinne (BobRi) 3-Nov-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
--*/
|
||
|
||
// #include <stddef.h>
|
||
#include "ntddk.h"
|
||
#include "string.h"
|
||
#include "pcmcia.h"
|
||
#include "card.h"
|
||
#include "extern.h"
|
||
#include <stdarg.h>
|
||
#include "stdio.h"
|
||
#include "tuple.h"
|
||
|
||
#ifdef POOL_TAGGING
|
||
#undef ExAllocatePool
|
||
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'cmcP')
|
||
#endif
|
||
|
||
#define WINDOW_SIZE (132 * 1024)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,PcmciaAllocateOpenMemoryWindow)
|
||
#pragma alloc_text(INIT,PcmciaDetectMca)
|
||
#pragma alloc_text(INIT,PcmciaDetectSpecialHardware)
|
||
#endif
|
||
|
||
PUCHAR
|
||
PcmciaAllocateOpenMemoryWindow(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG Start,
|
||
IN PULONG Mapped,
|
||
IN PULONG Physical
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Search the 640K to 1MB region for an open area to be used
|
||
for mapping PCCARD attribute memory.
|
||
|
||
Arguments:
|
||
|
||
Start - not used.
|
||
|
||
Return Value:
|
||
|
||
A physical address for the window to the card or zero meaning
|
||
there is no opening.
|
||
|
||
--*/
|
||
|
||
{
|
||
#define NUMBER_OF_TEST_BYTES 5
|
||
PHYSICAL_ADDRESS physicalMemoryAddress;
|
||
PHYSICAL_ADDRESS halMemoryAddress;
|
||
BOOLEAN translated;
|
||
ULONG untranslatedAddress;
|
||
PUCHAR memoryAddress;
|
||
PUCHAR bogus;
|
||
ULONG addressSpace;
|
||
ULONG index;
|
||
UCHAR memory[NUMBER_OF_TEST_BYTES];
|
||
|
||
*Mapped = FALSE;
|
||
|
||
if (DeviceExtension->PhysicalBase) {
|
||
untranslatedAddress = DeviceExtension->PhysicalBase;
|
||
} else {
|
||
untranslatedAddress = 0xd0000;
|
||
}
|
||
|
||
for (/* nothing */; untranslatedAddress < 0xFF000; untranslatedAddress += 0x4000) {
|
||
|
||
if (untranslatedAddress == 0xc0000) {
|
||
|
||
//
|
||
// This is VGA. Keep this test if the for loop should
|
||
// ever change.
|
||
//
|
||
|
||
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.LowPart;
|
||
} else {
|
||
memoryAddress = MmMapIoSpace(halMemoryAddress, WINDOW_SIZE, 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(NonPagedPool, 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.
|
||
//
|
||
|
||
*Mapped = addressSpace ? FALSE : TRUE;
|
||
*Physical = untranslatedAddress;
|
||
return memoryAddress;
|
||
}
|
||
}
|
||
|
||
if (!addressSpace) {
|
||
MmUnmapIoSpace(memoryAddress, WINDOW_SIZE);
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PcmciaDetectMca(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if this system is a micro channel system. If it is this
|
||
driver will not load. More importantly this driver will not go through
|
||
its detect sequence which has a tendency to disable processors on NCR
|
||
MP platforms.
|
||
|
||
The method of determining that this is a MicroChannel system is to
|
||
look into the firmware portion of the registry to see if the first
|
||
bus defined on the system is "MCA".
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE if the platform is MCA
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG resultLength;
|
||
HANDLE handle;
|
||
NTSTATUS status;
|
||
OBJECT_ATTRIBUTES attributes;
|
||
UNICODE_STRING nameString;
|
||
PWCHAR wideChar;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
RtlInitUnicodeString(&nameString, L"\\Registry\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\MultiFunctionAdapter\\0");
|
||
InitializeObjectAttributes(&attributes,
|
||
&nameString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
status = ZwOpenKey(&handle, MAXIMUM_ALLOWED, &attributes);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
keyValueInformation = ExAllocatePool(NonPagedPool, 1024);
|
||
if (keyValueInformation) {
|
||
|
||
RtlInitUnicodeString(&nameString, L"Identifier");
|
||
status = ZwQueryValueKey(handle,
|
||
&nameString,
|
||
KeyValueFullInformation,
|
||
keyValueInformation,
|
||
1024,
|
||
&resultLength);
|
||
ZwClose(handle);
|
||
if (NT_SUCCESS(status)) {
|
||
if (keyValueInformation->DataLength != 0) {
|
||
|
||
//
|
||
// Have something to check.
|
||
//
|
||
|
||
wideChar = (PWCHAR) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset);
|
||
|
||
if ((*wideChar == (WCHAR)'M') &&
|
||
(*(wideChar + 1) == (WCHAR)'C') &&
|
||
(*(wideChar + 2) == (WCHAR)'A')) {
|
||
ExFreePool(keyValueInformation);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PcmciaDetectDevicePresence(
|
||
IN ULONG IoPortBase,
|
||
IN ULONG Length,
|
||
IN UCHAR DeviceType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads the registers given to see if there is a
|
||
possibility of a device being located at the I/O port address.
|
||
A device's presence is viewed as possible if any of the I/O ports
|
||
return a value other than 0xff.
|
||
|
||
Arguments:
|
||
|
||
IoPortBase - where to start
|
||
Length - how long to read
|
||
DeviceType - type of device that is there. This is used to
|
||
perform special action when looking at I/O ports
|
||
that could be ATA registers.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If reading the IoPortBase for Length shows that there
|
||
is a possibility that a device exists at this address.
|
||
FALSE If all registers return FF.
|
||
|
||
--*/
|
||
|
||
{
|
||
PHYSICAL_ADDRESS address;
|
||
PHYSICAL_ADDRESS cardAddress;
|
||
BOOLEAN somethingThere;
|
||
PUCHAR port;
|
||
UCHAR value;
|
||
ULONG index;
|
||
ULONG addressSpace = 1;
|
||
|
||
address.LowPart = IoPortBase;
|
||
address.HighPart = 0;
|
||
|
||
somethingThere = HalTranslateBusAddress(Isa,
|
||
0,
|
||
address,
|
||
&addressSpace,
|
||
&cardAddress);
|
||
|
||
if (!somethingThere) {
|
||
|
||
//
|
||
// HAL won't translate the address so don't try to use it.
|
||
// Return to the caller that something is there to keep from
|
||
// using this address.
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
somethingThere = FALSE;
|
||
if (addressSpace) {
|
||
port = (PUCHAR) cardAddress.LowPart;
|
||
} else {
|
||
port = MmMapIoSpace(cardAddress,
|
||
Length,
|
||
FALSE);
|
||
}
|
||
|
||
if (DeviceType == PCCARD_TYPE_ATA) {
|
||
|
||
//
|
||
// Some ATA devices get into an inconsistent state if all
|
||
// of the registers are touched here so this code performs
|
||
// an ATA detection sequence instead of reading ports.
|
||
//
|
||
|
||
value = READ_PORT_UCHAR(port + 2);
|
||
if ((value == 0xFF) || (value == 0xB0)) {
|
||
|
||
WRITE_PORT_UCHAR(port + 2, 0xAA);
|
||
|
||
//
|
||
// Check if indentifier can be read back.
|
||
//
|
||
|
||
if (READ_PORT_UCHAR(port + 2) == 0xAA) {
|
||
somethingThere = TRUE;
|
||
}
|
||
} else {
|
||
somethingThere = TRUE;
|
||
}
|
||
|
||
} else {
|
||
for (index = 0; index < Length; index++) {
|
||
value = READ_PORT_UCHAR(port + index);
|
||
if (value != 0xFF) {
|
||
|
||
//
|
||
// PowerPC based systems return B0 for bytes that are
|
||
// not mapped to something.
|
||
//
|
||
|
||
if (value != 0xb0) {
|
||
somethingThere = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (somethingThere) {
|
||
if (Length > 3) {
|
||
|
||
//
|
||
// If the requested length of the port range is greater
|
||
// than three. Check to see if all ports in the range
|
||
// are the same. If they are, then assume that nothing
|
||
// is in the range.
|
||
//
|
||
|
||
somethingThere = FALSE;
|
||
value = READ_PORT_UCHAR(port);
|
||
for (index = 0; index < Length; index++) {
|
||
if (value != READ_PORT_UCHAR(port + index)) {
|
||
somethingThere = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!addressSpace) {
|
||
MmUnmapIoSpace(port, Length);
|
||
}
|
||
|
||
return somethingThere;
|
||
}
|
||
|
||
|
||
struct _BIOS_SearchTable {
|
||
ULONG BIOSLocation;
|
||
PUCHAR BIOSString;
|
||
ULONG AddedIrqMask;
|
||
ULONG MemoryBase;
|
||
};
|
||
|
||
struct _BIOS_SearchTable BIOS_SearchTable[] = {
|
||
{0xe0000, "OPYRIGHT IBM", (1 << 10), 0},
|
||
{0xf0eb0, "opyright 1993 Toshiba", 0, 0xd8000},
|
||
{0, NULL, 0, 0}
|
||
};
|
||
|
||
VOID
|
||
PcmciaDetectSpecialHardware(
|
||
IN PDEVICE_EXTENSION DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks for certain platform specific issues related to interrupts.
|
||
Currently this is done by a scan for BIOS strings to locate IBM 755 laptops and
|
||
avoid use of IRQ 10.
|
||
|
||
Future work will be to add the Windows 95 interrupt detection code here - or
|
||
share this responsibility with ntdetect.com.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - locates the interrupt mask and the base of all configuration
|
||
information.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
#define MEMORY_COMPARE_SIZE 100
|
||
#define MEMORY_FUDGE 8
|
||
struct _BIOS_SearchTable *entry;
|
||
PUCHAR memoryAddress;
|
||
PUCHAR mp;
|
||
PUCHAR cp;
|
||
PUCHAR allocBase;
|
||
ULONG addressSpace;
|
||
ULONG index;
|
||
ULONG resultLength;
|
||
BOOLEAN somethingThere;
|
||
BOOLEAN found;
|
||
HANDLE handle;
|
||
NTSTATUS status;
|
||
PHYSICAL_ADDRESS address;
|
||
PHYSICAL_ADDRESS halMemoryAddress;
|
||
OBJECT_ATTRIBUTES attributes;
|
||
UNICODE_STRING nameString;
|
||
PWCHAR wideChar;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
|
||
allocBase = ExAllocatePool(NonPagedPool, MEMORY_COMPARE_SIZE + MEMORY_FUDGE);
|
||
if (!allocBase) {
|
||
return;
|
||
}
|
||
|
||
found = FALSE;
|
||
for (entry = BIOS_SearchTable; entry->BIOSLocation; entry++) {
|
||
|
||
addressSpace = 0;
|
||
address.LowPart = entry->BIOSLocation;
|
||
address.HighPart = 0;
|
||
somethingThere = HalTranslateBusAddress(Isa,
|
||
0,
|
||
address,
|
||
&addressSpace,
|
||
&halMemoryAddress);
|
||
|
||
if (!somethingThere) {
|
||
|
||
//
|
||
// HAL won't translate the address so don't try to use it.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
if (addressSpace) {
|
||
memoryAddress = (PUCHAR) halMemoryAddress.LowPart;
|
||
} else {
|
||
memoryAddress = MmMapIoSpace(halMemoryAddress, MEMORY_COMPARE_SIZE + MEMORY_FUDGE, FALSE);
|
||
}
|
||
|
||
//
|
||
// Copy the BIOS string to a local buffer and free BIOS map.
|
||
//
|
||
|
||
for (cp = allocBase, mp = memoryAddress, index = 0;
|
||
index < MEMORY_COMPARE_SIZE;
|
||
cp++, mp++, index++) {
|
||
|
||
*cp = READ_REGISTER_UCHAR(mp);
|
||
}
|
||
|
||
//
|
||
// Insure e-o-s termination.
|
||
//
|
||
|
||
for (index = 0; index < MEMORY_FUDGE; index++, cp++) {
|
||
*cp = '\0';
|
||
}
|
||
|
||
//
|
||
// Done with mapped memory
|
||
//
|
||
|
||
if (!addressSpace) {
|
||
MmUnmapIoSpace(memoryAddress, MEMORY_COMPARE_SIZE + MEMORY_FUDGE);
|
||
}
|
||
|
||
//
|
||
// Search for string.
|
||
//
|
||
|
||
if (strstr(allocBase, entry->BIOSString)) {
|
||
|
||
//
|
||
// string is there.
|
||
//
|
||
|
||
if (entry->AddedIrqMask) {
|
||
DeviceExtension->AllocatedIrqlMask |= entry->AddedIrqMask;
|
||
}
|
||
|
||
if (entry->MemoryBase) {
|
||
DeviceExtension->PhysicalBase = entry->MemoryBase;
|
||
}
|
||
found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
ExFreePool(allocBase);
|
||
|
||
if (!found) {
|
||
|
||
//
|
||
// Check if the hardware is PPC...
|
||
//
|
||
|
||
RtlInitUnicodeString(&nameString,
|
||
L"\\Registry\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
|
||
InitializeObjectAttributes(&attributes,
|
||
&nameString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
|
||
status = ZwOpenKey(&handle, MAXIMUM_ALLOWED, &attributes);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
keyValueInformation = ExAllocatePool(NonPagedPool, 1024);
|
||
if (keyValueInformation) {
|
||
|
||
RtlInitUnicodeString(&nameString, L"Identifier");
|
||
status = ZwQueryValueKey(handle,
|
||
&nameString,
|
||
KeyValueFullInformation,
|
||
keyValueInformation,
|
||
1024,
|
||
&resultLength);
|
||
ZwClose(handle);
|
||
if (NT_SUCCESS(status)) {
|
||
if (keyValueInformation->DataLength != 0) {
|
||
|
||
//
|
||
// Have something to check.
|
||
//
|
||
|
||
wideChar = (PWCHAR) ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset);
|
||
|
||
if ((*wideChar == (WCHAR)'P') &&
|
||
(*(wideChar + 1) == (WCHAR)'o') &&
|
||
(*(wideChar + 2) == (WCHAR)'w') &&
|
||
(*(wideChar + 3) == (WCHAR)'e') &&
|
||
(*(wideChar + 4) == (WCHAR)'r') &&
|
||
(*(wideChar + 5) == (WCHAR)'P') &&
|
||
(*(wideChar + 6) == (WCHAR)'C')) {
|
||
|
||
//
|
||
// Disable Interrupt 10 from the mask
|
||
//
|
||
|
||
DeviceExtension->AllocatedIrqlMask |= (1 << 10);
|
||
}
|
||
}
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Search for registry override of memory window physical address
|
||
//
|
||
|
||
PcmciaRegistryMemoryWindow(DeviceExtension);
|
||
|
||
}
|