/*++

Copyright (c) 1997-2000 Microsoft Corporation

Module Name:

    registry.c

Abstract:

    This module contains the code that manipulates the ARC firmware
    tree and other elements in the registry.

Author:

    Bob Rinne
    Ravisankar Pudipeddi (ravisp) 1 Dec 1996
    Neil Sandlin (neilsa) June 1 1999    

Environment:

    Kernel mode

Revision History :


--*/

#include "pch.h"

//
// Internal References
//

VOID
PcmciaGetRegistryContextRange(
   IN HANDLE instanceHandle,
   IN PCWSTR Name,
   IN OPTIONAL const PCMCIA_CONTEXT_RANGE IncludeRange[], 
   IN OPTIONAL const PCMCIA_CONTEXT_RANGE ExcludeRange[], 
   OUT PPCMCIA_CONTEXT pContext
   );

ULONG
PcmciaGetDetectedFdoIrqMask(
   IN PFDO_EXTENSION FdoExtension
   );
   
NTSTATUS
PcmciaScanHardwareDescription(
   VOID
   );
                              
NTSTATUS
PcmciaGetHardwareDetectedIrqMask(
   IN HANDLE handlePcCard
   );
                         
//
//
// Registry related definitions
//
#define PCMCIA_REGISTRY_PARAMETERS_KEY              L"Pcmcia\\Parameters"
#define PCMCIA_REGISTRY_DETECTED_DEVICE_KEY         L"ControllerProperties"


//
// Per controller values (in control\class)
//

#define PCMCIA_REGISTRY_PCI_CONTEXT_VALUE  L"CBSSCSContextRanges"
#define PCMCIA_REGISTRY_CB_CONTEXT_VALUE   L"CBSSCBContextRanges"
#define PCMCIA_REGISTRY_EXCA_CONTEXT_VALUE L"CBSSEXCAContextRanges"
#define PCMCIA_REGISTRY_CACHED_IRQMASK     L"CachedIrqMask"
#define PCMCIA_REGISTRY_COMPATIBLE_TYPE    L"CompatibleControllerType"
#define PCMCIA_REGISTRY_VOLTAGE_PREFERENCE L"VoltagePreference"

//
// Irq detection values (in hardware\description)
//

#define PCMCIA_REGISTRY_CONTROLLER_TYPE    L"OtherController"


#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,PcmciaLoadGlobalRegistryValues)
#pragma alloc_text(INIT,PcmciaScanHardwareDescription)
#pragma alloc_text(INIT,PcmciaGetHardwareDetectedIrqMask)
#pragma alloc_text(PAGE,PcmciaGetControllerRegistrySettings)
#pragma alloc_text(PAGE,PcmciaGetRegistryFdoIrqMask)
#pragma alloc_text(PAGE,PcmciaGetDetectedFdoIrqMask)
#pragma alloc_text(PAGE,PcmciaGetLegacyDetectedControllerType)
#pragma alloc_text(PAGE,PcmciaSetLegacyDetectedControllerType)
#pragma alloc_text(PAGE,PcmciaGetRegistryContextRange)
#endif


NTSTATUS
PcmciaGetHardwareDetectedIrqMask(
   HANDLE handlePcCard
   )
/*++

Routine Description:

   This routine looks through the OtherController key for pccard entries
   created by NTDETECT. For each entry, the IRQ scan data is read in and
   saved for later.
   

Arguments:

   handlePcCard - open handle to "OtherController" key in registry at
                  HARDWARE\Description\System\MultifunctionAdapter\<ISA>

Return value:

   status

--*/
{
#define VALUE2_BUFFER_SIZE sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(CM_PCCARD_DEVICE_DATA) + sizeof(CM_FULL_RESOURCE_DESCRIPTOR)
   UCHAR valueBuffer[VALUE2_BUFFER_SIZE];
   PKEY_VALUE_PARTIAL_INFORMATION valueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) valueBuffer;

   NTSTATUS          status;
   KEY_FULL_INFORMATION KeyFullInfo;
   PKEY_BASIC_INFORMATION subKeyInfo = NULL;
   OBJECT_ATTRIBUTES attributes;
   UNICODE_STRING    strSubKey = {0};
   UNICODE_STRING    strIdentifier;
   UNICODE_STRING    strConfigData;
   HANDLE            handleSubKey = NULL;
   ULONG             subKeyInfoSize;
   ULONG             index;
   ULONG             resultLength;
   
   RtlInitUnicodeString(&strIdentifier, L"Identifier");
   RtlInitUnicodeString(&strConfigData, L"Configuration Data");
   
   status = ZwQueryKey(handlePcCard,
                       KeyFullInformation,
                       &KeyFullInfo,
                       sizeof(KeyFullInfo),
                       &resultLength);
   
   if ((!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW))) {
      goto cleanup;
   }   

   strSubKey.MaximumLength = (USHORT) KeyFullInfo.MaxNameLen;
   subKeyInfoSize = sizeof(KEY_BASIC_INFORMATION) + KeyFullInfo.MaxNameLen;
   subKeyInfo = ExAllocatePool(PagedPool, subKeyInfoSize);
   
   if (!subKeyInfo) {
      goto cleanup;
   }
   
   for (index=0;;index++) {
   
      //
      // Loop through the children of the PcCardController key
      //
   
      status = ZwEnumerateKey(handlePcCard,
                              index,
                              KeyBasicInformation,
                              subKeyInfo,
                              subKeyInfoSize,
                              &resultLength);

      if (!NT_SUCCESS(status)) {
         goto cleanup;
      }

      //
      // Init the name
      //

      if (subKeyInfo->NameLength > strSubKey.MaximumLength) {
         continue;
      }      
      strSubKey.Length = (USHORT) subKeyInfo->NameLength;
      strSubKey.Buffer = subKeyInfo->Name;
   
      //
      // Get a handle to a child of PcCardController
      //
   
      
      InitializeObjectAttributes(&attributes,
                                 &strSubKey,
                                 0,   //Attributes
                                 handlePcCard,
                                 NULL //SecurityDescriptor
                                 );
   
      if (handleSubKey) {
         // close handle from previous iteration
         ZwClose(handleSubKey);
         handleSubKey = NULL;
      }
      
      status = ZwOpenKey(&handleSubKey, MAXIMUM_ALLOWED, &attributes);
   
      if (!NT_SUCCESS(status)) {
         goto cleanup;
      }

      //
      // Get the value of "Identifier"
      //
 
      status = ZwQueryValueKey(handleSubKey,
                               &strIdentifier,
                               KeyValuePartialInformation,
                               valueInfo,
                               VALUE2_BUFFER_SIZE,
                               &resultLength);
      
      
      if (NT_SUCCESS(status) || (status == STATUS_BUFFER_OVERFLOW)) {
         PWCHAR pData = (PWCHAR)valueInfo->Data;
      
         if ((valueInfo->DataLength == 17*sizeof(WCHAR)) &&
            (pData[0] == (WCHAR)'P') &&
            (pData[1] == (WCHAR)'c') &&
            (pData[2] == (WCHAR)'C') &&
            (pData[3] == (WCHAR)'a') &&
            (pData[4] == (WCHAR)'r') &&
            (pData[5] == (WCHAR)'d')) {
            
            //
            // Get the IRQ detection data
            //
            status = ZwQueryValueKey(handleSubKey,
                                     &strConfigData,
                                     KeyValuePartialInformation,
                                     valueInfo,
                                     VALUE2_BUFFER_SIZE,
                                     &resultLength);
           
            if (NT_SUCCESS(status)) {   
               PCM_FULL_RESOURCE_DESCRIPTOR pFullDesc = (PCM_FULL_RESOURCE_DESCRIPTOR) valueInfo->Data;
               PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) pFullDesc->PartialResourceList.PartialDescriptors;

               if ((pPartialDesc->Type == CmResourceTypeDeviceSpecific) &&
                   (pPartialDesc->u.DeviceSpecificData.DataSize == sizeof(CM_PCCARD_DEVICE_DATA))) {
                   
                  PCM_PCCARD_DEVICE_DATA pData = (PCM_PCCARD_DEVICE_DATA) ((ULONG_PTR)&pPartialDesc->u.DeviceSpecificData + 3*sizeof(ULONG));
                  PPCMCIA_NTDETECT_DATA pNewData;
                  
                  pNewData = ExAllocatePool(PagedPool, sizeof(PCMCIA_NTDETECT_DATA));
                  
                  if (pNewData == NULL) {
                     goto cleanup;
                  }

                  pNewData->PcCardData = *pData;
                  pNewData->Next = pNtDetectDataList;
                  pNtDetectDataList = pNewData;

               }
            }
         }
      }
 
   }
cleanup:
   if (handleSubKey) {
      ZwClose(handleSubKey);
   }
   
   if (subKeyInfo) {
      ExFreePool(subKeyInfo);
   }
   return STATUS_SUCCESS;      
}



ULONG
PcmciaGetDetectedFdoIrqMask(
   IN PFDO_EXTENSION FdoExtension
   )
/*++

Routine Description:

   This routine looks through the cached PCMCIA_NTDETECT_DATA entries
   to see if there was an entry for this controller. It then returns the
   detected irq mask for that controller.

Arguments:

   FdoExtension - The fdo extension corresponding to the PCMCIA controller

Return value:

   status

--*/
{

   PPCMCIA_NTDETECT_DATA pData;
   PCM_PCCARD_DEVICE_DATA pPcCardData;
   ULONG detectedIrqMask = 0;
   
   if (FdoExtension->SocketList == NULL) {
      return 0;
   }

   for (pData = pNtDetectDataList; pData != NULL; pData = pData->Next) {

      pPcCardData = &pData->PcCardData;      

      
      if (CardBusExtension(FdoExtension)) {

         if (!(pPcCardData->Flags & PCCARD_DEVICE_PCI) || ((pPcCardData->BusData) == 0) ||
             ((pPcCardData->BusData & 0xff) != FdoExtension->PciBusNumber) ||
             (((pPcCardData->BusData >> 8) & 0xff) != FdoExtension->PciDeviceNumber)) {
            continue;
         }                  
         
         SetFdoFlag(FdoExtension, PCMCIA_FDO_IRQ_DETECT_DEVICE_FOUND);
         
         if (!(pPcCardData->Flags & PCCARD_MAP_ERROR)) {
            //
            // we found the device, and the map looks good
            //
            break;
         }
         
      } else {
      
         if ((pPcCardData->Flags & PCCARD_DEVICE_PCI) ||
            (pPcCardData->LegacyBaseAddress != (ULONG_PTR)FdoExtension->SocketList->AddressPort)) {
            continue;
         }
         
         SetFdoFlag(FdoExtension, PCMCIA_FDO_IRQ_DETECT_DEVICE_FOUND);
         
         if (!(pPcCardData->Flags & PCCARD_MAP_ERROR)) {
            //
            // we found the device, and the map looks good
            //
            break;
         }
         
      }
   }      
   
   if (pData) {
      ULONG i;
      //
      // Found the entry
      //
      // Since we don't currently handle "rewired" irqs, we can compact
      // it down to a bit mask, throwing away irqs that are wired, say
      // IRQ12 on the controller to IRQ15 on the isa bus.
      //

      for (i = 1; i < 16; i++) {
         if (pPcCardData->IRQMap[i] == i) {
            detectedIrqMask |= (1<<i);
         }
      }               
      SetFdoFlag(FdoExtension, PCMCIA_FDO_IRQ_DETECT_COMPLETED);
   }
   return detectedIrqMask;
}



NTSTATUS
PcmciaScanHardwareDescription(
   VOID
   )
/*++

Routine Description:

   This routine finds the "OtherController" entry in
   HARDWARE\Description\System\MultifunctionAdapter\<ISA>. This is
   where NTDETECT stores irq scan results.
   
   It also looks for machines that aren't supported, for example MCA
   bus.

Arguments:

Return value:

   status

--*/
{
#define VALUE_BUFFER_SIZE sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 3*sizeof(WCHAR)

   UCHAR valueBuffer[VALUE_BUFFER_SIZE];
   PKEY_VALUE_PARTIAL_INFORMATION valueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) valueBuffer;
   PKEY_BASIC_INFORMATION subKeyInfo = NULL;
   KEY_FULL_INFORMATION KeyFullInfo;
   
   HANDLE            handleRoot = NULL;
   HANDLE            handleSubKey = NULL;
   HANDLE            handlePcCard = NULL;
   UNICODE_STRING    strRoot, strIdentifier;
   UNICODE_STRING    strSubKey = {0};
   UNICODE_STRING    strPcCard = {0};
   NTSTATUS          status;
   OBJECT_ATTRIBUTES attributes;
   ULONG             subKeyInfoSize;
   ULONG             resultLength;
   ULONG             index;
   
   PAGED_CODE();

   //
   // Get a handle to the MultifunctionAdapter key
   //

   RtlInitUnicodeString(&strRoot, L"\\Registry\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\MultiFunctionAdapter");
   RtlInitUnicodeString(&strIdentifier, L"Identifier");
   RtlInitUnicodeString(&strPcCard, PCMCIA_REGISTRY_CONTROLLER_TYPE);
   
   InitializeObjectAttributes(&attributes,
                              &strRoot,
                              OBJ_CASE_INSENSITIVE,
                              NULL,
                              NULL);

   status = ZwOpenKey(&handleRoot, MAXIMUM_ALLOWED, &attributes);

   if (!NT_SUCCESS(status)) {
      goto cleanup;
   }   

   status = ZwQueryKey(handleRoot,
                       KeyFullInformation,
                       &KeyFullInfo,
                       sizeof(KeyFullInfo),
                       &resultLength);
   
   if ((!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW))) {
      goto cleanup;
   }   

   strSubKey.MaximumLength = (USHORT) KeyFullInfo.MaxNameLen;
   subKeyInfoSize = sizeof(KEY_BASIC_INFORMATION) + KeyFullInfo.MaxNameLen;
   subKeyInfo = ExAllocatePool(PagedPool, subKeyInfoSize);
   
   if (!subKeyInfo) {
      goto cleanup;
   }
   
   for (index=0;;index++) {
   
      //
      // Loop through the children of "MultifunctionAdapter"
      //
   
      status = ZwEnumerateKey(handleRoot,
                              index,
                              KeyBasicInformation,
                              subKeyInfo,
                              subKeyInfoSize,
                              &resultLength);

      if (!NT_SUCCESS(status)) {
         goto cleanup;
      }

      //
      // Init the name
      //

      if (subKeyInfo->NameLength > strSubKey.MaximumLength) {
         continue;
      }      
      strSubKey.Length = (USHORT) subKeyInfo->NameLength;
      strSubKey.Buffer = subKeyInfo->Name;
   
      //
      // Get a handle to a child of MultifunctionAdapter
      //
   
      
      InitializeObjectAttributes(&attributes,
                                 &strSubKey,
                                 0,   //Attributes
                                 handleRoot,
                                 NULL //SecurityDescriptor
                                 );
   
      if (handleSubKey) {
         // close handle from previous iteration
         ZwClose(handleSubKey);
         handleSubKey = NULL;
      }
      
      status = ZwOpenKey(&handleSubKey, MAXIMUM_ALLOWED, &attributes);
   
      if (!NT_SUCCESS(status)) {
         goto cleanup;
      }

      //
      // Get the value of "Identifier"
      //
 
      status = ZwQueryValueKey(handleSubKey,
                               &strIdentifier,
                               KeyValuePartialInformation,
                               valueInfo,
                               VALUE_BUFFER_SIZE,
                               &resultLength);
      
      
      if (NT_SUCCESS(status)) {
         PWCHAR pData = (PWCHAR)valueInfo->Data;
      
         if ((valueInfo->DataLength == 4*sizeof(WCHAR)) &&
            (pData[0] == (WCHAR)'M') &&
            (pData[1] == (WCHAR)'C') &&
            (pData[2] == (WCHAR)'A') &&
            (pData[3] == UNICODE_NULL)) {
            status = STATUS_NO_SUCH_DEVICE;
            goto cleanup;
         }
         
         if ((valueInfo->DataLength == 4*sizeof(WCHAR)) &&
            (pData[0] == (WCHAR)'I') &&
            (pData[1] == (WCHAR)'S') &&
            (pData[2] == (WCHAR)'A') &&
            (pData[3] == UNICODE_NULL)) {

            InitializeObjectAttributes(&attributes,
                                       &strPcCard,
                                       0,   //Attributes
                                       handleSubKey,
                                       NULL //SecurityDescriptor
                                       );
                  
            status = ZwOpenKey(&handlePcCard, MAXIMUM_ALLOWED, &attributes);
            
            if (NT_SUCCESS(status)) {
            
               status = PcmciaGetHardwareDetectedIrqMask(handlePcCard);
               ZwClose(handlePcCard);
            }               
         }
      }
   }
   
cleanup:
   if (handleRoot) {
      ZwClose(handleRoot);
   }

   if (handleSubKey) {
      ZwClose(handleSubKey);
   }
   
   if (subKeyInfo) {
      ExFreePool(subKeyInfo);
   }

   if (status == STATUS_NO_SUCH_DEVICE) {      
      //
      // Must be an MCA machine
      //
      return status;
   }
   return STATUS_SUCCESS;      
}   



NTSTATUS
PcmciaLoadGlobalRegistryValues(
   VOID
   )
/*++

Routine Description:

   This routine is called at driver init time to load in various global
   options from the registry.
   These are read in from SYSTEM\CurrentControlSet\Services\Pcmcia\Parameters.

Arguments:

   none

Return value:

   none

--*/
{
   PRTL_QUERY_REGISTRY_TABLE parms;
   NTSTATUS                  status;
   ULONG                     parmsSize;
   ULONG i;
   
   status = PcmciaScanHardwareDescription();
   
   if (!NT_SUCCESS(status)) {
      return status;
   }

   //
   // Needs a null entry to terminate the list
   //   

   parmsSize = sizeof(RTL_QUERY_REGISTRY_TABLE) * (GlobalInfoCount+1);

   parms = ExAllocatePool(PagedPool, parmsSize);

   if (!parms) {
       return STATUS_INSUFFICIENT_RESOURCES;
   }

   RtlZeroMemory(parms, parmsSize);

   //
   // Fill in the query table from our table
   //

   for (i = 0; i < GlobalInfoCount; i++) {
      parms[i].Flags         = RTL_QUERY_REGISTRY_DIRECT;
      parms[i].Name          = GlobalRegistryInfo[i].Name;
      parms[i].EntryContext  = GlobalRegistryInfo[i].pValue;
      parms[i].DefaultType   = REG_DWORD;
      parms[i].DefaultData   = &GlobalRegistryInfo[i].Default;
      parms[i].DefaultLength = sizeof(ULONG);
   }      

   //
   // Perform the query
   //

   status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
                                   PCMCIA_REGISTRY_PARAMETERS_KEY,
                                   parms,
                                   NULL,
                                   NULL);

   if (!NT_SUCCESS(status)) {
       //
       // This is possible during text mode setup
       //
       
       for (i = 0; i < GlobalInfoCount; i++) {
          *GlobalRegistryInfo[i].pValue = GlobalRegistryInfo[i].Default;
       }      
   }
   
   if (initSoundsEnabled) {
       PcmciaGlobalFlags |= PCMCIA_GLOBAL_SOUNDS_ENABLED;
   }

   if (initUsePolledCsc) {
       PcmciaGlobalFlags |= PCMCIA_GLOBAL_FORCE_POLL_MODE;
   }

   if (initDisableAcpiNameSpaceCheck) {
       PcmciaGlobalFlags |= PCMCIA_DISABLE_ACPI_NAMESPACE_CHECK;
   }

   if (initDefaultRouteR2ToIsa) {
       PcmciaGlobalFlags |= PCMCIA_DEFAULT_ROUTE_R2_TO_ISA;
   }

   if (!pcmciaIsaIrqRescanComplete) {
      UNICODE_STRING    unicodeKey, unicodeValue;
      OBJECT_ATTRIBUTES objectAttributes;      
      HANDLE            handle;
      ULONG             value;

      //
      // This mechanism is used to throw away the cached ISA irq map values. To do this
      // only once, we make sure a value in the registry is zero (or non-existant), and
      // here we set it to one.
      //
      
      RtlInitUnicodeString(&unicodeKey,
                           L"\\Registry\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Pcmcia\\Parameters");
      RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));
      InitializeObjectAttributes(&objectAttributes,
                                 &unicodeKey,
                                 OBJ_CASE_INSENSITIVE,
                                 NULL,
                                 NULL);
     
      if (NT_SUCCESS(ZwOpenKey(&handle, KEY_READ | KEY_WRITE, &objectAttributes))) {

         RtlInitUnicodeString(&unicodeValue, PCMCIA_REGISTRY_ISA_IRQ_RESCAN_COMPLETE);      
         value = 1;
            
         ZwSetValueKey(handle,
                       &unicodeValue,
                       0,
                       REG_DWORD,
                       &value,
                       sizeof(value));

         ZwClose(handle);                       
      }
   
   }

   ExFreePool(parms);
   
   return STATUS_SUCCESS;
}



NTSTATUS
PcmciaGetControllerRegistrySettings(
   IN OUT PFDO_EXTENSION FdoExtension
   )
/*++

Routine Description:

   This routine looks in the registry to see if a compatible controller type
   was specified in the INF.

Arguments:

   FdoExtension - The fdo extension corresponding to the PCMCIA controller
   
Return value:

--*/
{
   NTSTATUS status = STATUS_UNSUCCESSFUL;
   UNICODE_STRING    KeyName;
   HANDLE instanceHandle;
   UCHAR             buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
   PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
   ULONG             length;
   BOOLEAN           UseLegacyIrqMask = TRUE;
   ULONG             detectedIrqMask;

   if (FdoExtension->Pdo) {   
      status = IoOpenDeviceRegistryKey(FdoExtension->Pdo,
                                       PLUGPLAY_REGKEY_DRIVER,
                                       KEY_READ,
                                       &instanceHandle
                                       );
   }

   if (!NT_SUCCESS(status)) {
      instanceHandle = NULL;
   }
                                          
   if (instanceHandle) {

      //
      // Look to see if a controller ID was specified
      //
      RtlInitUnicodeString(&KeyName, PCMCIA_REGISTRY_COMPATIBLE_TYPE);
      
      status =  ZwQueryValueKey(instanceHandle,
                                &KeyName,
                                KeyValuePartialInformation,
                                value,
                                sizeof(buffer),
                                &length);
      
      
      if (NT_SUCCESS(status)) {
         PcmciaSetControllerType(FdoExtension, *(PPCMCIA_CONTROLLER_TYPE)(value->Data));
      }
      
      //
      // Check for voltage preference
      // When an 3v R2 card is plugged in, and the controller
      // sets both 5v and 3.3v, this allows 3.3v to be preferred.
      //
      RtlInitUnicodeString(&KeyName, PCMCIA_REGISTRY_VOLTAGE_PREFERENCE);
      
      status =  ZwQueryValueKey(instanceHandle,
                                &KeyName,
                                KeyValuePartialInformation,
                                value,
                                sizeof(buffer),
                                &length);
      
      
      if (NT_SUCCESS(status) && (*(PULONG)(value->Data) == 33)) {
         SetDeviceFlag(FdoExtension, PCMCIA_FDO_PREFER_3V);
      }
   }      
         
   //
   // Retrieve context ranges 
   //
   
   PcmciaGetRegistryContextRange(instanceHandle,
                                 PCMCIA_REGISTRY_PCI_CONTEXT_VALUE,
                                 DefaultPciContextSave,
                                 NULL,
                                 &FdoExtension->PciContext
                                 );
   
   PcmciaGetRegistryContextRange(instanceHandle,
                                 PCMCIA_REGISTRY_CB_CONTEXT_VALUE,
                                 DefaultCardbusContextSave,
                                 ExcludeCardbusContextRange,
                                 &FdoExtension->CardbusContext
                                 );
                                          
   PcmciaGetRegistryContextRange(instanceHandle,
                                 PCMCIA_REGISTRY_EXCA_CONTEXT_VALUE,
                                 NULL,
                                 NULL,
                                 &FdoExtension->ExcaContext);
   

   if (instanceHandle) {
      ZwClose(instanceHandle);
   }


   FdoExtension->IoLow               =  globalIoLow;
   FdoExtension->IoHigh              =  globalIoHigh;
   FdoExtension->ReadyDelayIter      =  globalReadyDelayIter;
   FdoExtension->ReadyStall          =  globalReadyStall;
   FdoExtension->AttributeMemoryLow  =  globalAttributeMemoryLow;
   FdoExtension->AttributeMemoryHigh =  globalAttributeMemoryHigh;
   
   if (FdoExtension->ControllerType == PcmciaDatabook) {
      FdoExtension->AttributeMemoryAlignment = TCIC_WINDOW_ALIGNMENT;
   } else {
      FdoExtension->AttributeMemoryAlignment = PCIC_WINDOW_ALIGNMENT;
   }      
      
   //
   // Assign default attribute memory window size
   //
   
   if (globalAttributeMemorySize == 0) {
      switch (FdoExtension->ControllerType) {
     
      case PcmciaDatabook:
           FdoExtension->AttributeMemorySize = TCIC_WINDOW_SIZE;
           break;
      default: 
           FdoExtension->AttributeMemorySize = PCIC_WINDOW_SIZE;
           break;
      }
   } else {
      FdoExtension->AttributeMemorySize = globalAttributeMemorySize;
   }

   //
   // See if the user asked for some special IRQ routing considerations based
   // on controller type
   //

   if (CardBusExtension(FdoExtension)) {

      //
      // route to PCI based on controller type
      //
         
      if (pcmciaIrqRouteToPciController) {
         ULONG ctlr = pcmciaIrqRouteToPciController;

         //
         // Check for exact match, or class if only a class was specified
         //
         if ((ctlr == FdoExtension->ControllerType) ||
             ((PcmciaClassFromControllerType(ctlr) == ctlr) && (ctlr == PcmciaClassFromControllerType(FdoExtension->ControllerType)))) {
             
            SetFdoFlag(FdoExtension, PCMCIA_FDO_PREFER_PCI_ROUTING);
         }
      }         
      
      //
      // route to ISA based on controller type
      //
      
      if (pcmciaIrqRouteToIsaController) {
         ULONG ctlr = pcmciaIrqRouteToIsaController;
      
         //
         // Check for exact match, or class if only a class was specified
         //
         if ((ctlr == FdoExtension->ControllerType) ||
             ((PcmciaClassFromControllerType(ctlr) == ctlr) && (ctlr == PcmciaClassFromControllerType(FdoExtension->ControllerType)))) {
     
            SetFdoFlag(FdoExtension, PCMCIA_FDO_PREFER_ISA_ROUTING);
         }
      }         

      //
      // route to PCI based on controller location
      //
         
      if (pcmciaIrqRouteToPciLocation) {
         ULONG loc = pcmciaIrqRouteToPciLocation;
      
         if ( ((loc & 0xff) == FdoExtension->PciBusNumber) &&
              (((loc >> 8) & 0xff) == FdoExtension->PciDeviceNumber)) {
              
            SetFdoFlag(FdoExtension, PCMCIA_FDO_FORCE_PCI_ROUTING);
         }
      }         
      
      //
      // route to ISA based on controller location
      //
         
      if (pcmciaIrqRouteToIsaLocation) {
         ULONG loc = pcmciaIrqRouteToIsaLocation;
      
         if ( ((loc & 0xff) == FdoExtension->PciBusNumber) &&
              (((loc >> 8) & 0xff) == FdoExtension->PciDeviceNumber)) {
              
            SetFdoFlag(FdoExtension, PCMCIA_FDO_FORCE_ISA_ROUTING);
         }
      }         
      
   }         

    
   return status;
}

   

VOID
PcmciaGetRegistryFdoIrqMask(
   IN OUT PFDO_EXTENSION FdoExtension
   )
/*++

Routine Description:

   This routine fills in the field "AllocatedIrqMask" in the specified
   fdo extension. 

Arguments:

   instanceHandle - open registry key for this controller
   pIrqMask       - pointer to variable to receive irq mask

Return value:

   none

--*/
{
   ULONG             irqMask, cachedIrqMask = 0;
   UNICODE_STRING    KeyName;
   NTSTATUS          status;
   UCHAR             buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
   PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
   ULONG             length;
   HANDLE            instanceHandle;
   ULONG             detectedIrqMask;
   
   PAGED_CODE();
   
   if (globalOverrideIrqMask) {   
   
      irqMask = globalOverrideIrqMask;
      
   } else {

      detectedIrqMask = PcmciaGetDetectedFdoIrqMask(FdoExtension);
      
      status = STATUS_UNSUCCESSFUL;
      
      if (FdoExtension->Pdo) {   
         status = IoOpenDeviceRegistryKey(FdoExtension->Pdo,
                                       PLUGPLAY_REGKEY_DRIVER,
                                       KEY_READ,
                                       &instanceHandle
                                       );
      }
                                             
      if (NT_SUCCESS(status)) {
         //
         // Here we cache the value, and accumulate bits so that
         // our mask improves with time.
         //
         RtlInitUnicodeString(&KeyName, PCMCIA_REGISTRY_CACHED_IRQMASK);

         if (pcmciaIsaIrqRescanComplete) {      
            status =  ZwQueryValueKey(instanceHandle,
                                      &KeyName,
                                      KeyValuePartialInformation,
                                      value,
                                      sizeof(buffer),
                                      &length);
           
            
            if (NT_SUCCESS(status)) {
               cachedIrqMask = *(PULONG)(value->Data);
            }
         }            
         
         irqMask = detectedIrqMask | cachedIrqMask;
         
         if ((cachedIrqMask != irqMask) || !pcmciaIsaIrqRescanComplete) {
            //
            // something changed, update the cached value
            //
            ZwSetValueKey(instanceHandle, &KeyName, 0, REG_DWORD, &irqMask, sizeof(irqMask));
         }

         ZwClose(instanceHandle);
      } else {
         //
         // Hmmm, no key. Can't cache the value
         //
         irqMask = detectedIrqMask;
      }         

      if (pcmciaDisableIsaPciRouting && (PcmciaCountOnes(irqMask) < 2)) {
         //
         // Perhaps irq detection is broken... fall back on old NT4 behavior
         //   
         irqMask = 0;
      }
   }

   irqMask &= ~globalFilterIrqMask;

   DebugPrint((PCMCIA_DEBUG_INFO, "IrqMask %08x (ovr %08x, flt %08x, det %08x, cache %08x)\n",
                    irqMask, globalOverrideIrqMask, globalFilterIrqMask, detectedIrqMask, cachedIrqMask));
                    
   FdoExtension->DetectedIrqMask = (USHORT)irqMask;
}


VOID
PcmciaGetRegistryContextRange(
   IN HANDLE instanceHandle,
   IN PCWSTR Name,
   IN OPTIONAL const PCMCIA_CONTEXT_RANGE IncludeRange[], 
   IN OPTIONAL const PCMCIA_CONTEXT_RANGE ExcludeRange[], 
   OUT PPCMCIA_CONTEXT pContext
   )
/*++

Routine Description:

    This routine returns a buffer containing the contents of the
    data which set by the controller's inf definition (AddReg). The value
    is in CurrentControlSet\Control\Class\{GUID}\{Instance}.

Arguments:

    FdoExtension - The fdo extension corresponding to the PCMCIA controller
    Name         - The name of the value in the registry
    IncludeRange - defines areas in the range that must be included
    ExcludeRange - defines areas in the range that must be excluded

Return value:

    Status

--*/
{
#define PCMCIA_MAX_CONTEXT_ENTRIES 128
#define MAX_RANGE_OFFSET 256   

   NTSTATUS          status;
   UNICODE_STRING    unicodeKeyName;
   UCHAR             buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                           PCMCIA_MAX_CONTEXT_ENTRIES*sizeof(PCMCIA_CONTEXT_RANGE)];
   PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
   UCHAR             rangeMap[MAX_RANGE_OFFSET] = {0};
   PPCMCIA_CONTEXT_RANGE newRange;
   LONG              rangeCount;
   ULONG             rangeLength;
   ULONG             bufferLength;
   UCHAR             lastEntry;
   ULONG             keyLength;
   USHORT            i, j;
   USHORT            startOffset, endOffset;

   PAGED_CODE();
   
   //
   // Initialize the range map with the minimum range
   //

   if (IncludeRange) {   
      for (i = 0; IncludeRange[i].wLen != 0; i++) {
      
         startOffset = IncludeRange[i].wOffset;
         endOffset   = IncludeRange[i].wOffset + IncludeRange[i].wLen - 1;
         
         if ((startOffset >= MAX_RANGE_OFFSET) ||
             (endOffset >= MAX_RANGE_OFFSET)) {
            continue;
         }
         
         for (j = startOffset; j <= endOffset; j++) {
            rangeMap[j] = 0xff;
         }
      }
   }
   

   if (instanceHandle) {   
      RtlInitUnicodeString(&unicodeKeyName, Name);
      
      status =  ZwQueryValueKey(instanceHandle,
                                &unicodeKeyName,
                                KeyValuePartialInformation,
                                value,
                                sizeof(buffer),
                                &keyLength);

   
      if (NT_SUCCESS(status)) {
      
         //
         // Merge in the range specified in the registry
         //
         newRange = (PPCMCIA_CONTEXT_RANGE) value->Data;
         for (i = 0; i < value->DataLength/sizeof(PCMCIA_CONTEXT_RANGE); i++) {
         
            startOffset = newRange[i].wOffset;
            endOffset   = newRange[i].wOffset + newRange[i].wLen - 1;
            
            if ((startOffset >= MAX_RANGE_OFFSET) ||
                (endOffset >= MAX_RANGE_OFFSET)) {
               continue;
            }
            
            for (j = startOffset; j <= endOffset; j++) {
               rangeMap[j] = 0xff;
            }
         }
      
      }
   }                              

   //
   // Filter out registers defined in the exclude range
   //

   if (ExcludeRange) {   
      for (i = 0; ExcludeRange[i].wLen != 0; i++) {
      
         startOffset = ExcludeRange[i].wOffset;
         endOffset   = ExcludeRange[i].wOffset + ExcludeRange[i].wLen - 1;
         
         if ((startOffset >= MAX_RANGE_OFFSET) ||
             (endOffset >= MAX_RANGE_OFFSET)) {
            continue;
         }
         
         for (j = startOffset; j <= endOffset; j++) {
            rangeMap[j] = 0;
         }
      }
   }
   

   //
   // Now build the resulting merged range in the buffer on the
   // stack, and figure out how big it is.
   //
   newRange = (PPCMCIA_CONTEXT_RANGE) buffer;
   rangeCount = -1;
   bufferLength = 0;
   lastEntry = 0;
   
   for (i = 0; i < MAX_RANGE_OFFSET; i++) {
   
      if (rangeMap[i]) {
         bufferLength++;
         if (lastEntry) {
            //
            // This new byte belongs to the current range
            //
            newRange[rangeCount].wLen++;
         } else {
            //
            // Starting a new range
            //
            if (rangeCount == (PCMCIA_MAX_CONTEXT_ENTRIES - 1)) {
               break;
            }
            rangeCount++;
            newRange[rangeCount].wOffset = i;
            newRange[rangeCount].wLen = 1;
         }                     
      
      } 
      lastEntry = rangeMap[i];
   }            
   rangeCount++;

   pContext->Range = NULL;
   pContext->RangeCount = 0;
   
   if (rangeCount) {
      //
      // Length of data
      //
      rangeLength = rangeCount*sizeof(PCMCIA_CONTEXT_RANGE);

      pContext->Range = ExAllocatePool(NonPagedPool, rangeLength);

      if (pContext->Range != NULL) {
         RtlCopyMemory(pContext->Range, buffer, rangeLength);
         pContext->RangeCount = (ULONG)rangeCount;
         pContext->BufferLength = bufferLength;

         //
         // Find the length of the longest individual range
         //         
         pContext->MaxLen = 0;
         for (i = 0; i < rangeCount; i++) {
            if (pContext->Range[i].wLen > pContext->MaxLen) {
               pContext->MaxLen = pContext->Range[i].wLen;
            }
         }
      } else {
         ASSERT(pContext->Range != NULL);
      }
   }      
}


NTSTATUS
PcmciaGetLegacyDetectedControllerType(
   IN PDEVICE_OBJECT Pdo,
   IN OUT PPCMCIA_CONTROLLER_TYPE ControllerType
   )
/*++

Routine Description:

    This routine returns the previously remembered controller type
    for the supplied pcmcia controller by poking in the registry
    at the appropriate places

Arguments:

    Pdo - The Physical device object corresponding to the PCMCIA controller
    ControllerType - pointer to the object in which the controller type will
                     be returned


Return value:

    Status

--*/
{
   NTSTATUS status;
   OBJECT_ATTRIBUTES objectAttributes;
   UNICODE_STRING    unicodeKeyName;
   HANDLE            instanceHandle=NULL;
   HANDLE            parametersHandle = NULL;
   RTL_QUERY_REGISTRY_TABLE queryTable[3];
   ULONG controllerType;
   ULONG invalid = 0xffffffff;
   

   PAGED_CODE();

   try {
      status = IoOpenDeviceRegistryKey(Pdo,
                                       PLUGPLAY_REGKEY_DEVICE,
                                       KEY_READ,
                                       &instanceHandle
                                      );
      if (!NT_SUCCESS(status)) {
         leave;
      }

      RtlInitUnicodeString(&unicodeKeyName, PCMCIA_REGISTRY_DETECTED_DEVICE_KEY);
      InitializeObjectAttributes(
                                &objectAttributes,
                                &unicodeKeyName,
                                OBJ_CASE_INSENSITIVE,
                                instanceHandle,
                                NULL);

      status = ZwOpenKey(&parametersHandle,
                         KEY_READ,
                         &objectAttributes);

      if (!NT_SUCCESS(status)) {
         leave;
      }


      RtlZeroMemory(queryTable, sizeof(queryTable));

      queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
      queryTable[0].Name = L"ControllerType";
      queryTable[0].EntryContext = &controllerType;
      queryTable[0].DefaultType = REG_DWORD;
      queryTable[0].DefaultData = &invalid;
      queryTable[0].DefaultLength = sizeof(ULONG);

      status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
                                      (PWSTR) parametersHandle,
                                      queryTable,
                                      NULL,
                                      NULL);

      if (!NT_SUCCESS(status)) {
         leave;
      }
      
      if (controllerType == invalid) {
         *ControllerType = PcmciaIntelCompatible;
      } else {
         *ControllerType = (PCMCIA_CONTROLLER_TYPE) controllerType;
      }

   } finally {

      if (instanceHandle != NULL) {
         ZwClose(instanceHandle);
      }

      if (parametersHandle != NULL) {
         ZwClose(parametersHandle);
      }
   }

   return status;
}


NTSTATUS
PcmciaSetLegacyDetectedControllerType(
   IN PDEVICE_OBJECT Pdo,
   IN PCMCIA_CONTROLLER_TYPE ControllerType
   )
/*++

Routine Description:

    This routine 'remembers' - by setting a value in the registry -
    the type of the  pcmcia controller that has been legacy detected
    to be retrieved and used in subsequent boots - if legacy re-detection
    of the controller is not performed

Arguments:

    Pdo - The Physical device object corresponding to the PCMCIA controller
    DeviceExtension -  Device extension of the fdo corresponding to the
                       controller

Return value:

    Status

--*/
{
   HANDLE               instanceHandle;
   NTSTATUS             status;
   OBJECT_ATTRIBUTES    objectAttributes;
   HANDLE               parametersHandle;
   UNICODE_STRING       unicodeString;

   PAGED_CODE();

   //
   // Get a handle to the registry devnode for this pdo
   //

   status = IoOpenDeviceRegistryKey(Pdo,
                                    PLUGPLAY_REGKEY_DEVICE,
                                    KEY_CREATE_SUB_KEY,
                                    &instanceHandle);

   if (!NT_SUCCESS(status)) {
      return status;
   }


   //
   // Open or create a sub-key for this devnode to store
   // the information in
   //

   RtlInitUnicodeString(&unicodeString, PCMCIA_REGISTRY_DETECTED_DEVICE_KEY);

   InitializeObjectAttributes(&objectAttributes,
                              &unicodeString,
                              OBJ_CASE_INSENSITIVE,
                              instanceHandle,
                              NULL);

   status = ZwCreateKey(&parametersHandle,
                        KEY_SET_VALUE,
                        &objectAttributes,
                        0,
                        NULL,
                        REG_OPTION_NON_VOLATILE,
                        NULL);

   if (!NT_SUCCESS(status)) {
      ZwClose(instanceHandle);
      return status;
   }
   //
   // Set the controller type value in the registry
   //
   RtlInitUnicodeString(&unicodeString, L"ControllerType");
   status = ZwSetValueKey(parametersHandle,
                          &unicodeString,
                          0,
                          REG_DWORD,
                          &ControllerType,
                          sizeof(ControllerType));
   ZwClose(parametersHandle);
   ZwClose(instanceHandle);
   return status;
}