/*++

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;
}